🚀 Java 随机数深度剖析:Random 与 ThreadLocalRandom 源码解析
Java 中生成随机数的两个核心类:Random 和 ThreadLocalRandom,深入源码分析为什么在高并发场景下 ThreadLocalRandom 是更好的选择。
🔍 什么是伪随机数?
Random 和 ThreadLocalRandom 生成的都是伪随机数。这意味着它们的随机数序列是完全确定的,是基于一个初始的 seed(种子)通过固定算法计算出来的。只要种子相同,生成的随机数序列就完全相同。
🧩 Random 类的实现原理
1. 初始化种子
创建 Random 对象时,可以指定种子,也可以让它自动生成一个:
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
private static long seedUniquifier() {
for (;;) {
long current = seedUniquifier.get();
long next = current * 1181783497276652981L;
if (seedUniquifier.compareAndSet(current, next))
return next;
}
}
public Random(long seed) {
if (getClass() == Random.class)
this.seed = new AtomicLong(initialScramble(seed));
else {
this.seed = new AtomicLong();
setSeed(seed);
}
}
- 无参构造:通过
seedUniquifier()和System.nanoTime()异或生成一个相对唯一的种子。 - 有参构造:直接使用传入的
seed,并通过initialScramble方法打乱。
2. 核心随机数生成
每次调用 nextXxx() 方法时,Random 都会基于当前种子计算出新的种子,并生成随机数:
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
public long nextLong() {
return ((long)(next(32)) << 32) + next(32);
}
- 核心逻辑:使用 CAS 操作原子性地更新种子,确保线程安全。
- 生成过程:新种子 = (旧种子 × 乘数 + 增量) & 掩码,然后通过位移操作得到最终的随机数。
⚠️ 并发场景下 Random 的短板
Random 是线程安全的,但它的线程安全是通过 CAS 操作来保证的。在高并发场景下,大量线程会同时竞争更新同一个 AtomicLong 类型的种子:
- CAS 自旋:当多个线程同时执行
seed.compareAndSet时,只有一个线程能成功,其他线程会进入自旋重试。 - CPU 开销大:自旋会导致 CPU 空转,浪费大量资源,同时也会降低整体吞吐量。
这种竞争问题,和我们之前分析 AtomicLong 在高并发下的问题如出一辙。
💡 高并发救星:ThreadLocalRandom
为了解决 Random 在高并发下的性能问题,JDK 1.7 引入了 ThreadLocalRandom。它的核心思想是让每个线程拥有自己的种子,彻底避免了竞争。
1. ThreadLocalRandom 的设计思想
ThreadLocalRandom 继承自 Random,但它的实现更像 ThreadLocal:
- 每个线程维护自己的种子,不再共享。
- 通过
Unsafe直接操作线程对象中的种子字段,效率极高。
2. 初始化与种子隔离
private static final AtomicLong seeder = new AtomicLong(
mix64(System.currentTimeMillis()) ^ mix64(System.nanoTime())
);
static {
String sec = VM.getSavedProperty("java.util.secureRandomSeed");
if (Boolean.parseBoolean(sec)) {
byte[] seedBytes = java.security.SecureRandom.getSeed(8);
long s = (long)seedBytes[0] & 0xffL;
for (int i = 1; i < 8; ++i)
s = (s << 8) | ((long)seedBytes[i] & 0xffL);
seeder.set(s);
}
}
public static ThreadLocalRandom current() {
if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
localInit();
return instance;
}
static final void localInit() {
int p = probeGenerator.addAndGet(PROBE_INCREMENT);
int probe = (p == 0) ? 1 : p;
long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
Thread t = Thread.currentThread();
UNSAFE.putLong(t, SEED, seed);
UNSAFE.putInt(t, PROBE, probe);
}
- 全局种子:
seeder是一个全局的AtomicLong,用于为每个新线程分配初始种子。 - 线程隔离:
localInit()会为当前线程生成并设置唯一的种子,存储在线程对象的SEED偏移量处。
3. 无竞争的随机数生成
public int nextInt() {
return mix32(nextSeed());
}
final long nextSeed() {
Thread t; long r;
UNSAFE.putLong(t = Thread.currentThread(), SEED,
r = UNSAFE.getLong(t, SEED) + GAMMA);
return r;
}
- 线程内无锁:每个线程只操作自己的种子,无需 CAS,无锁无竞争。
- 极致性能:通过
Unsafe直接读写内存,避免了锁和 CAS 带来的开销。
📊 Random vs ThreadLocalRandom 核心对比
| 特性 | Random | ThreadLocalRandom |
|---|---|---|
| 种子存储 | 全局共享的 AtomicLong | 每个线程独立存储,通过 Unsafe 访问 |
| 线程安全 | CAS 保证 | 线程隔离,天然安全 |
| 并发性能 | 高并发下 CAS 竞争激烈,性能差 | 无竞争,性能极高 |
| 适用场景 | 单线程或低并发场景 | 高并发场景 |
| 种子指定 | 支持 | 不支持,自动生成 |
🚀 性能对比测试代码
为了让你直观感受到两者在高并发下的性能差异,我编写了完整的性能测试代码,你可以直接运行验证:
import java.util.Random;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
/**
* Random vs ThreadLocalRandom 高并发性能对比测试
*/
public class RandomPerformanceTest {
// 测试线程数(模拟高并发)
private static final int THREAD_COUNT = 100;
// 每个线程生成随机数的次数
private static final int OP_COUNT_PER_THREAD = 1000000;
// 全局共享的Random实例
private static final Random GLOBAL_RANDOM = new Random();
// 线程池
private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(THREAD_COUNT);
public static void main(String[] args) throws InterruptedException {
// 1. 测试 Random 性能
System.out.println("===== 测试 Random 高并发性能 =====");
long randomStart = System.currentTimeMillis();
AtomicLong randomTotal = new AtomicLong(0);
CountDownLatch randomLatch = new CountDownLatch(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++) {
EXECUTOR.submit(() -> {
long count = 0;
for (int j = 0; j < OP_COUNT_PER_THREAD; j++) {
count += GLOBAL_RANDOM.nextInt();
}
randomTotal.addAndGet(count);
randomLatch.countDown();
});
}
randomLatch.await();
long randomCost = System.currentTimeMillis() - randomStart;
System.out.println("Random 总耗时:" + randomCost + "ms");
System.out.println("Random 总操作数:" + (THREAD_COUNT * OP_COUNT_PER_THREAD));
System.out.println("Random 每秒操作数:" + (THREAD_COUNT * OP_COUNT_PER_THREAD * 1000L) / randomCost);
// 2. 测试 ThreadLocalRandom 性能
System.out.println("\n===== 测试 ThreadLocalRandom 高并发性能 =====");
long threadLocalRandomStart = System.currentTimeMillis();
AtomicLong threadLocalRandomTotal = new AtomicLong(0);
CountDownLatch threadLocalRandomLatch = new CountDownLatch(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++) {
EXECUTOR.submit(() -> {
long count = 0;
for (int j = 0; j < OP_COUNT_PER_THREAD; j++) {
count += ThreadLocalRandom.current().nextInt();
}
threadLocalRandomTotal.addAndGet(count);
threadLocalRandomLatch.countDown();
});
}
threadLocalRandomLatch.await();
long threadLocalRandomCost = System.currentTimeMillis() - threadLocalRandomStart;
System.out.println("ThreadLocalRandom 总耗时:" + threadLocalRandomCost + "ms");
System.out.println("ThreadLocalRandom 总操作数:" + (THREAD_COUNT * OP_COUNT_PER_THREAD));
System.out.println("ThreadLocalRandom 每秒操作数:" + (THREAD_COUNT * OP_COUNT_PER_THREAD * 1000L) / threadLocalRandomCost);
// 3. 性能对比
System.out.println("\n===== 性能对比 =====");
System.out.println("ThreadLocalRandom 比 Random 快 " + String.format("%.2f", (double) randomCost / threadLocalRandomCost) + " 倍");
// 关闭线程池
EXECUTOR.shutdown();
}
}
测试结果说明(参考)
在普通服务器上的典型运行结果:
===== 测试 Random 高并发性能 =====
Random 总耗时:856ms
Random 总操作数:100000000
Random 每秒操作数:116822429
===== 测试 ThreadLocalRandom 高并发性能 =====
ThreadLocalRandom 总耗时:128ms
ThreadLocalRandom 总操作数:100000000
ThreadLocalRandom 每秒操作数:781250000
===== 性能对比 =====
ThreadLocalRandom 比 Random 快 6.69 倍
测试代码关键说明
- 模拟高并发:使用 100 个线程同时生成随机数,每个线程执行 100 万次随机数生成操作。
- 公平对比:保证两者执行的操作数完全一致,只对比耗时差异。
- 结果指标:通过「总耗时」和「每秒操作数」直观展示性能差距,同时计算性能提升倍数。
- 线程安全:使用
CountDownLatch等待所有线程执行完成,确保统计结果准确。
💡 小结与最佳实践
- Random 的本质:伪随机数生成器,基于固定算法和种子,线程安全但高并发下性能差。
- ThreadLocalRandom 的优化:通过线程隔离种子,彻底解决了并发竞争问题,是高并发场景的首选。
- 性能差异:在高并发下,
ThreadLocalRandom的吞吐量是Random的数倍,CPU 利用率也更高。 - 使用建议:
- 在单线程或低并发场景下,两者性能差异不大。
- 在高并发场景(如线程池、Web 服务器)中,必须使用
ThreadLocalRandom。

1656

被折叠的 条评论
为什么被折叠?



