Ref

  • Multithreaded ThreadLocalRandom usage | under the nuggets
  • The Random and distinguish ThreadLocalRandom | the Denver nuggets

Random numbers are used in Java

Java and random number related package, mainly including three

  1. java.lang.Math.random
  2. java.util.Random
  3. java.util.concurrent.ThreadLocalRandom

java.lang.Math.random

Math.random() returns a random double number in an interval [0.0,1.0] that is closed and open.

double val = Math.random();
Copy the code

Note that math.random () returns a double (0.0,1.0). If you want to get a random integer, it is not recommended to magnify val by 10 and round it.

A better solution is to use the nextInt or nextLong methods of Random objects directly.

java.util.Random

Random() has two constructors

  1. Random(): Creates a new random number generator that defaults to the milliseconds of the current system time as the seed number
  2. Random(long seed): Creates a new random number generator with the specified seed
/** * @param seed the initial seed */ public Random(long seed) { if (getClass() == Random.class) this.seed = new AtomicLong(initialScramble(seed)); else { // subclass might have overriden setSeed this.seed = new AtomicLong(); setSeed(seed); }}Copy the code

The most common method of the Random class is nextInt(), which returns a Random int value. The method can accept arguments such as nextInt(100), which returns a random number in the range [0,100], which is left and right open.

    // Returns a random integer of type int
    int nextInt(a)          
    
    // return a random integer of type [0,num], including 0 but not num
    int nextInt(int num) 
Copy the code

The source code for the nextInt(bound) method is shown below.

    public int nextInt(int bound) {
        if (bound <= 0)
            throw new IllegalArgumentException(BadBound);

        int r = next(31);
        int m = bound - 1;
        if ((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)); }return r;
    }
Copy the code

Random number between [min, Max

Random numbers between [min, Max] can be generated using the following code.

Random rand = new Random();
int num = rand.nextInt(MAX - MIN + 1) + MIN;
Copy the code

seed

For Random objects with the same seed, the Random number sequence generated is the same.

    private void test7(a){
        for(int j=0; j<5; j++){ System.out.println("= = = = the first"+ j +"Time = = = =");
            Random random = new Random(100);
            for(int i=0; i<4; i++){ System.out.println("index_"+ i +":" + random.nextInt(100)); }}}Copy the code

To execute the above code, enter the following. It can be seen that for Random objects with the same seed, the generated Random number sequence is the same, which is a pseudo-random number.

Pseudo random is random with rules.

==== 1st pass ==== index_0:15 index_1:50 index_2:74 index_3:88 ==== 2nd pass ==== Index_0:15 index_1:50 index_2:74 index_3: 88 ==== 3rd time ==== index_0:15 index_1:50 index_2:74 index_3:88 ==== 4th time ==== Index_0:15 index_1:50 index_2:74 index_3:88Copy the code

java.util.concurrent.ThreadLocalRandom

In multithreading, using java.util.Random to fetch Random numbers is thread-safe. In the case of a given seed, multiple threads will compete for the same seed, resulting in performance degradation.

This is because Random takes 2 steps to generate a new Random number

  1. According to the oldseedGenerate a newseed
  2. By the newseedCompute new random numbers

The algorithm for step 2 is fixed; if each thread concurrently fetches the same seed, the resulting random number will be the same. To avoid this, Random uses CAS operations to ensure that only one thread at a time can get and update the seed, and that any thread that fails needs spin retries.

Therefore, Random is not appropriate for multithreading. To solve this problem, ThreadLocalRandom is introduced, which maintains a seed variable for each thread in multithreading so that there is no race.

JMH performance comparison test -ThreadLocalRandom-Random

The following uses JMH to test the performance difference between ThreadLocalRandom and Random in the concurrent case.

First, import the following dependencies (JMH comes with JDK9 and does not need to import dependencies)

        <! --JMH-->
        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-core</artifactId>
            <version>1.23</version>
        </dependency>
        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-generator-annprocess</artifactId>
            <version>1.23</version>
        </dependency>
Copy the code

Write the test code as follows

@BenchmarkMode(Mode.Throughput) // Test type: throughput
@Warmup(iterations = 3, time = 1)
@Threads(100)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class SpbAppApplication {
    public static void main(String[] args) throws RunnerException {
// SpringApplication.run(SpbAppApplication.class, args);

        // Start the benchmark
        Options opt = new OptionsBuilder()
                .include(SpbAppApplication.class.getSimpleName()) // The test class to import
                .warmupIterations(5) // Preheat 5 rounds
                .measurementIterations(10) // Measure 10 rounds
                .forks(1)
                .build();
        new Runner(opt).run(); // Execute the test
    }



    /** * Random performance test */
    @Benchmark
    public void randomTest(a) {
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            // Generate random numbers from 0 to 9
            random.nextInt(10); }}/** * ThreadLocalRandom performance test */
    @Benchmark
    public void threadLocalRandomTest(a) {
        ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
        for (int i = 0; i < 10; i++) {
            threadLocalRandom.nextInt(10); }}}Copy the code

Set forks(1) and @threads () to 16 and 100 respectively to test throughput comparisons for 16 and 100 Threads in a single process.

The test results are as follows, where Cnt represents how many times it is run, Score represents the result of execution, and Units represents throughput per second.

# Threads: 16 threads, will synchronize iterations
Benchmark                                 Mode  Cnt      Score      Error   Units
SpbAppApplication.randomTest             thrpt   10   4996.466 ± 3629.543  ops/ms
SpbAppApplication.threadLocalRandomTest  thrpt   10  37720.715 ± 6566.971  ops/ms


# Threads: 100 threads, will synchronize iterations
Benchmark                                 Mode  Cnt      Score       Error   Units
SpbAppApplication.randomTest             thrpt   10   3259.461 ±  1819.021  ops/ms
SpbAppApplication.threadLocalRandomTest  thrpt   10  29948.252 ± 10276.214  ops/ms
Copy the code

As can be seen from the results of the JMH test, under the condition of 16 concurrent threads,ThreadLocalRandomThe throughput in the concurrent case is aboutRandomFive times. With 100 concurrent threads, the gap widens nearly tenfold.

Therefore, for high concurrency, use ThreadLocalRandom whenever possible.

Use in multiple threads

In each thread with ThreadLocalRandom. Current () to obtain object instance, then call nextInt () method for the random number.

import java.util.concurrent.ThreadLocalRandom;

public class ThreadLocalRandomDemo {

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            newPlayer().start(); }}private static class Player extends Thread {
        @Override
        public void run(a) {
            System.out.println(getName() + ":" +
            ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
            threadLocalRandom.nextInt(100)); }}}Copy the code

It is important to note that the multithreading cannot put ThreadLocalRandom. Under the current () is set to the final, namely the following code. Otherwise, the random numbers generated by multiple threads are the same. Details you can refer to a multithreaded ThreadLocalRandom usage | under the nuggets.

import java.util.concurrent.ThreadLocalRandom;

public class ThreadLocalRandomDemo {

    private static final ThreadLocalRandom RANDOM =
            ThreadLocalRandom.current();

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            newPlayer().start(); }}private static class Player extends Thread {
        @Override
        public void run(a) {
            System.out.println(getName() + ":" + RANDOM.nextInt(100)); }}}Copy the code

The output of the above program is as follows.

Thread-0: 4
Thread-1: 4
Thread-2: 4
Thread-3: 4
Thread-4: 4
Thread-5: 4
Thread-6: 4
Thread-7: 4
Thread-8: 4
Thread-9: 4
Copy the code