com.biz.common.id.SnowflakeGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of biz-all Show documentation
Show all versions of biz-all Show documentation
BizX 是一个灵活而高效的业务开发框架, 其中也有很多为业务开发所需要的工具类的提供。
The newest version!
package com.biz.common.id;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 雪花ID生成器,用于生成全局唯一且递增的ID。
*
* 该生成器基于Snowflake算法实现,通过时间戳、机器ID和序列号来生成唯一ID。
* ID由以下部分组成:
*
* - 时间戳部分:基于自定义的基准时间(2021年1月1日0点)以来的毫秒数。
* - 机器ID部分:用于区分不同的生成器实例,避免ID冲突。
* - 序列号部分:用于同一毫秒内生成多个ID,保证ID的唯一性和递增性。
*
*
*
* 通过此类,您可以生成全球唯一且有序的ID,非常适合分布式系统中的唯一标识符生成。
*
*
* 示例使用:
* {@code
* SnowflakeGenerator generator = new SnowflakeGenerator(SnowflakeGenerator.DEFAULT_MACHINE_ID);
* long id = generator.generate();
* }
*
*
* @author francis
* @version 1.0.1
* @since 1.0.1
*/
public class SnowflakeGenerator {
// 基准时间戳,即2021年1月1日0点,用于计算相对于此时间的毫秒数
private static final long EPOCH = 1609459200000L;
// 序列号占用的位数
private static final int SEQUENCE_BITS = 12;
// 机器ID占用的位数
private static final int MACHINE_ID_BITS = 10;
// 序列号的最大值
private static final int MAX_SEQUENCE = (1 << SEQUENCE_BITS) - 1;
// 机器ID的最大值
private static final int MAX_MACHINE_ID = (1 << MACHINE_ID_BITS) - 1;
// 基于基准时间的偏移量
private final long twepoch = EPOCH;
// 机器ID,用于区分不同的生成器实例
private final int machineId;
// 用于同步生成ID的锁
private final Lock lock = new ReentrantLock();
// 上次生成ID的时间戳
private volatile long lastTimestamp = -1L;
// 当前序列号
private volatile int sequence = 0;
/**
* 默认机器ID,用于没有指定机器ID的情况
*/
public static final int DEFAULT_MACHINE_ID = 0;
/**
* 构造函数,初始化SnowflakeGenerator。
*
* @param machineId 机器ID,用于区分不同的生成器实例。
* 必须在0到{@link #MAX_MACHINE_ID}之间。
* @throws IllegalArgumentException 如果机器ID超出允许范围,则抛出该异常
*/
public SnowflakeGenerator(int machineId) {
if (isMachineIdNotValid(machineId)) {
throw new IllegalArgumentException("Invalid machine ID");
}
this.machineId = machineId;
}
/**
* 生成全局唯一的ID。
*
* 该方法是线程安全的,能够确保在高并发环境下生成唯一且递增的ID。
*
* @return 生成的唯一ID
*/
public long generate() {
lock.lock();
try {
long timestamp = System.currentTimeMillis();
// 处理系统时间回拨的情况
if (timestamp < lastTimestamp) {
handleClockBackward(lastTimestamp);
}
// 当时间戳与上一次相同,递增序列号
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & MAX_SEQUENCE;
// 如果序列号溢出,则等待下一个时间戳
if (sequence == 0) {
timestamp = waitForNextMillis(lastTimestamp);
}
} else {
// 重置序列号
sequence = 0;
}
// 更新上一次生成ID的时间戳
lastTimestamp = timestamp;
// 构造ID:时间戳部分、机器ID部分和序列号部分
return ((timestamp - twepoch) << (MACHINE_ID_BITS + SEQUENCE_BITS)) |
((long) machineId << SEQUENCE_BITS) |
sequence;
} finally {
lock.unlock();
}
}
/**
* 检查机器ID是否不合法。
*
* @param machineId 机器ID
* @return 如果机器ID不在合法范围内,返回true,否则返回false
*/
public static boolean isMachineIdNotValid(int machineId) {
return machineId < 0 || machineId > MAX_MACHINE_ID;
}
/**
* 处理系统时间回拨的情况,抛出异常拒绝生成ID。
*
* @param lastTimestamp 上次生成ID的时间戳
* @throws RuntimeException 当检测到系统时间回拨时抛出此异常
*/
private void handleClockBackward(long lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backward. Refusing to generate id for timestamp %d while last timestamp was %d",
System.currentTimeMillis(), lastTimestamp));
}
/**
* 等待直到下一个毫秒,避免系统时间回拨导致的问题。
*
* @param lastTimestamp 上次生成ID的时间戳
* @return 等待后的当前时间戳
*/
private long waitForNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy