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
134
135
package com.bjtcrj.scm.common.utils;

/**
* 雪花算法
* 64位ID = 1位符号位(固定为0,表示正数) + 41位时间戳 + 10位工作机器id + 12位序列号
*
* @author shuyan.qi
* @date 2020/5/3 9:42 下午
*/
public class SnowFlakeID {
//12位序列号
private long sequence;//序列号
private long sequenceBits = 12L;//序列号位数
private long maxSequence = -1 ^ (-1 << sequenceBits);//序列号最大值

//10位工作机器id = 5位机房id + 5位机器id
private long workerId;//机器id
private long workerIdBits = 5L;//机器id位数
private long maxWorkerId = -1 ^ (-1 << workerIdBits);//机器id最大值
private long workerIdMoveBits = sequenceBits;//机器id左移位数 = 序列号位数
private long workerIdAfterMove = 0L;//左移后的机器id

private long workerCenterId;//机房id
private long workerCenterIdBits = 5L;//机房id位数
private long maxWorkerCenterId = -1 ^ (-1 << workerCenterIdBits);//机房id最大值
private long workerCenterIdMoveBits = workerIdBits + workerIdMoveBits;//机房id左移位数 = 机器id位数 + 序列号位数
private long workerCenterIdAfterMove = 0L;//左移后的机房id

//41位时间戳
private long lastTimestamp = -1L;//默认-1L
private long initTimestamp = 1588518046057L;//初始时间戳
private long timestampMoveBits = workerCenterIdBits + workerCenterIdMoveBits;//时间戳左移位数 = 机房id位数 + 机器id位数 + 序列号位数

public SnowFlakeID(long workerCenterId, long workerId) {
if (workerCenterId < 0 || workerCenterId > maxWorkerCenterId) {
throw new IllegalArgumentException("workerCenterId is illegal");
}
if (workerId < 0 || workerId > maxWorkerId) {
throw new IllegalArgumentException("workerId is illegal");
}
this.workerCenterId = workerCenterId;
this.workerId = workerId;
this.workerCenterIdAfterMove = this.workerCenterId << this.workerCenterIdMoveBits;
this.workerIdAfterMove = this.workerCenterId << this.workerCenterIdMoveBits;
}

/**
* 生成ID的核心方法
*/
public synchronized long nextId() {
long currentTimestamp = timestamp();
if (currentTimestamp < lastTimestamp) {
String s = String.format("currentTimestamp is earlier than lastTimestamp,lastTimestamp=%s,currentTimestamp=%s", lastTimestamp, currentTimestamp);
System.out.println(s);
//throw new RuntimeException(s);
// 时钟回拨后手动拨正。
// 因为依赖lastTimestamp,所以重启后第一次就发生时钟回拨的情况无法处理。
// 可以将lastTimestamp存放到redis之类第三方缓存中,但这样生成id的效率会降低,请开发者根据实际情况去选择。
currentTimestamp = lastTimestamp;
}
if (currentTimestamp == lastTimestamp) {
//同一时间戳,序列号加1
sequence = (sequence + 1) & maxSequence;
if (sequence == 0L) {
//如果序列号加1后的值为0,表示当前时间戳内的序列号已用完,需要获取下一个时间戳
currentTimestamp = nextTimestamp(currentTimestamp);
}
} else {
sequence = 0L;//不同时间戳,重置序列号
}
lastTimestamp = currentTimestamp;//更新成功生成id的最新时间戳
return ((currentTimestamp - initTimestamp) << timestampMoveBits) | workerCenterIdAfterMove | workerIdAfterMove | sequence;
}

/**
* 获取timestamp的下一毫秒数
*
* @param timestamp 当前毫秒数
* @return
*/
public long nextTimestamp(long timestamp) {
long timestamp1 = 0L;
do {
timestamp1 = timestamp();
} while (timestamp >= timestamp1);
return timestamp1;
}

/**
* 获取当前时间戳
*
* @return
*/
public long timestamp() {
return System.currentTimeMillis();
}

public static void main(String[] args) throws InterruptedException {

/*
//测试并发
Map<String,Object> map = new ConcurrentHashMap<>();
SnowFlakeID snowFlakeID = new SnowFlakeID(1, 1);
for(int i = 0;i < 100;i++){
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int j = 0 ;j < 100000;j++){
map.put(String.valueOf(snowFlakeID.nextId()),1);
}
}).start();;
}

for(;;){
TimeUnit.SECONDS.sleep(1);
System.out.println("size="+map.size());
}

*/
//测试速度
long startTime = System.currentTimeMillis();
for (int i = 0; i < 30; i++) {
System.out.println(getId());
}
System.out.println("耗时:" + (System.currentTimeMillis() - startTime) / 1000.0d + "秒");
}

private static SnowFlakeID snowFlakeID = new SnowFlakeID(1, 1);
public static long getId() {
return snowFlakeID.nextId();
}
}