public class SnowFlake {// 数据中心(机房) idprivate long datacenterId;// 机器IDprivate long workerId;// 同一时间的序列private long sequence;public SnowFlake(long workerId, long datacenterId) {this(workerId, datacenterId, 0);}public SnowFlake(long workerId, long datacenterId, long sequence) {// 合法判断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;}// 开始时间戳(2021-10-16 22:03:32)private long twepoch = 1634393012000L;// 机房号,的ID所占的位数 5个bit 最大:11111(2进制)--> 31(10进制)private long datacenterIdBits = 5L;// 机器ID所占的位数 5个bit 最大:11111(2进制)--> 31(10进制)private long workerIdBits = 5L;// 5 bit最多只能有31个数字,就是说机器id最多只能是32以内private long maxWorkerId = -1L ^ (-1L << workerIdBits);// 5 bit最多只能有31个数字,机房id最多只能是32以内private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);// 同一时间的序列所占的位数 12个bit 111111111111 = 4095最多就是同一毫秒生成4096个private long sequenceBits = 12L;// workerId的偏移量private long workerIdShift = sequenceBits;// datacenterId的偏移量private long datacenterIdShift = sequenceBits + workerIdBits;// timestampLeft的偏移量private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;// 序列号掩码 4095 (0b111111111111=0xfff=4095)// 用于序号的与运算,保证序号最大值在0-4095之间private long sequenceMask = -1L ^ (-1L << sequenceBits);// 最近一次时间戳private long lastTimestamp = -1L;// 获取机器IDpublic long getWorkerId() {return workerId;}// 获取机房IDpublic long getDatacenterId() {return datacenterId;}// 获取最新一次获取的时间戳public long getLastTimestamp() {return lastTimestamp;}// 获取下一个随机的IDpublic 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));}// 去重if (lastTimestamp == timestamp) {sequence = (sequence + 1) & sequenceMask;// sequence序列大于4095if (sequence == 0) {// 调用到下一个时间戳的方法timestamp = tilNextMillis(lastTimestamp);}} else {// 如果是当前时间的第一次获取,那么就置为0sequence = 0;}// 记录上一次的时间戳lastTimestamp = timestamp;// 偏移计算return ((timestamp - twepoch) << timestampLeftShift) |(datacenterId << datacenterIdShift) |(workerId << workerIdShift) |sequence;}private long tilNextMillis(long lastTimestamp) {// 获取最新时间戳long timestamp = timeGen();// 如果发现最新的时间戳小于或者等于序列号已经超4095的那个时间戳while (timestamp <= lastTimestamp) {// 不符合则继续timestamp = timeGen();}return timestamp;}private long timeGen() {return System.currentTimeMillis();}public static void main(String[] args) {SnowFlake worker = new SnowFlake(1, 1);long timer = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {worker.nextId();}System.out.println(System.currentTimeMillis());System.out.println(System.currentTimeMillis() - timer);}}
问题分析1. 第一位为什么不使用?在计算机的表示中,第一位是符号位,0表示整数,第一位如果是1则表示负数,我们用的ID默认就是正数,所以默认就是0,那么这一位默认就没有意义 。2.机器位怎么用?机器位或者机房位,一共10 bit,如果全部表示机器,那么可以表示1024台机器,如果拆分,5 bit 表示机房,5bit表示机房里面的机器,那么可以有32个机房,每个机房可以用32台机器 。
推荐阅读
- 铁路12306提醒!抢票时,请认准官方APP候补功能
- 面试官问 HTTPS 是怎么从 HTTP 转过来的,我有点懵
- 屏下摄像头|最完美全面屏!中兴Axon 40 Ultra官方渲染图公布:屏幕开孔全部“消失”
- 冰箱|银河护卫队冰箱门被怒关近两千万次 官方:我们故意的
- yeezy官方尺码对照表39.5?yeezy官方尺码对照表v2
- 高铁乘务员面试要求
- coo的定位是什么?首席运营官的工作职责是什么
- 高校|时尚心理学:见工面试怎样穿?心理学家透露穿这个颜色录取率最高
- 大学生|假如有5杯水,却来了6个领导,应该怎么分?国企面试题难倒众人
- 00后|00后毕业生简历太狂野了?谷爱凌的朋友!面试官:被简历笑出腹肌