importorg.junit.Test; importorg.junit.runner.RunWith; importorg.springframework.test.context.junit4.SpringJUnit4ClassRunner; /*** @author 86182* @date 2023/6/5 15:49* @desc*/publicclassSnowFlakeDemo { /**起始的时间戳,用于计算时间戳部分*/privatefinalstaticlongSTART_STMP=1480166465631L; /**序列号占用的位数*/privatefinalstaticlongSEQUENCE_BIT=12; /**机器标识占用的位数 */privatefinalstaticlongMACHINE_BIT=5; /**数据中心占用的位数 */privatefinalstaticlongDATA_CENTER_BIT=5; /**数据中心最大值*//*** 这个运算`-1L ^ (-1L << DATA_CENTER_BIT)`的含义是:* - -1L << DATA_CENTER_BIT是将-1的二进制表示向左移动DATA_CENTER_BIT位,也就是5位。* - 由于-1的二进制表示是除了最高位都是1,向左移动5位后,低5位是1,高位都是0。* - 然后使用`^`运算,即异或运算对-1和向左移动的结果进行运算。* - 由于异或运算遵循交换律和结合律,且异或相同为0,不同为1。* - 所以,最终得到的结果是高5位是1,低位都是0,也就是一个5位的二进制数,其十进制结果就是2^5 - 1 = 31。* 所以,简单来说,该运算的结果是获取一个位数为DATA_CENTER_BIT的二进制数,并将其转换为十进制 - 这里就是31。*/privatefinalstaticlongMAX_DATA_CENTER_NUM=-1L^ (-1L<<DATA_CENTER_BIT); /**机器标识最大值*/privatefinalstaticlongMAX_MACHINE_NUM=-1L^ (-1L<<MACHINE_BIT); /**序列号最大值*/privatefinalstaticlongMAX_SEQUENCE=-1L^ (-1L<<SEQUENCE_BIT); /**机器标识左移位数,12位*/privatefinalstaticlongMACHINE_LEFT=SEQUENCE_BIT; /**数据中心左移位数,12+5=17位*/privatefinalstaticlongDATA_CENTER_LEFT=SEQUENCE_BIT+MACHINE_BIT; /**时间戳左移位数,12+5+5=22位*/privatefinalstaticlongTIMESTMP_LEFT=DATA_CENTER_LEFT+DATA_CENTER_BIT; /**数据中心ID*/privatelongdataCenterId; /**机器标识ID*/privatelongmachineId; /**序列号 (备注:对于并发度不高的系统而言,此序列号始终是0,生成的雪花Id末尾一位数字始终为偶数!!!)*/privatelongsequence=0L; /**上一次时间戳*/privatelonglastStmp=-1L; publicSnowFlakeDemo(){} publicSnowFlakeDemo(longdataCenterId, longmachineId) { /**检验数据中心ID和机器ID合法性,不合法抛出异常*/if (dataCenterId>MAX_DATA_CENTER_NUM||dataCenterId<0) { thrownewIllegalArgumentException("dataCenterId can't be greater than MAX_DATA_CENTER_NUM or less than 0"); } if (machineId>MAX_MACHINE_NUM||machineId<0) { thrownewIllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); } /**合法的情况下将参数赋值给类成员变量*/this.dataCenterId=dataCenterId; this.machineId=machineId; } /**产生下一个ID*/publicsynchronizedlongnextId() { /**获取当前时间戳,毫秒级*/longcurrStmp=getNewstmp(); /**时间戳回拨,抛出异常*/if (currStmp<lastStmp) { thrownewRuntimeException("Clock moved backwards. Refusing to generate id"); } /**如果时间戳与上一次相同,序列号自增*/if (currStmp==lastStmp) { /*** (sequence + 1) & MAX_SEQUENCE 的含义是:* 1. sequence + 1 首先将序列号sequence增加1。* 2. & MAX_SEQUENCE 然后与序列号最大值MAX_SEQUENCE进行与运算。* 3. 由于与运算遵循交换律和结合律,且相同为1,不同为0。* 4. 所以,如果sequence增加后的值没有超过MAX_SEQUENCE,则与运算结果不变,sequence增加后的值仍然保留。* 5. 但如果sequence增加后的值超过了MAX_SEQUENCE,则与运算会将超出的高位全部抹去,只保留低MAX_SEQUENCE位,使得sequence不会超过该最大值。* 所以,该运算的效果是,每次让sequence增加1,但sequence增加后的值不会超过我们设置的MAX_SEQUENCE最大值,一旦超过,高位会被抹去,重新回到低MAX_SEQUENCE位。*/sequence= (sequence+1) &MAX_SEQUENCE; /**同一毫秒序列号已经最大,等待下一毫秒*/if (sequence==0L) { currStmp=getNextMill(); } } else { /**不同毫秒序列号置为0,从0开始*/sequence=0L; } /**更新时间戳为当前时间戳*/lastStmp=currStmp; /*** 计算ID* 时间戳部分(41 bit)* 数据中心部分(5 bit)* 机器标识部分(5 bit)* 序列号部分(12 bit)*//*** 含义是:* 1. (currStmp - START_STMP) << TIMESTMP_LEFT 首先计算出当前时间戳currStmp减去起始时间戳START_STMP的差值,然后将该差值向左移动TIMESTMP_LEFT位,也就是41位。这步骤生成时间戳部分。* 2. | dataCenterId << DATA_CENTER_LEFT 将数据中心ID向左移动DATA_CENTER_LEFT位,也就是17位,然后使用按位或|运算符将该结果与1步骤的结果进行|运算。这步骤在时间戳部分的基础上添加了数据中心ID部分。* 3. | machineId << MACHINE_LEFT 将机器ID向左移动MACHINE_LEFT位,也就是22位,然后使用按位或运算符将该结果与2步骤的结果进行|运算。这步骤在时间戳部分和数据中心ID部分的基础上添加了机器ID部分。* 4. | sequence 最后,直接使用按位或运算符将序列号sequence与3步骤的结果进行|运算,添加序列号部分,得到最终完整的ID。* 5. 通过这4步运算,我们按照时间戳→数据中心ID→机器ID→序列号的顺序,一步步构建完整的ID。这与雪花ID的结构与顺序是一致的。* 所以,这句运算的含义就是按照部分→部分的方式构建完整的雪花ID,每一步都使用按位或运算,这是一种高效的ID构建方式。这是雪花算法实现的关键所在。*/return (currStmp-START_STMP) <<TIMESTMP_LEFT|dataCenterId<<DATA_CENTER_LEFT|machineId<<MACHINE_LEFT|sequence; } /*** 等待下一毫秒** @return*/privatelonggetNextMill() { longmill=getNewstmp(); while (mill<=lastStmp) { mill=getNewstmp(); } returnmill; } /*** 获取当前时间戳,毫秒级* @return*/privatelonggetNewstmp() { returnSystem.currentTimeMillis(); } }