基于Snowflake 算法生成唯一标识符格式:yyyyMMdd+10随机数
2023-12-15 11:36:44
package com.ideatech.ams.ucam.utils;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
/**
* {生成唯一标识:yyyyDDmm+10随机数}
*
* @author Gotham
* @date: 2023/9/23 Time: 18:31
* Email: Gotham
* Description: {UniqueIdGenerator 类使用 Snowflake 算法生成唯一标识符,结合了时间戳和工作节点 ID。}
*/
public class UniqueIdGenerator {
// 纪元时间戳(2022 年 1 月 1 日午夜 UTC)
private static final long EPOCH = LocalDate.of(2022, 1, 1)
.atStartOfDay(ZoneOffset.UTC)
.toInstant()
.toEpochMilli();
// 用于工作节点 ID 的位数
private static final int WORKER_ID_BITS = 10;
// 用于序列号的位数
private static final int SEQUENCE_BITS = 12;
// 用于生成唯一 ID 的工作节点 ID
private final long workerId;
// 序列号计数器
private long sequence = 0L;
// 上一个生成 ID 的时间戳
private long lastTimestamp = -1L;
/**
* UniqueIdGenerator 类的构造函数。
*
* @param workerId 工作节点 ID(每个实例应保证唯一)
* @throws IllegalArgumentException 如果工作节点 ID 超出范围
*/
public UniqueIdGenerator(long workerId) {
if (workerId < 0 || workerId >= (1L << WORKER_ID_BITS)) {
throw new IllegalArgumentException("工作节点 ID 必须介于 0 和 " + ((1L << WORKER_ID_BITS) - 1) + " 之间");
}
this.workerId = workerId;
}
/**
* 生成唯一标识符,结合时间戳、工作节点 ID 和序列号。
*
* @return 表示唯一标识符的字符串
* @throws RuntimeException 如果系统时钟回拨
*/
public synchronized String generateUniqueId() {
long currentTimestamp = System.currentTimeMillis();
// 确保当前时间戳大于或等于上一个时间戳
if (currentTimestamp < lastTimestamp) {
throw new RuntimeException("时钟倒退。拒绝生成 ID");
}
// 如果在同一毫秒内生成,递增序列号
if (currentTimestamp == lastTimestamp) {
sequence = (sequence + 1) & ((1L << SEQUENCE_BITS) - 1);
if (sequence == 0) {
currentTimestamp = waitNextMillis(currentTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = currentTimestamp;
// 使用时间戳、工作节点 ID 和序列号生成唯一 ID
long uniqueId = ((currentTimestamp - EPOCH) << (WORKER_ID_BITS + SEQUENCE_BITS)) |
(workerId << SEQUENCE_BITS) | sequence;
// 将时间戳部分格式化为 yyyyMMdd
String timestamp = Instant.ofEpochMilli(currentTimestamp)
.atZone(ZoneOffset.UTC)
.toLocalDate()
.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
// 将唯一 ID 部分格式化为 10 位数字
String uniqueIdStr = String.format("%010d", uniqueId);
// 拼接时间戳和唯一 ID(截取为 10 位)
return timestamp + uniqueIdStr.substring(uniqueIdStr.length() - 10);
}
/**
* 等待下一个毫秒,确保生成新的时间戳。
*
* @param currentTimestamp 当前时间戳
* @return 下一个有效时间戳
*/
private long waitNextMillis(long currentTimestamp) {
while (currentTimestamp <= lastTimestamp) {
currentTimestamp = System.currentTimeMillis();
}
return currentTimestamp;
}
}
文章来源:https://blog.csdn.net/qq_37707251/article/details/135012455
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!