ThreadLocalRandom随机数源码分析

ThreadLocalRandom介绍

ThreadLocalRandom是JDK1.7以后提供出来的一个随机数生成工具类,性能比传统的Math.random()更高。

性能比较

ThreadLocalRandom和Math.random()的性能比较,测试步骤如下:

public class Test {

    public static void main(String[] args) throws InterruptedException {

        CountDownLatch countDownLatch = new CountDownLatch(10);
        long startTime = System.currentTimeMillis();
        //Math.random()  开启10个线程
        for (int i = 0; i < 10 ; i++){
            Thread thread = new Thread(() -> {
                // 循环10000次
                for (int j = 0; j < 10000 ; j++) {
                    int x = (int) Math.random();
                }
                countDownLatch.countDown();
            });
            thread.start();
        }
        countDownLatch.await();
        System.out.println("Math.random() 耗时 = " + (System.currentTimeMillis()-startTime));
        long startTimeLocal = System.currentTimeMillis();
       // ThreadLocalRandom  开启10个线程
        for (int i = 0; i < 10 ; i++){
            Thread thread = new Thread(() -> {
                for (int j = 0; j < 10000 ; j++) {
                    int x = ThreadLocalRandom.current().nextInt();
                }
            });
            thread.start();
        }
        System.out.println("ThreadLocalRandom 耗时 = " + (System.currentTimeMillis()-startTimeLocal));

    }
}

测试结果如下:

第一次运行:
Math.random() 耗时 = 112
ThreadLocalRandom 耗时 = 7 

第二次运行:
Math.random() 耗时 = 115
ThreadLocalRandom 耗时 = 15  
  
第三次运行:
Math.random() 耗时 = 94
ThreadLocalRandom 耗时 = 9  
 
第四次运行:
Math.random() 耗时 = 126
ThreadLocalRandom 耗时 = 16  

通过上面的总结,我们可以很清晰看到,ThreadLocalRandom的性能优秀8-10倍

源码分析

public static ThreadLocalRandom current() {
         // 调用unsafe类里面通过这个方法是从当前对象中获取偏移offset的值
        if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
            // 等于0 ,说明当前线程没有初始化,调用初始化方法。
            localInit();
        return instance;
    }
/**
* 该方法,主要是将当前线程,seed对象,放入到MAP结构中,
*/
static final void localInit() {
        int p = probeGenerator.addAndGet(PROBE_INCREMENT);
        int probe = (p == 0) ? 1 : p; // skip 0
        long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
        Thread t = Thread.currentThread();
        UNSAFE.putLong(t, SEED, seed);
        UNSAFE.putInt(t, PROBE, probe);
    }

下面我们看一下nextInt 这个方法。

public int nextInt() {
        return mix32(nextSeed());
}

/**
 * 通过当前线程,拿到seed , 返回出去,用作随机数计算
 */
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;
}

随机数的一个核心参数就是seed值的 , seed就是随机算法中的种子 , 通过这个种子和一个未知的数"0xff51afd7ed558ccdL" 相乘,然后得到一个未知的数,根据不同的类型截取不同的长度,如int, long 等数据类型。

Math.Random实现

protected int next(int bits) {
        long oldseed, nextseed;
        // 多个线程获取到的是同一个seed对象
        AtomicLong seed = this.seed;
        do {
            // 当前值
            oldseed = seed.get();
            // 下一个值通过某种计算出来
            nextseed = (oldseed * multiplier + addend) & mask;
          // 通过CAS操作去更新,保证更新正确
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed >>> (48 - bits));
    }

从上面很清晰的就看到了,原始的实现是所有的线程共享一个seed对象,虽然用的是CAS去更新,但是do while里面还是通过循环是重试,在高并发场景下是很耗费资源的。

总结:

ThreadLocalRandom是通过每个线程获取自己的seed对象,不存在线程之间的资源竞争,因此速度那是快的飞起。

你可能感兴趣的:(ThreadLocalRandom随机数源码分析)