package com.ydtech.utils; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashSet; import java.util.Set; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; /** * Twitter_Snowflake
* SnowFlake改进符合业务的结构如下(每部分用-分开):
* bizCode(业务代码) + 201805051212(日期) + 4096(毫秒内随机数)
*/ /**** * @ClassName: SnowflakeIdWorker * @Description: * @author ccc520 * @date 2018年6月5日 上午8:44:27 * @modificationHistory===============逻辑或功能性重大变更记录 * @modify by user: (修改人) * @modify by reason: (修改原因) */ public class SnowflakeIdVariant { // ==============================Fields=========================================== /** * 序列在id中占的位数 */ private final long sequenceBits = 12L; /** * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */ private final long sequenceMask = -1L ^ (-1L << sequenceBits); /** * 毫秒内序列(0~4095) */ private long sequence = 0L; /** * 上次生成ID的时间截 */ private long lastTimestamp = -1L; private static ThreadLocal threadLocal = new ThreadLocal() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyyMMddhhmmssSSS"); } }; //==============================Constructors===================================== /** * 构造函数 */ public SnowflakeIdVariant() { } // ==============================Methods========================================== /** * 获得下一个ID (该方法是线程安全的) * * @return SnowflakeId */ public synchronized String nextId(String machineId, String bizCode) { long timestamp = timeGen(); //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 if (timestamp < lastTimestamp) { throw new RuntimeException( String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } //如果是同一时间生成的,则进行毫秒内序列 if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; //毫秒内序列溢出 if (sequence == 0) { //阻塞到下一个毫秒,获得新的时间戳 timestamp = tilNextMillis(lastTimestamp); } } //时间戳改变,毫秒内序列重置 else { sequence = 0L; } //上次生成ID的时间截 lastTimestamp = timestamp; //移位并通过或运算拼到一起组成64位的ID // return bizCode + machineId + (threadLocal.get().format(new Date(timestamp))) + String.format("%05d",sequence); String precision = "5"; //几位序列号 String format = "%0" + precision + "d"; return bizCode + machineId + (threadLocal.get().format(new Date(timestamp))) + String.format(format, sequence); } /** * 阻塞到下一个毫秒,直到获得新的时间戳 * * @param lastTimestamp 上次生成ID的时间截 * @return 当前时间戳 */ protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } /** * 返回以毫秒为单位的当前时间 * * @return 当前时间(毫秒) */ protected long timeGen() { return System.currentTimeMillis(); } //==============================Test============================================= /** * 测试 */ // public static void main(String[] args) throws InterruptedException { // System.out.println(SysConstants.MAC_ADDRESS); // String machineId = SysConstants.MAC_ADDRESS.replaceAll("-", "").substring(0,3); // SnowflakeIdVariant idWorker = new SnowflakeIdVariant(); // long time1 = System.currentTimeMillis(); // for (int i = 0; i < 100000; i++) { //// String id = idWorker.nextId("001","ORC"); // String id = idWorker.nextId(machineId,"ORC"); // //Thread.sleep(1); // System.out.println(id); // } // System.out.println(System.currentTimeMillis()-time1); // } // //////////// test //////////// public static void main(String[] args) throws Exception { final Set set = new HashSet(); SnowflakeIdVariant idWorker = new SnowflakeIdVariant(); final CyclicBarrier cdl = new CyclicBarrier(100); for (int i = 0; i < 10000; i++) { new Thread(new Runnable() { @Override public void run() { try { cdl.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } // id String id = idWorker.nextId("001", "ORC"); if (set.contains(id)) { System.out.println(id + " exists"); } set.add(id); System.out.println(id); // id2 String id2 = idWorker.nextId("001", "ORC"); if (set.contains(id2)) { System.out.println(id2 + " exists"); } set.add(id2); System.out.println(id2); } }).start(); } try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } }