From: juejin. Cn/post / 684490…

Jack Johnson recently crashed his car in an interview. However, to his dismay, this time he stumbled on Random, which he often uses……

Interview questions

Why do you need ThreadLocalRandom when you already have Random?

The body of the

Math.random() is the most widely used Random number generation tool, and even the bottom layer of math.random () is implemented with Random.

It can be seen that
Math.random()It points directly to
Random.nextDouble()Methods.

The Random use of

Before we begin, let’s take a look at the use of Random.

Random random = new Random();
for(int i = 0; i < 3; I++) {// generate a random integer from 0 to 9 random.nextint (10); } Duplicate codeCopy the code

The execution results of the above programs are as follows:

1

0

7

Random source code parsing

Random generates Random integers using the nextInt() method. Let’s look at his implementation source code:

Public int nextInt(int bound) {public int nextInt(int bound)if(bound <= 0) throw new IllegalArgumentException(BadBound); Int r = next(31); Int m = bound-1; // Calculate the random number according to the new seedif ((bound & m) == 0)  // i.e., bound is a power of 2
        r = (int)((bound * (long)r) >> 31);
    else {
        for (int u = r;
                u - (r = u % bound) + m < 0;
                u = next(31))
            ;
    }
    returnr; } Duplicate codeCopy the code

We can see from the above source code, the core of the whole source code has two parts:

  1. Making new seeds from old seeds;
  2. Calculate a random number from the new seed.

The code for calculating random numbers from the new seed is already clear, so we need to confirm how the next() method is implemented, continue to look at the source code:

/** * JDK 11 */ protected int next(int bits) {// declare oldseed and new seed long oldseed, nextseed; AtomicLong seed = this.seed;do{oldseed = seed.get(); Nextseed = (oldseed * multiplier + addend) &mask; }while(! seed.compareAndSet(oldseed, nextseed)); // Use CAS to update seedsreturn(int)(nextseed >>> (48 - bits)); } Duplicate codeCopy the code

According to the above source code, it can be seen that when using the old seed to obtain the new seed, if it is multi-threaded operation, only one thread CAS (Conmpare And Swap, compare And exchange) will succeed at the same time, And other failed threads will wait to obtain the new seed through spin, so there will be a certain performance consumption.

That’s why JDK 1.7 introduced the answer to ThreadLocalRandom, which was introduced to improve the performance of Random in multithreaded situations. So how does it ascend? Let’s see.

ThreadLocalRandom use

Let’s start by looking at the class diagram of ThreadLocalRandom:

ThreadLocalRandom inherits from the Random class.

ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
for(int i = 0; i < 3; I++) {/ / generates random number from 0 to 9 System. Out. The println (threadLocalRandom. NextInt (10)); } Duplicate codeCopy the code

The execution results of the above programs are as follows:

1

7

5

ThreadLocalRandom, like Random, is generated by the nextInt() method.

ThreadLocalRandom source code parsing

ThreadLocalRandom generates a random number from ThreadLocalRandom.

Public int nextInt(int bound) {if(bound <= 0) throw new IllegalArgumentException(BAD_BOUND); Int r = mix32(nextSeed()); int m = bound - 1; // Calculate the random number according to the new seed calculationif ((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)
            ;
    }
    returnr; } Duplicate codeCopy the code

NextSeed () nextSeed() nextSeed() nextSeed() nextSeed() nextSeed() nextSeed()

/** * JDK 11 */ final LongnextSeed() {
    Thread t; long r; // readU.putlong (t = thread.currentThread (), seed, r = u.getLong (t, SEED) + GAMMA);returnr; } @HotSpotIntrinsicCandidate public native void putLong(Object o, long offset, long x); Copy the codeCopy the code

ThreadLocalRandom does not use CAS and spin to obtain new seeds as Thread does. Instead, ThreadLocalRandom uses the old seeds saved in each Thread to generate new seeds, thus avoiding multi-thread contention and spin waiting time. So performance is higher in multithreaded environments.

ThreadLocalRandom Precautions

When using ThreadLocalRandom, it is important to note that you cannot share a ThreadLocalRandom object in multiple threads, otherwise you will generate the same random number, as shown in the following code:

/ / declare multithreaded ExecutorService service = Executors. NewCachedThreadPool (); / / Shared ThreadLocalRandom ThreadLocalRandom ThreadLocalRandom = ThreadLocalRandom. Current ();for(int i = 0; i < 10; Submit (() -> {system.out.println (thread.currentThread ().getName() +)":"+ threadLocalRandom.nextInt(10)); ; }); } Duplicate codeCopy the code

The execution results of the above procedures are as follows:

pool-1-thread-2:4

pool-1-thread-1:4

pool-1-thread-3:4

pool-1-thread-10:4

pool-1-thread-6:4

pool-1-thread-7:4

pool-1-thread-4:4

pool-1-thread-9:4

pool-1-thread-8:4

pool-1-thread-5:4

Random VS ThreadLocalRandom

Random generates and obtains new seeds, as shown in the figure below:

ThreadLocalRandom generates and obtains new seeds, as shown below:

The performance comparison

Next, we use JMH (Java Microbenchmark Harness, Java Microbenchmark Suite), a performance test tool provided by Oracle. To test the throughput of Random and ThreadLocalRandom (number of successful programs executed per unit of time) :

import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; /** * JDK: 11 * Windows 10 i5-4460/12g */ @benchmarkmode (mode.throughput) // // @threads (16) @outputTimeUnit (timeunit.milliseconds) public class MILLISECONDS {public static void main(String[]) Args) throws RunnerException {// Start the benchmark. Options opt = new OptionsBuilder() . Include (RandomExample. Class. GetSimpleName ()) / / to import test class warmupIterations (5) / / preheating 5 rounds. MeasurementIterations (10) / / Measure 10 rounds.forks(1).build(); new Runner(opt).run(); /** * Random performance test */ @benchmark public voidrandomTest() {
        Random random = new Random();
        for(int i = 0; i < 10; I++) {// generate a random number from 0 to 9 random.nextint (10); } /** * ThreadLocalRandom performance test */ @benchmark public voidthreadLocalRandomTest() {
        ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
        for(int i = 0; i < 10; i++) { threadLocalRandom.nextInt(10); }}} copy the codeCopy the code

The test results are as follows:

Among them,
Cnt is how many times it was run, Score is how many times it was executed, and Units is throughput per second.

As you can see from the results of the JMH tests, ThreadLocalRandom performs about 5 times better than Random in the concurrent case.

Complete benchmark code download: github.com/vipstone/bl…

conclusion

This paper describes the use and source code analysis of Random and ThreadLocalRandom. Random generates Random numbers through CAS and spin. In multi-threaded mode, only one thread can obtain new seeds through CAS and generate Random numbers at the same time, while other threads can only spin and wait. So there is a certain performance loss. ThreadLocalRandom was added in JDK 1.7, and its seeds are stored in its own threads, so there is no spin waiting, so high concurrency is better.

Finally, according to the official benchmark tool JMH, The performance of ThreadLocalRandom is about 5 times that of Random, so try to use ThreadLocalRandom in high concurrency situations.

Reference & thanks for the beauty of Concurrent Programming in Java