SnowFlake算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | package util; public class IdUtils { private static IdWorkerUtils idWorkerUtils= new IdWorkerUtils( 1 , 1 , 1 ); public static Long nextId(){ return idWorkerUtils.nextId(); } private static class IdWorkerUtils { //下面两个每个5位,加起来就是10位的工作机器id private long workerId; //工作id private long datacenterId; //数据id //12位的序列号 private long sequence; public IdWorkerUtils( long workerId, long datacenterId, long sequence){ // sanity check for workerId if (workerId > maxWorkerId || workerId < 0 ) { throw new IllegalArgumentException(String.format( "worker Id can't be greater than %d or less than 0" ,maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0 ) { throw new IllegalArgumentException(String.format( "datacenter Id can't be greater than %d or less than 0" ,maxDatacenterId)); } System.out.printf( "worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d" , timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId); this .workerId = workerId; this .datacenterId = datacenterId; this .sequence = sequence; } //初始时间戳 private long twepoch = 1288834974657L; //长度为5位 private long workerIdBits = 5L; private long datacenterIdBits = 5L; //最大值 private long maxWorkerId = -1L ^ (-1L << workerIdBits); private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); //序列号id长度 private long sequenceBits = 12L; //序列号最大值 private long sequenceMask = -1L ^ (-1L << sequenceBits); //工作id需要左移的位数,12位 private long workerIdShift = sequenceBits; //数据id需要左移位数 12+5=17位 private long datacenterIdShift = sequenceBits + workerIdBits; //时间戳需要左移位数 12+5+5=22位 private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; //上次时间戳,初始值为负数 private long lastTimestamp = -1L; public long getWorkerId(){ return workerId; } public long getDatacenterId(){ return datacenterId; } public long getTimestamp(){ return System.currentTimeMillis(); } //下一个ID生成算法 public synchronized long nextId() { long timestamp = timeGen(); //获取当前时间戳如果小于上次时间戳,则表示时间戳获取出现异常 if (timestamp < lastTimestamp) { System.err.printf( "clock is moving backwards. Rejecting requests until %d." , lastTimestamp); throw new RuntimeException(String.format( "Clock moved backwards. Refusing to generate id for %d milliseconds" , lastTimestamp - timestamp)); } //获取当前时间戳如果等于上次时间戳(同一毫秒内),则在序列号加一;否则序列号赋值为0,从0开始。 if (lastTimestamp == timestamp) { sequence = (sequence + 1 ) & sequenceMask; if (sequence == 0 ) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0 ; } //将上次时间戳值刷新 lastTimestamp = timestamp; /** * 返回结果: * (timestamp - twepoch) << timestampLeftShift) 表示将时间戳减去初始时间戳,再左移相应位数 * (datacenterId << datacenterIdShift) 表示将数据id左移相应位数 * (workerId << workerIdShift) 表示将工作id左移相应位数 * | 是按位或运算符,例如:x | y,只有当x,y都为0的时候结果才为0,其它情况结果都为1。 * 因为个部分只有相应位上的值有意义,其它位上都是0,所以将各部分的值进行 | 运算就能得到最终拼接好的id */ return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } //获取时间戳,并与上次时间戳比较 private long tilNextMillis( long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } //获取系统时间戳 private long timeGen(){ return System.currentTimeMillis(); } //---------------测试--------------- public static void main(String[] args) { com.longfor.falcon.util.IdWorkerUtils worker = new com.longfor.falcon.util.IdWorkerUtils( 1 , 1 , 1 ); for ( int i = 0 ; i < 30 ; i++) { System.out.println(worker.nextId()); } } } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
2018-08-01 jmap 查看内存使用直方图
2018-08-01 并发下线程池的最佳数量计算
2018-08-01 java中的类加载器ClassLoader和类初始化
2017-08-01 c# CacheManager 缓存管理
2017-08-01 新一代AJAX API:FETCH
2017-08-01 SqlServer,Oracle,Mysql 获取指定行数
2017-08-01 事件总线EventBus