Skip to content

Instantly share code, notes, and snippets.

@zouzanyan
Last active March 26, 2025 02:19
Show Gist options
  • Select an option

  • Save zouzanyan/688719924379cbe9050eaea581d1af83 to your computer and use it in GitHub Desktop.

Select an option

Save zouzanyan/688719924379cbe9050eaea581d1af83 to your computer and use it in GitHub Desktop.
无锁实现的雪花算法
class SnowflakeIdGenerator {
// 起始时间戳 (2024-01-01 00:00:00)
private final long START_TIMESTAMP = 1704038400000L;
// 每部分占用位数
private final long SEQUENCE_BIT = 12; // 序列号占12位
private final long MACHINE_BIT = 5; // 机器ID占5位
private final long DATACENTER_BIT = 5; // 数据中心占5位
// 最大值
private final long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
private final long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
private final long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
// 每部分向左的位移
private final long MACHINE_LEFT = SEQUENCE_BIT;
private final long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private final long TIMESTAMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
private long datacenterId; // 数据中心ID
private long machineId; // 机器ID
private final AtomicLong sequence = new AtomicLong(0L); // 改用 AtomicLong
private volatile long lastTimestamp = -1L; // 使用 volatile 保证可见性
public SnowflakeIdGenerator(long datacenterId, long machineId) {
if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
throw new IllegalArgumentException("数据中心ID不能大于" + MAX_DATACENTER_NUM + "或小于0");
}
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new IllegalArgumentException("机器ID不能大于" + MAX_MACHINE_NUM + "或小于0");
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}
// 生成下一个ID
public long nextId() {
long timestamp = System.currentTimeMillis();
long currentSequence;
while (true) {
long lastTime = lastTimestamp;
if (timestamp < lastTime) {
throw new RuntimeException("系统时钟回退,拒绝生成ID");
}
if (lastTime == timestamp) {
// CAS 更新序列号
currentSequence = sequence.incrementAndGet() & MAX_SEQUENCE;
if (currentSequence == 0) {
// 序列号用完,获取下一毫秒
timestamp = getNextMillis(lastTime);
continue;
}
} else {
// 不同毫秒,序列号重置为0
currentSequence = sequence.getAndSet(0);
lastTimestamp = timestamp;
}
break;
}
return ((timestamp - START_TIMESTAMP) << TIMESTAMP_LEFT)
| (datacenterId << DATACENTER_LEFT)
| (machineId << MACHINE_LEFT)
| currentSequence;
}
// 获取下一毫秒时间戳
private long getNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment