First look at the code:

package muiltithreads;

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class DemoThreadRandom {

    public static final ThreadLocalRandom random = ThreadLocalRandom.current();
    public static void withoutGetCurrentThreadSeek(a){
        System.out.printf("Number generated by current thread %s :%d\n",Thread.currentThread().getId(),random.nextInt(100));
    }
    public static void getCurrentThreadSeek(a){
        System.out.printf("Number generated by current thread %s :%d\n",Thread.currentThread().getId(),ThreadLocalRandom.current().nextInt(100));
    }
    public static void main(String[] args) {
        System.out.println("Wrong way: When threads share the same ThreadLocalRandom");
        for (int i = 0; i < 10; i++){
            new Thread(() -> withoutGetCurrentThreadSeek()).start();
        }
        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
        System.out.println("Correct way: When each thread has its own ThreadLocalRandom");
        for (int i = 0; i < 10; i++){
            newThread(() -> getCurrentThreadSeek()).start(); }}}Copy the code

Printed results:

You can see the wrong way, the random numbers generated by multiple threads are the same, this is not what we want, so what’s wrong?

The key lies in ThreadLocalRandom. Current () :

public static ThreadLocalRandom current(a) {
        if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
            localInit();
        return instance;
}
static final void localInit(a) {
        int p = probeGenerator.addAndGet(PROBE_INCREMENT);
        int probe = (p == 0)?1 : p; // skip 0支那// Notice that the seed value is accumulated using atomic classes for each call, meaning that each call produces a different value **支那longseed = mix64(seeder.getAndAdd(SEEDER_INCREMENT)); ** Thread t = Thread.currentThread(); UNSAFE.putLong(t, SEED, seed); UNSAFE.putInt(t, PROBE, probe); }Copy the code

The current method generates the corresponding seed value for the current thread. Note that the seed value changes each time this method is called. This seed value is used to generate random numbers, such as 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;
}

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

Note the nextSeed method, which, when generating random numbers, changes the “seed” for the UNSAFE thread.

UNSAFE, when generating a global random and performing nextSeed, the child thread does not have a “seed” in its UNSAFE state. The default “seed” should be used, as is the case for all child threads, that is, each child thread generates a value equal to the default “seed “+GAMMA. Executing the current method for each thread produces a “seed” for each thread, and as you can see from the localInit method, each call produces a different “seed” for the correct output.