Java收集的雪花算法代碼詳解
package com.java265.other;
public class Test {
// 因?yàn)槎M(jìn)制里第一個(gè) bit 為如果是 1,那么都是負(fù)數(shù),但是我們生成的 id 都是正數(shù),所以第一個(gè) bit 統(tǒng)一都是 0。
// 機(jī)器ID 2進(jìn)制5位 32位減掉1位 31個(gè)
private long workerId;
// 機(jī)房ID 2進(jìn)制5位 32位減掉1位 31個(gè)
private long datacenterId;
// 代表一毫秒內(nèi)生成的多個(gè)id的最新序號(hào) 12位 4096 -1 = 4095 個(gè)
private long sequence;
// 設(shè)置一個(gè)時(shí)間初始值 2^41 - 1 差不多可以用69年
private long twepoch = 1585644268888L;
// 5位的機(jī)器id
private long workerIdBits = 5L;
// 5位的機(jī)房id
private long datacenterIdBits = 5L;
// 每毫秒內(nèi)產(chǎn)生的id數(shù) 2 的 12次方
private long sequenceBits = 12L;
// 這個(gè)是二進(jìn)制運(yùn)算,就是5 bit最多只能有31個(gè)數(shù)字,也就是說(shuō)機(jī)器id最多只能是32以內(nèi)
private long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 這個(gè)是一個(gè)意思,就是5 bit最多只能有31個(gè)數(shù)字,機(jī)房id最多只能是32以內(nèi)
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private long workerIdShift = sequenceBits;
private long datacenterIdShift = sequenceBits + workerIdBits;
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private long sequenceMask = -1L ^ (-1L << sequenceBits);
// 記錄產(chǎn)生時(shí)間毫秒數(shù),判斷是否是同1毫秒
private long lastTimestamp = -1L;
public long getWorkerId() {
return workerId;
}
public long getDatacenterId() {
return datacenterId;
}
public long getTimestamp() {
return System.currentTimeMillis();
}
public Test(long workerId, long datacenterId, long sequence) {
// 檢查機(jī)房id和機(jī)器id是否超過(guò)31 不能小于0
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));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
this.sequence = sequence;
}
// 這個(gè)是核心方法,通過(guò)調(diào)用nextId()方法,讓當(dāng)前這臺(tái)機(jī)器上的snowflake算法程序生成一個(gè)全局唯一的id
public synchronized long nextId() {
// 這兒就是獲取當(dāng)前時(shí)間戳,單位是毫秒
long timestamp = timeGen();
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));
}
// 下面是說(shuō)假設(shè)在同一個(gè)毫秒內(nèi),又發(fā)送了一個(gè)請(qǐng)求生成一個(gè)id
// 這個(gè)時(shí)候就得把seqence序號(hào)給遞增1,最多就是4096
if (lastTimestamp == timestamp) {
// 這個(gè)意思是說(shuō)一個(gè)毫秒內(nèi)最多只能有4096個(gè)數(shù)字,無(wú)論你傳遞多少進(jìn)來(lái),
// 這個(gè)位運(yùn)算保證始終就是在4096這個(gè)范圍內(nèi),避免你自己傳遞個(gè)sequence超過(guò)了4096這個(gè)范圍
sequence = (sequence + 1) & sequenceMask;
// 當(dāng)某一毫秒的時(shí)間,產(chǎn)生的id數(shù) 超過(guò)4095,系統(tǒng)會(huì)進(jìn)入等待,直到下一毫秒,系統(tǒng)繼續(xù)產(chǎn)生ID
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0;
}
// 這兒記錄一下最近一次生成id的時(shí)間戳,單位是毫秒
lastTimestamp = timestamp;
// 這兒就是最核心的二進(jìn)制位運(yùn)算操作,生成一個(gè)64bit的id
// 先將當(dāng)前時(shí)間戳左移,放到41 bit那兒;將機(jī)房id左移放到5 bit那兒;將機(jī)器id左移放到5 bit那兒;將序號(hào)放最后12 bit
// 最后拼接起來(lái)成一個(gè)64 bit的二進(jìn)制數(shù)字,轉(zhuǎn)換成10進(jìn)制就是個(gè)long型
return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
| (workerId << workerIdShift) | sequence;
}
/**
* 當(dāng)某一毫秒的時(shí)間,產(chǎn)生的id數(shù) 超過(guò)4095,系統(tǒng)會(huì)進(jìn)入等待,直到下一毫秒,系統(tǒng)繼續(xù)產(chǎn)生ID
*
* @param lastTimestamp
* @return
*/
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
// 獲取當(dāng)前時(shí)間戳
private long timeGen() {
return System.currentTimeMillis();
}
/**
* main 測(cè)試類
*
* @param args
*/
public static void main(String[] args) {
System.out.println(1 & 4596);
System.out.println(2 & 4596);
System.out.println(6 & 4596);
System.out.println(6 & 4596);
System.out.println(6 & 4596);
System.out.println(6 & 4596);
Test test = new Test(1, 1, 1);
for (int i = 0; i < 22; i++) {
System.out.println(test.nextId());
}
}
}
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
java ArrayList集合中的某個(gè)對(duì)象屬性進(jìn)行排序的實(shí)現(xiàn)代碼
這篇文章主要介紹了java ArrayList集合中的某個(gè)對(duì)象屬性進(jìn)行排序的實(shí)現(xiàn)代碼,需要的朋友可以參考下2016-07-07
Java中SynchronousQueue的底層實(shí)現(xiàn)原理剖析
BlockingQueue的實(shí)現(xiàn)類中,有一種阻塞隊(duì)列比較特殊,就是SynchronousQueue(同步移交隊(duì)列),隊(duì)列長(zhǎng)度為0。本文就來(lái)剖析一下SynchronousQueue的底層實(shí)現(xiàn)原理,感興趣的可以了解一下2022-11-11
Java源碼解析之Gateway請(qǐng)求轉(zhuǎn)發(fā)
今天給大家?guī)?lái)的是關(guān)于Java的相關(guān)知識(shí),文章圍繞著Gateway請(qǐng)求轉(zhuǎn)發(fā)展開(kāi),文中有非常詳細(xì)介紹及代碼示例,需要的朋友可以參考下2021-06-06
使用springboot logback動(dòng)態(tài)獲取application的配置項(xiàng)
這篇文章主要介紹了使用springboot logback動(dòng)態(tài)獲取application的配置項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
詳解如何在Spring MVC中處理AJAX請(qǐng)求
在現(xiàn)代 web 開(kāi)發(fā)中,AJAX(Asynchronous JavaScript and XML)被廣泛用于創(chuàng)建響應(yīng)式和動(dòng)態(tài)的用戶界面,與傳統(tǒng)的頁(yè)面刷新不同,AJAX 允許網(wǎng)頁(yè)在不重新加載的情況下與服務(wù)器交換數(shù)據(jù),從而提升了用戶體驗(yàn),本篇博客將深入探討如何在 Spring MVC 中處理 AJAX 請(qǐng)求2024-11-11
HelloSpringMVC配置版實(shí)現(xiàn)步驟解析
這篇文章主要介紹了HelloSpringMVC配置版實(shí)現(xiàn)步驟解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
Java實(shí)現(xiàn)定時(shí)讀取json文件里內(nèi)容的示例代碼
有時(shí)候我們會(huì)需要定時(shí)來(lái)讀取JSON配置文件里的內(nèi)容,來(lái)執(zhí)行一些業(yè)務(wù)邏輯上的操作,本文就介紹了Java實(shí)現(xiàn)定時(shí)讀取json文件里內(nèi)容的示例代碼,感興趣的可以了解一下2023-08-08

