Welcome to the public number [sharedCode] committed to mainstream middleware source code analysis, personal website: www.shared-code.com/

ThreadLocalRandom introduction

ThreadLocalRandom is a random number generation utility class released after JDK1.7 that performs better than traditional math.random ().

Performance comparison

ThreadLocalRandom vs. Math.random()

public class Test {

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

        CountDownLatch countDownLatch = new CountDownLatch(10);
        long startTime = System.currentTimeMillis();
        Math.random() opens 10 threads
        for (int i = 0; i < 10 ; i++){
            Thread thread = new Thread(() -> {
                // Loop 10000 times
                for (int j = 0; j < 10000 ; j++) {
                    int x = (int) Math.random();
                }
                countDownLatch.countDown();
            });
            thread.start();
        }
        countDownLatch.await();
        System.out.println("Math.random() time =" + (System.currentTimeMillis()-startTime));
        long startTimeLocal = System.currentTimeMillis();
       // ThreadLocalRandom starts 10 threads
        for (int i = 0; i < 10 ; i++){
            Thread thread = new Thread(() -> {
                for (int j = 0; j < 10000 ; j++) {
                    intx = ThreadLocalRandom.current().nextInt(); }}); thread.start(); } System.out.println("ThreadLocalRandom time ="+ (System.currentTimeMillis()-startTimeLocal)); }}Copy the code

The test results are as follows:

First run: math.random () Time =112ThreadLocalRandom time-consuming =7Second run: math.random () Time =115ThreadLocalRandom time-consuming =15Third run: math.random () Time =94ThreadLocalRandom time-consuming =9Fourth run: math.random () Time =126ThreadLocalRandom time-consuming =16  
Copy the code

From the summary above, it is clear that ThreadLocalRandom performs 8-10 times better

Source code analysis

public static ThreadLocalRandom current(a) {
         // Calls the unsafe class using this method to retrieve the offset value from the current object
        if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
            // If the value is 0, the current thread is not initialized.
            localInit();
        return instance;
    }
/** * This method basically puts the current thread, the seed object, into the MAP structure, */
static final void localInit(a) {
        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);
    }
Copy the code

So let’s look at the nextInt method.

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

/** * From the current thread, take the seed and return it for random number calculation */
final long nextSeed(a) {
        Thread t; long r; // read and update per-thread seed
        UNSAFE.putLong(t = Thread.currentThread(), SEED,
                       r = UNSAFE.getLong(t, SEED) + GAMMA);
        return r;
}
Copy the code

A core parameter of random number is seed value. Seed is the seed in the random algorithm. By multiplying this seed with an unknown number “0xff51afd7ed558ccdL”, an unknown number is obtained.

# # # Math. The Random implementation

protected int next(int bits) {
        long oldseed, nextseed;
        // Multiple threads get the same seed object
        AtomicLong seed = this.seed;
        do {
            / / the current value
            oldseed = seed.get();
            // The next value is calculated by some sort of calculation
            nextseed = (oldseed * multiplier + addend) & mask;
          // Make sure the update is correct by CAS operation
        } while(! seed.compareAndSet(oldseed, nextseed));return (int)(nextseed >>> (48 - bits));
    }
Copy the code

As you can clearly see from the above, the original implementation was for all threads to share a seed object. Although CAS was used to update the seed object, the loop in do while was still retry, which was very expensive in high concurrency scenarios.

Conclusion:

ThreadLocalRandom gets its own seed object from each thread. There is no competition between threads for resources, so the speed is fast.