ThreadLocalRandom类是JDK 7在JUC包下新增的随机数生成器,它弥补了Random类在多线程下的缺陷。
Random类——java.util.Random
Random random = new Random();int randomnum = random.nextInt();
1、单线程下的随机数生成原理
(1)一个默认的种子,这个种子其实是一个long类型的数字,你可以在创建Random对象时通过构造函数指定,如果不指定则在默认构造函数内部生成一个默认的值。 (2)首先根据老的种子生成新的种子,然后根据新的种子来计算新的随机数。 在单线程情况下每次调用nextInt都是根据老的种子计算出新的种子,这是可以保证随机数产生的随机性的。
2、多线程情况下存在的问题?
在多线程下多个线程可能都拿同一个老的种子去执行步骤(4)以计算新的种子,这会导致多个线程产生的新种子是一样的,由于步骤(5)的算法是固定的,所以会导致多个线程产生相同的随机值。
3、多线程情况下random如何保证生成的数的随机性?
当多个线程根据同一个老种子计算新种子时,第一个线程的新种子被计算出来后,第二个线程要丢弃自己老的种子,而使用第一个线程的新种子来计算自己的新种子,依此类推,只有保证了这个,才能保证在多线程下产生的随机数是随机的。Random函数使用一个原子变量达到了这个效果,在创建Random对象时初始化的种子就被保存到了种子原子变量里面,next()的代码如下。

代码(8)使用CAS操作,它使用新的种子去更新老的种子,在多线程下可能多个线程都同时执行到了代码(6),那么可能多个线程拿到的当前种子的值是同一个,然后执行步骤(7)计算的新种子也都是一样的,但是步骤(8)的CAS操作会保证只有一个线程可以更新老的种子为新的,失败的线程会通过循环重新获取更新后的种子作为当前种子去计算老的种子,这就解决了上面提到的问题,保证了随机数的随机性。
4、Random的缺点
由于原子变量的更新是CAS操作,同时只有一个线程会成功,所以会造成大量线程进行自旋重试,这会降低并发性能,所以ThreadLocalRandom应运而生。 原因是多个线程会使用同一个原子性种子变量,从而导致对原子变量更新的竞争。
ThreadLocalRandom类
ThreadLocalRandom random = new TreadLocalRandom.current();int randomnum = random.nextInt(5);
1、优点
每个线程都维护一个种子变量,则每个线程生成随机数时都根据自己老的种子计算新的种子,并使用新种子更新老的种子,再根据新种子计算随机数,就不会存在竞争问题了,这会大大提高并发性能。
2、类图

(1)ThreadLocalRandom类继承了Random类并重写了nextInt方法 (2)没有使用继承自Random类的原子性种子变量。具体的种子存放在具体的调用线程的threadLocalRandomSeed变量里面。当线程调用ThreadLocalRandom的current方法时,ThreadLocalRandom负责初始化调用线程的threadLocalRandomSeed变量,也就是初始化种子。 (3)seeder和probeGenerator是两个原子性变量,在初始化调用线程的种子和探针变量时会用到它们,每个线程只会使用一次。 (4)变量instance是ThreadLocalRandom的一个实例,该变量是static的。当多线程通过ThreadLocalRandom的current方法获取ThreadLocalRandom的实例时,其实获取的是同一个实例。 (5)是线程安全的。
3、主要方法
| ThreadLocalRandom current() | 该方法获取ThreadLocalRandom实例,并初始化调用线程中的threadLocalRandomSeed和threadLocalRandomProbe变量 |
|---|---|
| int nextInt(int bound) | 计算当前线程的下一个随机数。 |
