基于Snowflake 算法生成唯一标识符格式:yyyyMMdd+10随机数

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;
    }
}




相关推荐

  1. 时间格式 yyyyMMdd

    2023-12-15 12:20:03       55 阅读
  2. 新型唯一标识符 ULID 详解

    2023-12-15 12:20:03       49 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2023-12-15 12:20:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2023-12-15 12:20:03       101 阅读
  3. 在Django里面运行非项目文件

    2023-12-15 12:20:03       82 阅读
  4. Python语言-面向对象

    2023-12-15 12:20:03       91 阅读

热门阅读

  1. 【Rust】第四节:通用编程概念

    2023-12-15 12:20:03       67 阅读
  2. python传递给delphi dll只能显示第1个字符?

    2023-12-15 12:20:03       58 阅读
  3. axios不用封装单独上传图片文件

    2023-12-15 12:20:03       50 阅读
  4. redis的hash实现

    2023-12-15 12:20:03       60 阅读
  5. android常用

    2023-12-15 12:20:03       48 阅读
  6. Vue 循环渲染 v-for

    2023-12-15 12:20:03       53 阅读
  7. OSS上传pdf无法解析的问题

    2023-12-15 12:20:03       52 阅读
  8. Python 自动化之处理图片(一)

    2023-12-15 12:20:03       52 阅读
  9. Leetcode724.寻找数组中心下标(通俗易懂版)

    2023-12-15 12:20:03       54 阅读
  10. RocketMQ的架构是什么样的❓

    2023-12-15 12:20:03       66 阅读
  11. LeetCode-232. 用栈实现队列【栈 设计 队列】

    2023-12-15 12:20:03       46 阅读