ThreadLocal 是一个多线程操作的数据存储类,它可以实现当前线程的数据保存操作,而Random类是一个随机数的操作类
范例:观察一个多线程环境中的Random的使用问题
@Test
public void randomTest() {
Random random = new Random(); // 定义一个随机数的处理类
for (int i = 0; i < 3; i++) {
new Thread(() -> {
for (int x = 0; x < 3; x++) {
System.out.printf(
"【%s】【%d】生成的随机数:%d%n",
Thread.currentThread().getName(),x, random.nextInt(100));
}
}, "随机数生成线程 - " + i).start();
}
}
结果:
【随机数生成线程 - 0】【0】生成的随机数:1
【随机数生成线程 - 0】【1】生成的随机数:1
【随机数生成线程 - 0】【2】生成的随机数:37
【随机数生成线程 - 2】【0】生成的随机数:82
【随机数生成线程 - 1】【0】生成的随机数:2
【随机数生成线程 - 2】【1】生成的随机数:5
【随机数生成线程 - 1】【1】生成的随机数:92
【随机数生成线程 - 2】【2】生成的随机数:66
【随机数生成线程 - 1】【2】生成的随机数:29
此时执行代码没有任何的问题,但是由于这个时候所有的线程都使用的同一个Random,那么最终表现的结果就是共享了同一个随机银子的数据,而这个随机因子用于控制随机数的生成。
观察Random.nextInt() 方法
public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
int r = next(31);
int m = bound - 1;
if ((bound & m) == 0) // i.e., bound is a power of 2
r = (int)((bound * (long)r) >> 31);
else {
for (int u = r;
u - (r = u % bound) + m < 0;
u = next(31))
;
}
return r;
}
观察random.next() 方法
protected int next(int bits) {
long oldseed, nextseed;
// 考虑到了多线程的同步,每次只能生成一个随机银子
AtomicLong seed = this.seed;
do {
// 获取已经存在的随机银子
oldseed = seed.get();
// 计算下一个随机银子
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
观察以上代码,所有的线程同时共享了同一个随机因子,如果每一个线程都独享自己的随机银子,所以就要使用ThreadLocalRandom 类来实现。
使用ThreadLocalRandom处理
@Test
public void threadLocalRandomTest(){
for (int i = 0; i < 3; i++) {
new Thread(() -> {
for (int x = 0; x < 3; x++) {
System.out.printf(
"【%s】【%d】生成的随机数:%d%n",
Thread.currentThread().getName(),
x,
ThreadLocalRandom.current().nextInt(100));
}
}, "随机数生成线程 - " + i).start();
}
}
相比较Random操作来讲,它可以针对于每一个不同的线程保存各自的因子,从而实现准确的随机数生成。