Java并发包中ThreadLocalRandom类原理剖析
一、Random类的局限性
Random类产生随机数的两个步骤
- 根据老的种子生成新的种子
- 根据新的种子计算新的随机数
在并发条件下,由于Random类在产生新种子的时候不是原子性操作,这就可能导致多个线程拿到了相同的新种子,产生了相同的随机数,破坏了随机性。random函数使用了一个原子变量能够让新的种子被计算出来之后,其他线程丢弃自己老的种子,而使用新产生出来的种子作为老种子计算自己的新种子,其中通过CAS来控制,保证了随机数的随机性。
二、ThreadLocalRandom
JUC包下的ThreadLocalRandom弥补了Random的缺陷。
它的原理是通过每个线程维护一个种子变量,每个线程计算自己的随机数时,都是根据自己老的种子去计算新的种子,避免了竞争,因此是线程安全的。
同时ThreadLocalRandom继承了Random类,重写了nextInt方法,但是没有继承原子类,而是依赖Thread里面的ThreadLocalRandomSeed变量。
current()方法解析
- 判断线程是否是第一次调用
- 如果是则调用初始化方法,初始化中,根据probeGenerator计算当前线程的threadLocalRandomProbe,再根据seeder计算当前线程的初始化种子,再把这个两个变量赋值到当前线程中。
- 如果不是,则返回当前实例,其中current方法是个静态方法,意味着所有线程返回的都是同一实例。
nextInt方法还是根据当前新种子计算随机数。
nextSeed方法是通过获取当前线程中的threadLocalRandomSeed变量的值,然后累加GAMMA值作为新种子,最后把新的种子放入到当前线程的threadLocalRandomSeed中。