生成一個long類型的java長整型數(shù)字(單機(jī)遞增)
組成
64bit組成部分
- 第一位:1bit篡腌,值為0,無實際含義
- 時間戳:41bit勾效,精確到毫秒嘹悼,可容納69年時間
- 工作機(jī)器:10bit叛甫,其中高位5bit是數(shù)據(jù)中心ID,低位5bit是工作節(jié)點(diǎn)ID杨伙,最多可容納1024個節(jié)點(diǎn)
- 序列號:12bit其监,每個節(jié)點(diǎn)每毫秒0開始不斷累加,最多可以累加到4095限匣,一共可以產(chǎn)生4096個ID
雪花算法同一毫秒內(nèi)可以產(chǎn)生1024*4096=4194304個全局唯一ID
依賴參數(shù):數(shù)據(jù)中心ID和數(shù)據(jù)節(jié)點(diǎn)ID
算法流程
流程圖
算法實現(xiàn)
public class IdWorker{
//下面兩個每個5位抖苦,加起來就是10位的工作機(jī)器id
private long workerId; //工作id
private long datacenterId; //數(shù)據(jù)id
//12位的序列號
private long sequence;
public IdWorker(long workerId, long datacenterId, long sequence){
// sanity check for workerId
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0",maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0",maxDatacenterId));
}
System.out.printf("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",
timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);
this.workerId = workerId;
this.datacenterId = datacenterId;
this.sequence = sequence;
}
//初始時間戳
private long twepoch = 1288834974657L;
//長度為5位
private long workerIdBits = 5L;
private long datacenterIdBits = 5L;
//最大值
private long maxWorkerId = -1L ^ (-1L << workerIdBits);
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
//序列號id長度
private long sequenceBits = 12L;
//序列號最大值
private long sequenceMask = -1L ^ (-1L << sequenceBits);
//工作id需要左移的位數(shù),12位
private long workerIdShift = sequenceBits;
//數(shù)據(jù)id需要左移位數(shù) 12+5=17位
private long datacenterIdShift = sequenceBits + workerIdBits;
//時間戳需要左移位數(shù) 12+5+5=22位
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
//上次時間戳膛腐,初始值為負(fù)數(shù)
private long lastTimestamp = -1L;
public long getWorkerId(){
return workerId;
}
public long getDatacenterId(){
return datacenterId;
}
public long getTimestamp(){
return System.currentTimeMillis();
}
//下一個ID生成算法
public synchronized long nextId() {
long timestamp = timeGen();
//獲取當(dāng)前時間戳如果小于上次時間戳睛约,則表示時間戳獲取出現(xiàn)異常
if (timestamp < lastTimestamp) {
System.err.printf("clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
lastTimestamp - timestamp));
}
//獲取當(dāng)前時間戳如果等于上次時間戳(同一毫秒內(nèi)),則在序列號加一哲身;否則序列號賦值為0,從0開始贸伐。
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0;
}
//將上次時間戳值刷新
lastTimestamp = timestamp;
/**
* 返回結(jié)果:
* (timestamp - twepoch) << timestampLeftShift) 表示將時間戳減去初始時間戳勘天,再左移相應(yīng)位數(shù)
* (datacenterId << datacenterIdShift) 表示將數(shù)據(jù)id左移相應(yīng)位數(shù)
* (workerId << workerIdShift) 表示將工作id左移相應(yīng)位數(shù)
* | 是按位或運(yùn)算符,例如:x | y捉邢,只有當(dāng)x脯丝,y都為0的時候結(jié)果才為0,其它情況結(jié)果都為1伏伐。
* 因為個部分只有相應(yīng)位上的值有意義宠进,其它位上都是0,所以將各部分的值進(jìn)行 | 運(yùn)算就能得到最終拼接好的id
*/
return ((timestamp - twepoch) << timestampLeftShift) |
(datacenterId << datacenterIdShift) |
(workerId << workerIdShift) |
sequence;
}
//獲取時間戳藐翎,并與上次時間戳比較
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
//獲取系統(tǒng)時間戳
private long timeGen(){
return System.currentTimeMillis();
}
//---------------測試---------------
public static void main(String[] args) {
IdWorker worker = new IdWorker(1,1,1);
for (int i = 0; i < 30; i++) {
System.out.println(worker.nextId());
}
}
}
算法理解
負(fù)數(shù)的二進(jìn)制表示
負(fù)數(shù)的二進(jìn)制是用補(bǔ)碼來表示的材蹬。
- 補(bǔ)碼 = 反碼 + 1
- 補(bǔ)碼 = (原碼 - 1)再取反碼
如:-1的二進(jìn)制表示
00000000 00000000 00000000 00000001 //原碼:1的二進(jìn)制
11111111 11111111 11111111 11111110 //取反碼:1的二進(jìn)制的反碼
11111111 11111111 11111111 11111111 //加1:-1的二進(jìn)制表示(補(bǔ)碼)