重新聚焦UUID构建:深入分析JDK中构建UUID的逻辑

[id_[id_129[id_2069735838]926193]46413414]id_1845450026]

重新聚焦UUID构建:深入分析JDK中构建UUID的逻辑

前言

正如在《ULID:构建分布式ID的另一种选择》中所讲,UUID通常由32个字符组成,这些字符以16进制形式呈现。其一般表现为8 - 4 - 4 - 4 - 12的形式。再加上连接字符“-”,总共是36个字符。

(图中各段的含义可参考往期内容,在此我们便不再赘述~)

JDK中与UUID相关API

JDK为方便使用UUID作为资源的唯一标识信息。它提供了java.util.UUID类。用来帮助我们快速构建UUID。具体如下:


public void testUUID() {
    log.info("generating UUID : [{}]", UUID.randomUUID());
}

运行上述代码就能生成一串UUID。它的名字是7e281cab-6b44-4426-bab1-f6a1405201d9。不难发现,通过UUID.randomUUID()可生成一串版本号为4的UUID。正如我们之前所讲的在UUID规范里 对于UUID的版本 主要存在两种实现方式 一种是基于时间的version-1 另一种是完全随机的version-4

说到这里 你可能会有这样的疑问 既然UUID有两种版本的实现 那么JDK中实现的是version-4的随机版本吗 当然不是 JDK中大致提供了如下几种不同的版本信息

“时间戳UUID (Version 1)”:这是UUID最常见的版本。它依据时间戳和节点信息生成。通常包含时间戳。还包含时钟序列号。也包含节点标识等信息。

「DCE安全UUID (版本2)」:此版本的UUID虽具备DCE(分布式计算环境)的安全特性,然而在开发里却鲜少被使用

“随机生成UUID (Version 3)”:此版本的UUID采用基于名称的生成方法。通常把名称(像命名空间和名称字符串)以及特定算法当作输入来生成UUID。这种方式并不常用。

“基于MD5散列的UUID (Version 4)” 是生成随机UUID的常用方式。它借助伪随机数生成器生成UUID。其随机性良好。它还是UUID.randomUUID()方法默认使用的版本

「基于SHA-1散列的UUID (Version 5)」 :这个版本的UUID和版本3相似。它使用SHA-1散列算法生成。除此之外。它通常还需要输入名称和命名空间作为参数。

UUID相关生成原理

来分析JDK中构建UUID的相关逻辑

UUID.randomUUID()

public static UUID randomUUID() {
SecureRandom ng 等于 Holder.numberGenerator

    byte[] randomBytes = new byte[16];
ng接着生成随机字节。ng获取下一批字节。这些字节是随机生成的。
    randomBytes[6]  &= 0x0f;  /* clear version        */
    randomBytes[6]  |= 0x40;  /* set to version 4     */
    randomBytes[8]  &= 0x3f;  /* clear variant        */
    randomBytes[8]  |= 0x80;  /* set to IETF variant  */
    return new UUID(randomBytes);
}

不难发现,randomUUID首先会构建一个SecureRandom对象。SecureRandom是Java中的一个类。它主要用于生成安全的伪随机数。看到随机数生成,可能你会很快想到Math库中的Random函数。虽然Random函数也能生成随机数,但两者存在差距。

具体而言 SecureRandom 是一个工具类 专门设计用来生成安全性强的伪随机数 主要应用于密码学 安全通信以及安全相关应用 SecureRandom 会采用多重底层随机源 并使用强加密算法 以此确保生成的随机数具备高度随机性和不可预测性而Random:普通的Random类生成随机数的质量比不上SecureRandom。它借助一个伪随机数生成算法来生成随机数。通常采用的是线性同余法。在某些情形下。其生成的结果实际上是能够被预测的

为避免构建重复的UUID,此处选用SecureRandom构建安全系数更高的随机数。获取随机数后,从SecureRandom中获取16个随机字节。接着进行一通位运算操作。

为分析清楚这些位运算操作究竟做了哪些操作。接下来进行假设。假设ng.nextBytes(randomBytes)读取到的内容是由1组成的随机串。也就是说randomBytes中的内容为0xFF。

首先,执行randomBytes[6] &= 0x0f,也就是FF & 0F。操作后,randomBytes[6]的结果为0F。随后,执行0F|40的操作。可以发现,经过这一操作,randomBytes[6]存储的内容为4f。后续会对randomBytes[8]进行操作。这些操作主要用来计算变体的相关信息。

注:可参考ULID:构建分布式ID的另一种选择的相关分析获取有关UUID变体的相关信息

总之若生成的16个随机字节全是11111111。那么经上述位运算后。得到的UUID的版本如下所示。得到的UUID的变体如下所示。

剩下的操作是进行组装。它要把SecureRandom生成的16个随机字节进行组装。组装成一个128位的UUID。

最终 返回生成的UUID 当16个随机字节全是11111111时 最终得到的UUID是ffffffff-ffff-4fff-8fff-ffffffffffff

至于其中的8你可能会感到疑惑。接下来,我们来解释下8的由来。在分析之前首先明确一点。8来自于UUID中的变体字段。UUID变体字段通常占据了UUID的13 - 16个字节。具体来说。UUID的第13个字节的高4位必须是固定的。第13个字节的低4位是变化的。它将在0 - 15之间。

生成UUID时 变体字段一般依规范设置 用来指示UUID的生成方法与结构 变体字段低4位虽能是0到15间任意值 但实际中通常设为10 表示基于随机数生成UUID 这就是示例UUID中8的由来

确定好UUID中的版本信息。确定好UUID中的变体内容。之后下一步要做的。是通过new UUID(randomBytes)返回一个UUID实例对象。

接下来 我们看看UUID的构造函数做了哪些操作 其内部相关代码如下

UUID的构造函数

利用长度是16的字节数组 计算mostSigBits的值 计算leastSigBits的值 用这两个值初始化UUID实例
private UUID(byte[] data) {
    long msb = 0;
    long lsb = 0;
    assert data.length == 16 : 数据的长度必须为16字节;
    for (int i=0; i<8; i++)
        msb = (msb << 8) | (data[i] & 0xff);
    for (int i=8; i<16; i++)
        lsb = (lsb << 8) | (data[i] & 0xff);
    this.mostSigBits = msb;
    this.leastSigBits = lsb;
}

上述代码里的变量mostSigBits和leastSigBits,分别代表UUID的最高有效位与最低有效位。进一步讲,在UUID够赞函数内部,会借助二进制的移位操作。通过此操作,data中字节数组的所有位,会转移到mostSigBits和leastSigBits上。最终构成一个128位的UUID字符,并返回结果。

总结

在Java里,UUID.randomUUID()是一种我们常用以构建UUID信息的方法。该方法内部大致逻辑如下:

“生成随机数”:在randomUUID()内部。其会借助SecureRandom强随机数生成器产生随机数据。这个随机数是构建新UUID的基础。

「调整随机数以符合UUID规范」:按照UUID版本4的规范来。构建uuid时要提供版本号。还要提供变体信息。这两个数据位置一般是固定的。即

使用私有构造函数创建 UUID 对象:借助 SecureRandom 生成随机数。随机数经调整。调整后被分为两个 64 位数据。两个 64 位数据分别代表 UUID 的最高有效位和最低有效位。最终返回一个 UUID 实例

以上便是每次调用randomUUID()背后的逻辑。它基于强随机数生成器SecureRandom。所以生成的UUID在实践中具备非常高的唯一性与随机性。这才致使它们适宜用作数据库主键、对象标识符等。

至此 我们对UUID内部的randomUUID方法做了细致分析 希望文章能帮助你理解UUID

点击关注公众号,”技术干货”及时达!

你可能想看:
« 上一篇
下一篇 »

相关推荐

java中什么是冒泡,冒泡算法

2026年09月06日

27阅读

java中冒号,java双冒号

2026年09月06日

25阅读

java中的->,java中的是什么意思

2026年09月06日

24阅读

java中的super用法,Java中super的说明

2026年09月06日

27阅读

java中反射的应用,java中反射的应用场景

2026年09月06日

23阅读

java中开关的定义,Java关键字有哪些

2026年09月06日

25阅读