其实雪花算法比较简单,可能称不上什么算法就是一种构造UID的方法。
点1:UID是一个long类型的41位时间戳,10位存储机器码,12位存储序列号。
点2:时间戳的单位是毫秒,可以同时链接1024台机器,每台机器每毫秒可以使用4096个序列好,我们会给生成id上一个同步锁,阻塞住其他线程的访问。
点3:利用掩码我们可以检测序列是否溢出,如果溢出的话,就强制等待到下一毫秒。
java
/**
* @author hardstone
* @since 29 July 2023(1690603385473)
*/
public class SnowFlakes {
//开始的时间戳
private final long start = 1690603385473L;
//机器标识长度5位
private final long machineIdBits = 5L;
//机器集群标识长度5位
private final long centerIdBits = 5L;
//序列标识所占位数12位
private final long sequenceBits = 12L;
//机器标识最大值
private final long maxMachineId = -1L ^ (-1L << machineIdBits);
//机器集群标识最大值
private final long maxCenterId = -1L ^ (-1L << centerIdBits);
//序列标识的最大值
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
//机器标识左移长度
private final long machineIdShift = sequenceBits;
//机器集群标识左移长度
private final long centerIdShift = sequenceBits + machineIdBits;
//时间戳左移长度
private final long timeStampIdShift = sequenceBits + machineIdBits + centerIdBits;
//序列Id
private long sequence = 0L;
//机器Id
private long machineId;
//机器集群Id
private long centerId;
//时间戳
private long lastTimeStamp = -1L;
public SnowFlakes(long machineId, long centerId) {
if (machineId > maxMachineId || machineId < 0) {
throw new IllegalArgumentException(String.format("WorkerId should be between 0 and 31"));
}
if (centerId > maxCenterId || centerId < 0) {
throw new IllegalArgumentException(String.format("CenterId should be between 0 and 31"));
}
}
public synchronized long nextId() {
long timeStamp = System.currentTimeMillis();
//时间回滚现象
if (timeStamp < lastTimeStamp) {
throw new RuntimeException(
String.format("Time gone backwards!")
);
}
if (lastTimeStamp == timeStamp) {
sequence = (sequence + 1) & sequenceMask;
//如果序列分配完了
if (sequence == 0) {
timeStamp = getNextMillis(lastTimeStamp);
}
} else {
sequence = 0L;
}
lastTimeStamp = timeStamp;
return ((timeStamp - start) << timeStampIdShift)
| (centerId << centerIdShift)
| (machineId << machineIdShift)
| sequence;
}
protected long getNextMillis(long lastTimeStamp) {
long timeStamp = System.currentTimeMillis();
while (timeStamp <= lastTimeStamp) {
timeStamp = System.currentTimeMillis();
}
return timeStamp;
}
public static void main(String[] args) {
System.out.println(new SnowFlakes(0, 0).nextId());
}
}