先看代码:
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(){
System.out.printf("当前线程%s生成的数字:%d\n",Thread.currentThread().getId(),random.nextInt(100));
}
public static void getCurrentThreadSeek(){
System.out.printf("当前线程%s生成的数字:%d\n",Thread.currentThread().getId(),ThreadLocalRandom.current().nextInt(100));
}
public static void main(String[] args) {
System.out.println("错误方式:当线程共用同一个ThreadLocalRandom时");
for (int i = 0; i < 10; i++){
new Thread(() -> withoutGetCurrentThreadSeek()).start();
}
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
System.out.println("正确方式:当每个线程拥有自己的ThreadLocalRandom时");
for (int i = 0; i < 10; i++){
new Thread(() -> getCurrentThreadSeek()).start();
}
}
}
复制代码
打印的结果:
可以看到错误方式,多线程生成的随机数是相同的,这个不是我们想要的结果,那错在哪里呢?
关键在于ThreadLocalRandom.current()
:
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
**//注意这个seed值,每次调用,都会使用原子类累加,也就是每次调用都会产生一个不同的值**
**long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));**
Thread t = Thread.currentThread();
UNSAFE.putLong(t, SEED, seed);
UNSAFE.putInt(t, PROBE, probe);
}
复制代码
对current这个方法的总结,就是为当前线程生成对应的seed(种子)值,注意这个”种子”的值,每次调用这个方法,都会变化,这个”种子”值在生成随机数的时候用到,如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; // read and update per-thread seed
UNSAFE.putLong(t = Thread.currentThread(), SEED,
r = UNSAFE.getLong(t, SEED) + GAMMA);
return r;
}
复制代码
注意这个nextSeed
方法,生成随机数的时候,就是修改UNSAFE中该线程对应的”种子”。
当我们生成一个全局的random
的时候,执行到nextSeed的时候,子线程在UNSAFE里面没有对应的”种子”,这个时候应该用默认”种子”,所有的子线程都是如此,也就是每个子线程生成的值都等于默认"种子"+GAMMA
,但如果用正确方法,每个线程都执行current方法,就会给每个线程产生一个”种子”,而且从localInit
方法中可以看出,每次调用都会生成不同的”种子”,所以才能正确输出。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END