Java并发编程—ThreadLocalRandom类

1 结构分析

因为Random类在高并发情况下将造成多个线程竞争同一个原子类种子,所以为了提高高并发性能诞生了该类,即让每一个Thread都持有一个种子计算自己的随机数,ThreadLocalRandom类的类图如下:

Java并发编程—ThreadLocalRandom类_第1张图片

 Thread类中和ThreadLocalRandom相关的字段

Java并发编程—ThreadLocalRandom类_第2张图片

 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();
    }

你可能感兴趣的:(java,开发语言,后端,并发编程)