1. 使用AtomicLong
生成唯一ID(适用于单机场景)
这个示例已经在之前的回答中给出,但我会再次展示它,以便与后续示例保持连贯性。
import java.util.concurrent.atomic.AtomicLong;
public class UniqueIdGenerator {
private final AtomicLong counter = new AtomicLong(0);
public long nextId() {
return counter.incrementAndGet();
}
public static void main(String[] args) {
UniqueIdGenerator generator = new UniqueIdGenerator();
long id1 = generator.nextId();
long id2 = generator.nextId();
System.out.println("ID 1: " + id1);
System.out.println("ID 2: " + id2);
}
}
2. 使用UUID并转换为数字形式(虽然不是纯数字,但提供了唯一性)
由于UUID本身是字符串形式的,我们可以通过哈希函数(如MD5、SHA-1等)将其转换为数字,但需要注意哈希碰撞的可能性(尽管在实际应用中非常低)。然而,更常见的是保留UUID的字符串形式或使用其作为基础来生成符合特定需求的数字ID。
import java.util.UUID;
public class UuidToNumberExample {
// 注意:这不是将UUID直接转换为唯一数字的有效方法,因为存在哈希碰撞的风险。
// 这里只是为了说明概念。
public static long uuidToNumber(UUID uuid) {
// 简单的示例:将UUID的字符串表示形式与另一个数字组合,然后进行哈希(注意:这不是安全的做法)
String uuidStr = uuid.toString();
long base = 123456789L; // 假设的基数
// 这里不实现哈希函数,而是使用字符串长度加基数作为示例(仅为演示)
return uuidStr.length() + base; // 这不是一个好的实现!
// 更实际的方法是使用安全的哈希函数,但请注意哈希碰撞的可能性
// 并且,由于哈希函数的输出是固定长度的,直接用作ID可能需要进一步处理
}
public static void main(String[] args) {
UUID uuid = UUID.randomUUID();
long number = uuidToNumber(uuid); // 这里的实现是错误的,仅用于说明
System.out.println("UUID: " + uuid);
System.out.println("Converted Number (Incorrect Method): " + number);
// 正确的做法可能是将UUID用作查找或生成更复杂ID的基础
}
}
3. 模拟Snowflake算法生成唯一ID(分布式场景)
Snowflake算法的实现相对复杂,但我们可以简化其核心思想来展示一个基本框架。
public class SnowflakeIdWorker {
// 假设的时间戳、数据中心ID、机器ID和序列号字段(实际应用中需要更精细的管理)
private final long twepoch = 1288834974657L; // 自定义起始时间戳
private final long datacenterIdBits = 5L; // 数据中心ID位数
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 数据中心ID最大值
private final long workerIdBits = 5L; // 机器ID位数
private final long maxWorkerId = -1L ^ (-1L << workerIdBits); // 机器ID最大值
private final long sequenceBits = 12L; // 序列号位数
private final long workerIdShift = sequenceBits; // 机器ID左移位数
private final long datacenterIdShift = sequenceBits + workerIdBits; // 数据中心ID左移位数
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; // 时间戳左移位数
private long sequence = 0L; // 序列号
private long lastTimestamp = -1L; // 上次生成ID的时间戳
// 构造函数中初始化workerId和datacenterId(这里为示例,实际中应由外部指定)
public SnowflakeIdWorker(long workerId, long datacenterId) {
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));
}
// 这里省略了实际将workerId和datacenterId设置到对象中的步骤
}
// 生成ID的方法(简化版,未包含时钟回拨处理等)
public synchronized long nextId() {
long timestamp = timeGen();
// 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回拨过
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
// 如果是同一时间生成的,则进行毫秒内序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & (-1L ^ (-1L << sequenceBits));
if (sequence == 0) {
// 毫秒内序列溢出
timestamp = tilNextMillis(lastTimestamp);
}
} else {
// 时间戳改变,毫秒内序列重置
sequence = 0L;
}
// 上次生成ID的时间戳
lastTimestamp = timestamp;
// 移位并通过或运算拼到一起组成64位的ID
// 这里省略了实际的workerId和datacenterId的左移和或操作
// 示例:return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
return 0L; // 返回模拟的0,实际应返回通过上述方式计算的ID
}
// 模拟获取系统当前时间戳(毫秒)
protected long timeGen() {
return System.currentTimeMillis();
}
// 等待到下一个毫秒,获得新的时间戳
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
// 省略main方法和其他可能的辅助方法...
}
请注意,上述Snowflake算法的实现是高度简化的,并省略了许多关键细节(如实际的数据中心ID和机器ID的设置、序列号的正确处理以及时钟回拨的详细处理逻辑)。在实际应用中,您需要根据自己的需求和环境来完整实现这些功能。
标签:Java,UUID,永不,重复,long,public,哈希,ID,String From: https://blog.51cto.com/u_16993624/12073345