package com.webmanage.util;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
/**
|
* Snowflake 雪花算法ID生成器(64位)
|
* 结构:1位符号位 + 41位时间戳 + 5位数据中心 + 5位工作机器 + 12位序列
|
*/
|
public class SnowflakeIdWorker {
|
|
private final long twepoch;
|
private final long workerIdBits = 5L;
|
private final long datacenterIdBits = 5L;
|
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
|
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
|
private final long sequenceBits = 12L;
|
|
private final long workerIdShift = sequenceBits;
|
private final long datacenterIdShift = sequenceBits + workerIdBits;
|
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
|
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
|
|
private final long workerId;
|
private final long datacenterId;
|
|
private long sequence = 0L;
|
private long lastTimestamp = -1L;
|
|
private final ReentrantLock lock = new ReentrantLock(true);
|
|
/**
|
* @param workerId 工作机器ID (0~31)
|
* @param datacenterId 数据中心ID (0~31)
|
* @param twepochMs 自定义纪元时间(毫秒),建议为系统上线前固定时间戳,默认 2020-01-01
|
*/
|
public SnowflakeIdWorker(long workerId, long datacenterId, long twepochMs) {
|
if (workerId > maxWorkerId || workerId < 0) {
|
throw new IllegalArgumentException("worker Id can't be greater than " + maxWorkerId + " or less than 0");
|
}
|
if (datacenterId > maxDatacenterId || datacenterId < 0) {
|
throw new IllegalArgumentException("datacenter Id can't be greater than " + maxDatacenterId + " or less than 0");
|
}
|
this.workerId = workerId;
|
this.datacenterId = datacenterId;
|
this.twepoch = twepochMs;
|
}
|
|
/**
|
* 线程安全生成下一个ID
|
*/
|
public long nextId() {
|
lock.lock();
|
try {
|
long timestamp = currentTime();
|
|
if (timestamp < lastTimestamp) {
|
// 时钟回拨处理:等待到 lastTimestamp
|
long offset = lastTimestamp - timestamp;
|
try {
|
Thread.sleep(offset);
|
} catch (InterruptedException ignored) { }
|
timestamp = currentTime();
|
if (timestamp < lastTimestamp) {
|
// 如果仍小于,强制使用 lastTimestamp
|
timestamp = lastTimestamp;
|
}
|
}
|
|
if (lastTimestamp == timestamp) {
|
sequence = (sequence + 1) & sequenceMask;
|
if (sequence == 0) {
|
// 同毫秒内序列溢出,等待下一毫秒
|
timestamp = tilNextMillis(lastTimestamp);
|
}
|
} else {
|
sequence = 0L;
|
}
|
|
lastTimestamp = timestamp;
|
|
return ((timestamp - twepoch) << timestampLeftShift)
|
| (datacenterId << datacenterIdShift)
|
| (workerId << workerIdShift)
|
| sequence;
|
} finally {
|
lock.unlock();
|
}
|
}
|
|
private long tilNextMillis(long lastTimestamp) {
|
long timestamp = currentTime();
|
while (timestamp <= lastTimestamp) {
|
timestamp = currentTime();
|
}
|
return timestamp;
|
}
|
|
private long currentTime() {
|
return System.currentTimeMillis();
|
}
|
}
|