p-honggang.li
5 天以前 cda9decfde8c6b518639c5da506aa293c07f88ff
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
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();
    }
}