一、UUID简介
UUID(Universally Unique Identifier)是一种标识符,由一组32个十六进制数所构成,其理论上的唯一性是通过时间戳和随机数来保证的。
Java中的UUID是通过java.util.UUID类来实现的,其主要有以下几种方法:
/** * 静态方法,返回一个随机生成的UUID。 * @return 随机生成的UUID */ public static UUID randomUUID() {...} /** * 静态方法,返回一个根据指定字节数组生成的UUID。 * @param input 字节数组 * @return 根据指定字节数组生成的UUID */ public static UUID nameUUIDFromBytes(byte[] input) {...} /** * 构造方法,根据指定的参数生成UUID * @param mostSigBits 64位的最高有效位 * @param leastSigBits 64位的最低有效位 */ private UUID(long mostSigBits, long leastSigBits) {...}
二、UUID的唯一性
UUID的唯一性是通过时间戳和随机数来保证的,其中时间戳的精度是100纳秒,而随机数则是通过SecureRandom类的实例生成的。因此,UUID的重复概率相当地小。
一方面从理论上讲,如果一个系统每微秒生成一条UUID,那么大约需要3.4×10^38年才能出现重复的UUID。另一方面,UUID的长度为128位,实际上是超过目前世界人口数量的。
因此,可以认为Java UUID几乎是唯一的。不过,在实际应用中,我们仍然需要考虑UUID重复的情况。
三、UUID重复的情况
1.多线程环境下UUID重复
在多线程环境下,如果多个线程同时调用Java UUID的randomUUID()方法,那么可能会出现UUID重复的情况。
为了避免这种情况,我们可以使用ThreadLocalRandom类代替SecureRandom类,即:
UUID uuid = new UUID(ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong());
2.分布式环境下UUID重复
在分布式环境下,如果多个节点同时生成UUID,那么也会出现UUID重复的情况。
为了解决这个问题,我们可以使用第三方的UUID生成器,比如UUIDGenerator、Snowflake等。
下面是使用Snowflake算法生成UUID的Java示例:
public class IdWorker { private long workerId; private long datacenterId; private long sequence = 0L; private long twepoch = 1288834974657L; private long workerIdBits = 5L; private long datacenterIdBits = 5L; private long maxWorkerId = -1L ^ (-1L << workerIdBits); private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); private long sequenceBits = 12L; private long workerIdShift = sequenceBits; private long datacenterIdShift = sequenceBits + workerIdBits; private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; private long sequenceMask = -1L ^ (-1L << sequenceBits); private long lastTimestamp = -1L; public IdWorker(long workerId, long datacenterId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException("workerId cannot be greater than " + maxWorkerId + " or less than 0"); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException("datacenterId cannot be greater than " + maxDatacenterId + " or less than 0"); } this.workerId = workerId; this.datacenterId = datacenterId; } public synchronized long nextId() { long timestamp = timeGen(); if (timestamp < lastTimestamp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id"); } if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } private long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } private long timeGen() { return System.currentTimeMillis(); } public static void main(String[] args) { IdWorker idWorker = new IdWorker(0, 0); for (int i = 0; i < 100; i++) { System.out.println(idWorker.nextId()); } } }
四、结论
总的来说,Java UUID的理论唯一性是较高的,但在实际应用中仍需要考虑UUID重复的情况。针对不同的情况,我们可以采用不同的解决方案,比如使用ThreadLocalRandom类、第三方UUID生成器、自定义算法等。在使用的时候,一定要根据实际情况进行选择并进行合理的使用和调整,以保证系统的性能和稳定性。