Last active
March 26, 2025 02:19
-
-
Save zouzanyan/688719924379cbe9050eaea581d1af83 to your computer and use it in GitHub Desktop.
无锁实现的雪花算法
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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