Java生成12-13位唯一随机序列号的最佳实践
生成12-13位唯一序列号的最佳实践
首先,你当前代码的问题很明确:直接打印了Random对象本身,而不是调用它的方法生成随机数。java.util.Random@13d8cc98是对象的默认字符串表示(类名+哈希码),不是你要的序列号。
下面分场景给出解决方案:
基础场景:低并发、对唯一性要求一般
如果只是简单生成12-13位随机数,且不会在同一毫秒内频繁生成,可按以下方式修正代码:
// 用当前毫秒时间做种子初始化Random Random random = new Random(System.currentTimeMillis()); // 定义12-13位的范围:100000000000(12位最小值)到9999999999999(13位最大值) long min = 100000000000L; long max = 9999999999999L; long range = max - min + 1; // 生成指定范围的随机数(处理nextLong()可能返回负数的情况) long serialNo = min + (random.nextLong() & Long.MAX_VALUE) % range; System.out.println("serial number is " + serialNo);
关键优化点:
- 不要每次生成序列号都新建
Random实例,复用同一个实例能避免同一毫秒内种子重复导致的随机序列重复:// 全局复用一个Random实例 private static final Random RANDOM = new Random(System.currentTimeMillis()); public static long getSerialNo() { long min = 100000000000L; long max = 9999999999999L; long range = max - min + 1; return min + (RANDOM.nextLong() & Long.MAX_VALUE) % range; }
进阶场景:需要较高唯一性
如果要进一步降低重复概率,建议结合时间戳+随机数的方式,利用时间的递增性减少冲突:
public static String generateSerialNo() { // 取当前毫秒时间戳的后10位(保证时间维度的唯一性基础) String timeSegment = String.valueOf(System.currentTimeMillis()).substring(3); // 生成2-3位随机数(让总长度在12-13位之间) Random random = new Random(); int randomSegment = random.nextInt(900) + 100; // 生成3位随机数;若要2位则用random.nextInt(90)+10 return timeSegment + randomSegment; }
这种方式生成的序列号既包含时间信息,又有随机部分,短时间内重复的概率极低。
高级场景:分布式/高并发下严格唯一
如果是分布式系统或高并发场景,要求序列号绝对唯一,可使用简化版雪花算法,通过拆分时间戳、机器标识、序列号的方式保证唯一性:
public class SerialGenerator { // 起始时间戳(自定义,比如2024-01-01 00:00:00) private static final long START_TIMESTAMP = 1704067200000L; private static long lastTimestamp = -1L; private static int sequence = 0; // 同一毫秒内最多生成10个不同序列号 private static final int MAX_SEQUENCE = 9; // 加锁保证线程安全 public static synchronized String generate() { long currentTimestamp = System.currentTimeMillis(); // 处理时钟回退问题 if (currentTimestamp < lastTimestamp) { throw new RuntimeException("Clock moved backwards. Cannot generate serial number."); } // 同一毫秒内递增序列号 if (currentTimestamp == lastTimestamp) { sequence = (sequence + 1) % (MAX_SEQUENCE + 1); if (sequence == 0) { // 等待下一个毫秒 while (currentTimestamp <= lastTimestamp) { currentTimestamp = System.currentTimeMillis(); } } } else { sequence = 0; } lastTimestamp = currentTimestamp; // 时间差部分(取10位,保证约3年的可用周期) long timeDiff = currentTimestamp - START_TIMESTAMP; String timeStr = String.format("%010d", timeDiff); // 机器标识(自定义,比如机器IP后两位、进程ID,这里简化为固定值) String machineId = "01"; // 序列号部分(1位) String sequenceStr = String.valueOf(sequence); // 组合成13位序列号:10位时间差 + 2位机器标识 + 1位序列号 return timeStr + machineId + sequenceStr; } }
说明:
- 时间差部分:基于起始时间的差值,避免时间戳过长
- 机器标识:区分不同节点,防止分布式场景下的重复
- 序列号:同一毫秒内同一节点生成的序列号递增,保证毫秒内唯一
内容的提问来源于stack exchange,提问作者Anshul
相关产品推荐
相关产品推荐

