我们都知道,JDK 自带一个 Random 类,可以 生成随机数。但是 ,这个类有个问题,多线程情况下,可能产生相同的随机数。代码如下:
protected int next(int bits) {
long oldseed, nextseed;
// 多线程时,使用了相同的 seed,那么就会产生相同的随机数
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
// 这里,多线程,当seed相同时,只会有一个 线程更新成功
// 其他线程 更新失败会,会再次去生成一个随机数
// 直到成功为止, cas自旋 虽然解决了问题,但 会消耗cpu资源,性能也底下
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
于是,有一个新的类 ThreadLocalRandom
public static ThreadLocalRandom current() {
if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
localInit(); // 给当前线程 生成自己的 seed
return instance;
}
// 使用当前线程的 seed,获取新的 seed
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;
}
//实例代码
/**
这段代码好像不对啊,根据上面的 源码来看,这里生成的每个 线程 用的 seed 都是 主线程的啊
另外 ThreadLocalRandom 是单例的,current() 永远只能获取到一个 实例对象
*/
Random random = ThreadLocalRandom.current();
for (int i = 0; i < 5; ++i) {
new Thread(() -> {
System.out.println(Thread.currentThread() + " "+random.nextInt(5));
}).start();
}
/**
正确使用 ThreadLocalRandom
*/
for (int i = 0; i < 5; ++i) {
new Thread(() -> {
// 每个线程 自己去 调用 current() 来获取 ThreadLocalRandom 才对
Random random = ThreadLocalRandom.current();
System.out.println(Thread.currentThread() + " "+random.nextInt(5));
}).start();
}