1、基本构成

0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
1.1 符号位
1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
1.2 41位时间戳(毫秒级)
41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的。
1.3 10位的数据机器位
10位的数据机器位,可以部署在1024个分布式节点上,包括5位datacenterId(机房)和5位workerId(机器节点)。
1.4 12位序列
12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号。
1+41+10+12 = 64
2.代码实现
2.1 基本代码
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);}}
2.2 成员变量
| 属性 | 含义 | 默认值 |
|---|---|---|
| twepoch | 开始时间截 | 项目起始点 |
| workerIdBits | 机器id所占的位 | |
| datacenterIdBits | 数据标识id所占的位数 | |
| maxWorkerId | 支持的最大机器id,结果是31 | (-1^(-1<<5))=31 |
| maxDatacenterId | 支持的最大数据标识id,结果是31 | (-1^(-1<<5))=31 |
| sequenceBits | 序列在id中占的位数 | 12 |
| workerIdShift | 机器ID向左移12位 | 12 |
| datacenterIdShift | 数据标识id向左移17位 | 17 |
| timestampLeftShift | 时间截向左移22位(5+5+12) | 22 |
| sequenceMask | 生成序列的掩码,这里为4095 | (-1^(-1<<12))=4095 |
| workerId | 工作机器ID(0~31) | (-1^(-1<<5))=31 |
| datacenterId | 数据中心ID(0~31) | (-1^(-1<<5))=31 |
| sequence | 毫秒内序列(0~4095) | 12 |
| lastTimestamp | 上次生成ID的时间截 |
