The Beauty of Java Concurrent Programming Chapter 3 Reading Notes

Analysis of the principle of ThreadLocalRandom class in java concurrent package

Is this ilei a new random number generator under the JUC package of JDK7, which makes up for the defects of the Random class under multi-threading

The Random class and its limitations

public class RandomTest {
    public static void main(String[] args) {
        Random random=new Random();
        for (int i=0;i<10;i++){
            System.out.println(random.nextInt(5));
        }
    }
}

Each Random instance has an atomic seed variable to record the current seed value. When the random number to be generated needs to calculate a new seed based on the current seed and update it back to the atomic variable, use a single Random instance to generate random numbers under multi-threading. When counting, when multiple threads calculate random numbers at the same time to calculate a new seed, multiple threads will compete for the update operation of the same atomic variable. Since the update of the atomic variable is a CAS operation, only one thread will succeed at the same time, causing a large number of Threads spin, which reduces concurrency performance

ThreadLocalRandom

public class ThreadLocalRandomTest {
    public static void main(String[] args) {
        ThreadLocalRandom random=ThreadLocalRandom.current();
        for (int i=0;i<10;i++){
            System.out.println(random.nextInt(5));
        }

    }
}

Each thread maintains a seed variable. When each variable generates a random number, it calculates a new seed based on its old seed, and uses the new seed to update the old seed, and then calculates the random number based on the seed. There will be no competition problem. Greatly improved high concurrency performance

Source code analysis

It can be seen from the figure that the ThreadLocalRandom class inherits the Random class and rewrites the nextlnt method. The atomic seed variable inherited from the Random class is not used in the ThreadLocalRandom class. The specific seed is not stored in ThreadLocalRandom, and the specific seed is stored in the specific Inside the threadLocalRandomSeed variable of the calling thread. ThreadLocalRandom is similar to the ThreadLocal class, which is a tool class. When a thread calls the current method of ThreadLocalRandom, ThreadLocalRandom is responsible for the threadLocalRandomSeed variable of the initial calling thread, that is, the initialization seed.

When the nextInt method of ThreadLocalRandom is called, the current thread threadLocalRandomSeed variable is actually obtained as the current seed to calculate the new seed, and then the new seed is updated to the threadLocalRandomSeed variable of the current thread, and then the random number is calculated based on the new seed and using a specific algorithm. It should be noted that the threadLocalRandomSeed variable is an ordinary long variable in the Thread class, and it is not an atomic variable. In fact, the reason is very simple, because this variable is thread-level, so there is no need to use

Atomic variables, if you still don't understand, you can think about the principle of ThreadLocal. The seeder and probeGenerator are two atomic variables. They will be used when initializing the seed and probe variables of the calling thread, and each thread will only be used once.

In addition, the variable instance is an instance of ThreadLocalRandom, which is static. When multiple threads obtain an instance of ThreadLocalRandom through the current method of ThreadLocalRandom, they actually obtain the same instance. But since the specific seed is stored in the thread, the instance of ThreadLocalRandom only contains general algorithms that have nothing to do with the thread, so it is thread-safe

Unsafe mechanism

current() method of ThreadLocalRandom

This method is to obtain the ThreadLocalRandom instance and initialize the ThreadLocalRandomSeed and ThreadLocalRandomProbe variables in the calling thread

static final ThreadLocalRandom instance = new ThreadLocalRandom();
public static ThreadLocalRandom current() {
    if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
        localInit();
    return instance;
}
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);
    }

int 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(){
    Thread t;
    long r;
    UNSAFE.putLong(t=Thread.currentThread(),SEED,r=UNSAFE.getLong(t,SEED)+GAMMA);
    return r; 
}

Summarize

Firstly, it explained the implementation principle of Random and the shortcomings of Random’s need to compete for seed atomic variable update operations under multi-threading, thus introducing the ThreadLocalRandom class, which uses the principle of ThreadLocal, so that each thread holds a local seed variable, the seed variable It will only be initialized when using random numbers. The calculation of new seeds under multi-threading is updated according to the seed variables maintained in their own threads, thus avoiding competition

Guess you like

Origin blog.csdn.net/weixin_60257072/article/details/130513790