Welcome to github.com/hsfxuebao/j… , hope to help you, if you feel ok, please click on the Star

From: www.jianshu.com/p/89dfe9902…

preface

When learning ThreadLocalRandom, I encountered some doubts. Why does ThreadLocalRandom generate the same random number in multiple threads?

After reading the source code finally a little understanding of some of its operation mechanism, summed up its correct use in multi-threading, hereby record.

The usefulness of ThreadLocalRandom

In multithreading, it is thread-safe to use instances generated by java.util.Random to generate Random numbers. However, digging into the implementation process of Random will find that multiple threads will compete for the same seed, resulting in performance degradation.

The reasons are as follows:

Random Generating a new Random number requires two steps:

  • According to the oldseedGenerate a newseed
  • By the newseedCompute new random numbers

The algorithm for the second step is fixed. If each thread concurrently obtains the same seed, the 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.

ThreadLocalRandom generates the same random number in multiple threads.

ThreadLocalRandom Generates the same random number in multiple threads

Take a look at sample code that generates the same random number:

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++) { new Player().start(); } } private static class Player extends Thread { @Override public void run() { System.out.println(getName() + ": " + RANDOM.nextInt(100)); }}}Copy the code

Running the code results in the following:

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

To do this, I read the source code for ThreadLocalRandom and found a clue.

First, the static current() method:

Public static ThreadLocalRandom current() {// If the thread calls current() for the first time, Unsafe.getint (thread.currentThread (), PROBE) == 0) localInit(); return instance; }Copy the code

In the localInit() initialization method, the seed is initialized for the thread and stored in UNSAFE, where the UNSAFE method is native and I don’t know much about it, but it doesn’t matter. You can think of this as initializing the seed, storing the thread and seed as key-value pairs.

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);
}
Copy the code

When generating random numbers, call nextInt() :

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; }Copy the code

Focus on the first nextSeed() method:

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;
}
Copy the code

Okay, here’s the problem! The value returned here is r = UNSAFE. GetLong (t, SEED) + GAMMA, taken from UNSAFE. But the question is, are we getting the right values here? Or can I get it out?

Going back to the sample code, on the main thread, we call the current() method of TreadLocalRandom, which stores the main thread and its seed into UNSAFE.

Next, we call nextInt() on the non-main thread, but the key-value pairs of the non-main thread and seed are not stored before UNSAFE. Even though UNSAFE, we take the seed value from the non-main thread. Although I don’t know what the seed value is, it is definitely not the desired result in multithreading, which results in the random number being repeated in multiple threads.

So how do you use ThreadLocalRandom correctly in multiple threads?

Correct use of ThreadLocalRandom in multithreading

Combining the above analysis, the proper use of ThreadLocalRandom, must need to give each thread initialization of a seed, then you need to call ThreadLocalRandom. The current () method.

So there is a question that in each thread calls ThreadLocalRandom. The current (), will produce multiple ThreadLocalRandom instance?

No, see source code:

/** The common ThreadLocalRandom */ static final ThreadLocalRandom instance = new ThreadLocalRandom(); /** * Returns the current thread's {@code ThreadLocalRandom}. * * @return the current thread's {@code ThreadLocalRandom}  */ public static ThreadLocalRandom current() { if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0) localInit(); return instance; }Copy the code

Feel free to use it.

So the sample code changes as follows:

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

Run it to get the desired result:

Thread-0: 90
Thread-3: 77
Thread-2: 97
Thread-5: 96
Thread-4: 42
Thread-1: 3
Thread-6: 4
Thread-7: 6
Thread-8: 52
Thread-9: 39
Copy the code

To summarize, in multithreaded ThreadLocalRandom generate random Numbers, use ThreadLocalRandom directly. The current (). XXXX