snowflake是Twitter開源的分佈式ID生成算法,結果是一個Long型的ID。其核心思想是:使用41bit做爲毫秒數,10bit做爲機器的ID(5個bit是數據中心,5個bit的機器ID),12bit做爲毫秒內的序列號(意味着每一個節點在每毫秒能夠產生 4096 個 ID),最後還有一個符號位,永遠是0。html
特色:算法
結果是一個Long型的ID,64位,結構圖以下:分佈式
清楚告終構後,就比較好實現了。this
當前時間的時間戳減去開始時間的時間戳,左移22位spa
(ts - beginTs) << timestampLeftOffset
自定義的業務節點ID,固定的值,左移12位code
workerId << workerIdLeftOffset
毫秒內序列號,以此遞增,若是溢出就阻塞到下一秒從0開始計數orm
// 同一時間內,則計算序列號 if (ts == lastTimestamp) { // 序列號溢出 if (++sequence > maxSequence) { ts = tilNextMillis(lastTimestamp); sequence = 0L; } } else { // 時間戳改變,重置序列號 sequence = 0L; } lastTimestamp = ts;
/** * 阻塞到下一個毫秒 * * @param lastTimestamp * @return */ private long tilNextMillis(long lastTimestamp) { long ts = System.currentTimeMillis(); while (ts <= lastTimestamp) { ts = System.currentTimeMillis(); } return ts; }
return (ts - beginTs) << timestampLeftOffset | workerId << workerIdLeftOffset | sequence;
最後貼出完整代碼。htm
public class SnowflakeIdWorker { /** * 開始時間:2020-01-01 00:00:00 */ private final long beginTs = 1577808000000L; private final long workerIdBits = 10; /** * 2^10 - 1 = 1023 */ private final long maxWorkerId = -1L ^ (-1L << workerIdBits); private final long sequenceBits = 12; /** * 2^12 - 1 = 4095 */ private final long maxSequence = -1L ^ (-1L << sequenceBits); /** * 時間戳左移22位 */ private final long timestampLeftOffset = workerIdBits + sequenceBits; /** * 業務ID左移12位 */ private final long workerIdLeftOffset = sequenceBits; /** * 合併了機器ID和數據標示ID,統稱業務ID,10位 */ private long workerId; /** * 毫秒內序列,12位,2^12 = 4096個數字 */ private long sequence = 0L; /** * 上一次生成的ID的時間戳,同一個worker中 */ private long lastTimestamp = -1L; public SnowflakeIdWorker(long workerId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("WorkerId必須大於或等於0且小於或等於%d", maxWorkerId)); } this.workerId = workerId; } public synchronized long nextId() { long ts = System.currentTimeMillis(); if (ts < lastTimestamp) { throw new RuntimeException(String.format("系統時鐘回退了%d毫秒", (lastTimestamp - ts))); } // 同一時間內,則計算序列號 if (ts == lastTimestamp) { // 序列號溢出 if (++sequence > maxSequence) { ts = tilNextMillis(lastTimestamp); sequence = 0L; } } else { // 時間戳改變,重置序列號 sequence = 0L; } lastTimestamp = ts; // 0 - 00000000 00000000 00000000 00000000 00000000 0 - 00000000 00 - 00000000 0000 // 左移後,低位補0,進行按位或運算至關於二進制拼接 // 原本高位還有個0<<63,0與任何數字按位或都是自己,因此寫不寫效果同樣 return (ts - beginTs) << timestampLeftOffset | workerId << workerIdLeftOffset | sequence; } /** * 阻塞到下一個毫秒 * * @param lastTimestamp * @return */ private long tilNextMillis(long lastTimestamp) { long ts = System.currentTimeMillis(); while (ts <= lastTimestamp) { ts = System.currentTimeMillis(); } return ts; } }
這裏面有大量的二進制位運算,目的只有一個:快。blog
規則:1爲真,0爲否,其實就是同位之間的布爾運算。it
都爲真就是真,其餘都是否
1&1=1 1&0=0 0&1=0 0&0=0
只要有一個真就是真
1|1=1 1|0=1 0|1=1 0|0=0
相同就是否,不一樣就是真
1^1=0 1^0=1 0^1=1 0^0=0
全部位向左移動多少位,低位補0,高位多出的直接刪掉
全部位向右移動多少位,低位多出的刪掉,高位是0補0,是1就補1
原碼
原碼就是十進制數字的原始二進制表示,對於整數而言,最高位爲符號位,1表示負數,0表示正數。以32位int型的整數2及-2舉例:
2的原碼:00000000 00000000 00000000 00000010
-2的原碼:10000000 00000000 00000000 00000010
反碼
正數的反碼就是其原碼,負數的反碼除了最高位的符號位外,其餘位取反(0改1,1改0)
2的反碼:00000000 00000000 00000000 00000010
-2的反碼:11111111 11111111 11111111 11111101
補碼
正數的補碼就是其原碼,負數的補碼是其反碼加1
2的補碼:00000000 00000000 00000000 00000010
-2的反碼:11111111 11111111 11111111 11111101
-2的補碼:11111111 11111111 11111111 11111110
原文出處:https://www.cnblogs.com/zhou-920644981/p/12202391.html