java|Java并发编程—ThreadLocalRandom类

1 结构分析 【java|Java并发编程—ThreadLocalRandom类】因为Random类在高并发情况下将造成多个线程竞争同一个原子类种子,所以为了提高高并发性能诞生了该类,即让每一个Thread都持有一个种子计算自己的随机数,ThreadLocalRandom类的类图如下:
java|Java并发编程—ThreadLocalRandom类
文章图片

Thread类中和ThreadLocalRandom相关的字段
java|Java并发编程—ThreadLocalRandom类
文章图片

2 主要方法分析 1 current()方法
该方法会获得ThreadLocal实例并且将会初始化线程中的Seed和Probe属性(这两个属性初始值都是0)

public static ThreadLocalRandom current() { //利用unsafe来判断当前线程的Probe是否等于0, //等于0则表示该线程是第一次调佣该方法,然后会接着调用localInit()方法进行初始化 if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0) localInit(); //返回ThreadLocalRandom实例,该方法是静态方法,所有线程调用该方法返回的是同一个ThreadLocalRandom实例 return instance; }

static final void localInit() { //下面三行代码是用来初始化probe和seed int p = probeGenerator.addAndGet(PROBE_INCREMENT); int probe = (p == 0) ? 1 : p; // skip 0 long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT)); //下面三行代码使用来设置当前线程的probe和seed,完成当前线程属性的初始化 Thread t = Thread.currentThread(); UNSAFE.putLong(t, SEED, seed); UNSAFE.putInt(t, PROBE, probe); }

2 nextInt(int bound)方法
该方法用来计算下一个随机数
public int nextInt(int bound) { //传入参数不合法抛出异常 if (bound <= 0) throw new IllegalArgumentException(BadBound); //根据当前线程中的种子进行计算新种子 int r = mix32(nextSeed()); int m = bound - 1; if ((bound & m) == 0) // power of two r &= m; else { // reject over-represented candidates for (int u = r >>> 1; u + m - (r = u % bound) < 0; u = mix32(nextSeed()) >>> 1) ; } return r; }

3 用法 在线程内部调用current()方法获得ThreadLocalRandom实例后再调用相应方法获得随机数,注意一定不要和用Random类一样在main线程创建实例后再在子线程中使用,因为仅仅在main线程中调用后只会初始化main线程的seed和probe,而子线程中由于没有进行初始化seed和probe的工作所以最后得到的随机数是一样的(原因可以看前述localInit()方法源,只初始化了当前线程的seed,没有调用current()方法的线程都是相同的默认初始值)
public static void main(String[] args) { //创建线程1并启动 new Thread(() -> { ThreadLocalRandom current = ThreadLocalRandom.current(); System.out.println(current.nextInt(10)); }).start(); //创建线程2并启动 new Thread(() -> { ThreadLocalRandom current = ThreadLocalRandom.current(); System.out.println(current.nextInt(10)); }).start(); }

错误用法,两个线程不论执行多少次最后得到的随机数都是4
public static void main(String[] args) { ThreadLocalRandom current = ThreadLocalRandom.current(); //创建线程1并启动 new Thread(() -> { //ThreadLocalRandom current = ThreadLocalRandom.current(); System.out.println(current.nextInt(100)); //4 }).start(); //创建线程2并启动 new Thread(() -> { //ThreadLocalRandom current = ThreadLocalRandom.current(); System.out.println(current.nextInt(100)); //4 }).start(); }


    推荐阅读