網站首頁 編程語言 正文
1,導入工具類
package com.sport.finetrackserver.common.utils;
import cn.hutool.core.convert.Convert;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
/**
* @author zdj
* @version 1.0
* @date 2022/05/30 15:35
*/
public class SnowFlakeUtil {
// ==============================Fields===========================================
/**
* 開始時間截 (2021-01-19)
*/
private final long twepoch = 1611043703161L;
/**
* 機器id所占的位數
*/
private final long workerIdBits = 5L;
/**
* 數據標識id所占的位數
*/
private final long dataCenterIdBits = 5L;
/**
* 支持的最大機器id,結果是31 (這個移位算法可以很快的計算出幾位二進制數所能表示的最大十進制數)
*/
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
/**
* 支持的最大數據標識id,結果是31
*/
private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);
/**
* 序列在id中占的位數
*/
private final long sequenceBits = 12L;
/**
* 機器ID向左移12位
*/
private final long workerIdShift = sequenceBits;
/**
* 數據標識id向左移17位(12+5)
*/
private final long dataCenterIdShift = sequenceBits + workerIdBits;
/**
* 時間截向左移22位(5+5+12)
*/
private final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;
/**
* 生成序列的掩碼,這里為4095 (0b111111111111=0xfff=4095)
*/
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
/**
* 工作機器ID(0~31)
*/
private long workerId;
/**
* 數據中心ID(0~31)
*/
private long dataCenterId;
/**
* 備用的數據中心ID(0~31),當時鐘回撥時,為了不拋異常,啟用備用ID
*/
private long standbyDatacenterId;
/**
* 毫秒內序列(0~4095)
*/
private long sequence = 0L;
/**
* 上次生成ID的時間截
*/
private long lastTimestamp = -1L;
/**
* 是否時鐘回撥
*/
private boolean isTimestampBack = false;
// ==============================Constructors=====================================
/**
* 最大容忍時間, 單位毫秒, 即如果時鐘只是回撥了該變量指定的時間, 那么等待相應的時間即可;
* 考慮到sequence服務的高性能, 這個值不易過大
*/
private static final long MAX_BACKWARD_MS = 3;
/**
* 構造函數
* @param workerId 工作ID (0~31)
* @param datacenterId 數據中心ID (0~31)
*/
SnowFlakeUtil(long workerId, long datacenterId, long standbyDatacenterId) {
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));
}
if (standbyDatacenterId > maxDataCenterId || standbyDatacenterId < 0) {
throw new IllegalArgumentException(String.format("standby datacenter Id can't be greater than %d or less than 0", maxDataCenterId));
}
if (datacenterId == standbyDatacenterId) {
throw new IllegalArgumentException("datacenter Id can't equal to standby datacenter Id.");
}
this.workerId = workerId;
this.dataCenterId = datacenterId;
this.standbyDatacenterId = standbyDatacenterId;
}
// ==============================Methods==========================================
/**
* 獲得下一個ID (該方法是線程安全的)
* @return SnowflakeId
*/
synchronized long nextId() {
long timestamp = timeGen();
// 如果當前時間小于上一次ID生成的時間戳,說明系統時鐘回退過
if (timestamp < lastTimestamp) {
// 如果時鐘回撥在可接受范圍內, 等待即可
long offset = lastTimestamp - timestamp;
if (offset <= MAX_BACKWARD_MS) {
try {
//睡(lastTimestamp - currentTimestamp)ms讓其追上
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(offset));
timestamp = timeGen();
//如果當前時間還是小于上一次ID生成的時間戳,這時啟用備用的datacenterId
if (timestamp < lastTimestamp) {
isTimestampBack = true;
//服務器時鐘被調整了
//log.error(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
//throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
} else {
isTimestampBack = false;
}
} catch (Exception e) {
//log.error(e);
}
}
}
// 如果是同一時間生成的,則進行毫秒內序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
// 毫秒內序列溢出
if (sequence == 0) {
// 阻塞到下一個毫秒,獲得新的時間戳
timestamp = tilNextMillis(lastTimestamp);
}
}
// 時間戳改變,毫秒內序列重置
else {
sequence = 0L;
}
// 上次生成ID的時間截
lastTimestamp = timestamp;
//要使用的datacenterId
long datacenterIdToUse = isTimestampBack ? standbyDatacenterId : dataCenterId;
// 移位并通過或運算拼到一起組成64位的ID
return ((timestamp - twepoch) << timestampLeftShift) //
| (datacenterIdToUse << dataCenterIdShift) //
| (workerId << workerIdShift) //
| sequence;
}
/**
* 阻塞到下一個毫秒,直到獲得新的時間戳
* @param lastTimestamp 上次生成ID的時間截
* @return 當前時間戳
*/
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
/**
* 返回以毫秒為單位的當前時間
* @return 當前時間(毫秒)
*/
protected long timeGen() {
return System.currentTimeMillis();
}
/**
* 使用雙重校驗獲取實例對象
*/
private volatile static SnowFlakeUtil snowflakeIdWorker;
public static SnowFlakeUtil getInstance() {
if (snowflakeIdWorker == null) {
synchronized (SnowFlakeUtil.class) {
if (snowflakeIdWorker == null) {
snowflakeIdWorker = new SnowFlakeUtil(1, 0,11);
}
}
}
return snowflakeIdWorker;
}
/**
* 獲得下一個ID (該方法是線程安全的)
* @return SnowflakeId
*/
public static String getNextId() {
return Convert.toStr(getInstance().nextId());
}
}
2,使用:
在需要的地方直接使用即可:
原文鏈接:https://blog.csdn.net/gelinwangzi_juge/article/details/125045353
- 上一篇:從List<Map>中截取指定的范圍數據集合
- 下一篇:安卓請求權限
相關推薦
- 2022-10-21 解決Git?Revert?再次合代碼無效問題_相關技巧
- 2022-04-16 C#算法之實現阿姆斯特朗數_C#教程
- 2022-05-13 可變參C API va_list,va_start,va_arg_va_end以及c++可變參模板
- 2022-08-04 C語言實現快速排序算法實例_C 語言
- 2022-11-29 C#交換兩個變量值的幾種方法總結_C#教程
- 2023-07-07 spring security權限路由匹配restful格式的詳情id設計
- 2022-03-10 通過容器擴展屬性IExtenderProvider實現WinForm通用數據驗證組件_C#教程
- 2023-10-15 el-popover彈窗修改三角樣式或者位置
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支