Welcome to the public number [sharedCode] committed to mainstream middleware source code analysis, personal website: www.shared-code.com/
ThreadLocalRandom introduction
ThreadLocalRandom is a random number generation utility class released after JDK1.7 that performs better than traditional math.random ().
Performance comparison
ThreadLocalRandom vs. Math.random()
public class Test {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(10);
long startTime = System.currentTimeMillis();
Math.random() opens 10 threads
for (int i = 0; i < 10 ; i++){
Thread thread = new Thread(() -> {
// Loop 10000 times
for (int j = 0; j < 10000 ; j++) {
int x = (int) Math.random();
}
countDownLatch.countDown();
});
thread.start();
}
countDownLatch.await();
System.out.println("Math.random() time =" + (System.currentTimeMillis()-startTime));
long startTimeLocal = System.currentTimeMillis();
// ThreadLocalRandom starts 10 threads
for (int i = 0; i < 10 ; i++){
Thread thread = new Thread(() -> {
for (int j = 0; j < 10000 ; j++) {
intx = ThreadLocalRandom.current().nextInt(); }}); thread.start(); } System.out.println("ThreadLocalRandom time ="+ (System.currentTimeMillis()-startTimeLocal)); }}Copy the code
The test results are as follows:
First run: math.random () Time =112ThreadLocalRandom time-consuming =7Second run: math.random () Time =115ThreadLocalRandom time-consuming =15Third run: math.random () Time =94ThreadLocalRandom time-consuming =9Fourth run: math.random () Time =126ThreadLocalRandom time-consuming =16
Copy the code
From the summary above, it is clear that ThreadLocalRandom performs 8-10 times better
Source code analysis
public static ThreadLocalRandom current(a) {
// Calls the unsafe class using this method to retrieve the offset value from the current object
if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
// If the value is 0, the current thread is not initialized.
localInit();
return instance;
}
/** * This method basically puts the current thread, the seed object, into the MAP structure, */
static final void localInit(a) {
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
So let’s look at the nextInt method.
public int nextInt(a) {
return mix32(nextSeed());
}
/** * From the current thread, take the seed and return it for random number calculation */
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
A core parameter of random number is seed value. Seed is the seed in the random algorithm. By multiplying this seed with an unknown number “0xff51afd7ed558ccdL”, an unknown number is obtained.
# # # Math. The Random implementation
protected int next(int bits) {
long oldseed, nextseed;
// Multiple threads get the same seed object
AtomicLong seed = this.seed;
do {
/ / the current value
oldseed = seed.get();
// The next value is calculated by some sort of calculation
nextseed = (oldseed * multiplier + addend) & mask;
// Make sure the update is correct by CAS operation
} while(! seed.compareAndSet(oldseed, nextseed));return (int)(nextseed >>> (48 - bits));
}
Copy the code
As you can clearly see from the above, the original implementation was for all threads to share a seed object. Although CAS was used to update the seed object, the loop in do while was still retry, which was very expensive in high concurrency scenarios.
Conclusion:
ThreadLocalRandom gets its own seed object from each thread. There is no competition between threads for resources, so the speed is fast.