0%

高并发下随机数的获取--ThreadlocalRandom

Random类的局限性

每个Random 实例里面都有一个原子性的种子变量用来记录当前的种子值,当要生成新的随机数时需要根据当前种子计算新的种子并更新回原子变量。在多线程下使用单个Random 实例生成随机数时,当多个线程同时计算随机数来计算新的种子时,多个线程会竞争同一个原子变量的更新操作,由于原子变量的更新是CAS 操作,同时只有一个线程会成功,所以会造成大量线程进行自旋重试, 这会降低并发性能,所以ThreadLocalRandom 应运而生。

1
2
3
4
5
6
7
8
9
10
//Random类的核心方法,CAS操作会导致高竞用情况下的性能下降
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));
}

ThreadlocalRandom

为了弥补多线程高并发情况下Random 的缺陷, ThreadLocalRandom类应运而生。ThreadLocalRandom 使用ThreadLocal的原理,让每个线程都持有一个本地的种子变量,该种子变量只有在使用随机数时才会被初始化。在多线程下计算新种子时是根据自己线程内维护的种子变量进行更新,从而避免了竞争。
ThreadLocalRandom 类似于ThreadLocal 类,就是个工具类。当线程调用ThreadLocalRandomcurrent方法时, ThreadLocalRandom 负责初始化调用线程的threadLocalRandomSeed 变量, 也就是初始化种子。
当调用ThreadLocalRandomnextlnt 方法时,实际上是获取当前线程的threadLocalRandomSeed 变量作为当前种子来计算新的种子,然后更新新的种子到当前线程的threadLocalRandomSeed 变量,而后再根据新种子并使用具体算法计算随机数。

threadLocalRandomSeed 变量是Thread 类里面的一个普通long 变量

1
2
3
4
5
6
7
   //ThreadLocalRandom类的核心代码
final long nextSeed() {
Thread t; long r; // read and update per-thread seed
UNSAFE.putLong(t = Thread.currentThread(), SEED,
r = UNSAFE.getLong(t, SEED) + GAMMA);
return r;
}

image-20211016224505557