ThreadLocalRandom 随机数生成器
ThreadLocalRandom 来自 JDK 7,是一个随机数生成器。
Random 类的局限
Random random = new Random();
for (int i = 0; i < 10; i++) {
System.out.println(random.nextInt(5));//[0,5)
}
查看 java.util.Random#nextInt(int)
源码
public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
//根据老种子计算新种子
int r = next(31);
// 计算新种子计算随机数
return r;
}
java.util.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));//CAS 自旋,某时刻只有一个线程成功
return (int)(nextseed >>> (48 - bits));
}
所以,传统 Random 类的实现会产生多线程下的自旋重试,降低并发性能。
ThreadLocalRandom 的诞生
使用示例
ThreadLocalRandom random = ThreadLocalRandom.current();
for (int i = 0; i < 10; i++) {
System.out.println(random.nextInt(5));
}
Random 的局限在于:多个线程竞争同一个原子性变量(种子)。
ThreadLocalRandom:每个线程都维护一个自身的本地种子,避免竞争。
ThreadLocalRandom 源码解读
public class ThreadLocalRandom extends Random
ThreadLocalRandom 继承自 Random 类,并重写部分 nextInt() 方法。
ThreadLocalRandom 的种子放于具体的调用线程 Thread 的 ThreadLocalRandomSeed 变量中。这点跟 ThreadLocal 的原理很类似。
当调用 ThreadLocalRandom.current() 时,初始化调用线程的 ThreadLocalRandomSeed 变量。
java.lang.Thread#threadLocalRandomSeed
/** The current seed for a ThreadLocalRandom */
@sun.misc.Contended("tlr")
long threadLocalRandomSeed;
threadLocalRandomSeed 只是一个普通的 long 变量,因为已经是线程级别,不需要考虑原子化。
java.util.concurrent.ThreadLocalRandom#seeder
/**
* The next seed for default constructors.
*/
private static final AtomicLong seeder = new AtomicLong(initialSeed());
java.util.concurrent.ThreadLocalRandom#probeGenerator
/** Generates per-thread initialization/probe field */
private static final AtomicInteger probeGenerator = new AtomicInteger();
seeder 和 probeGenerator 都是原子性变量,表示种子和探针生成器。
java.util.concurrent.ThreadLocalRandom#instance
/** The common ThreadLocalRandom */
static final ThreadLocalRandom instance = new ThreadLocalRandom();
instance 是 ThreadLocalRandom 一个静态实例,内部实现只调用了一些它的方法。
Unsafe
java.util.concurrent.ThreadLocalRandom#UNSAFE
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
} catch (Exception e) {
throw new Error(e);
}
}
static 代码块的逻辑是:利用反射机制获取 Thread 类中某些变量 threadLocalRandomXXX 的偏移量。
ThreadLocalRandom current()
public static ThreadLocalRandom current() {
if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)//1
localInit();//2
return instance;//3
}
- 判断当前线程中 PROBE 的值是否为 0
- 如果 PROBE 的值为 0,初始化当前线程的种子变量
- 如果 PROBE 的值不为 0,直接返回 instance 实例(ThreadLocalRandom 一个静态实例)。
java.util.concurrent.ThreadLocalRandom#localInit
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);
}
根据 probe 计算 seed,然后使用 UNSAFE.put 方法设置到当前线程。
java.util.concurrent.ThreadLocalRandom#nextInt(int)
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;
}
这个方法和 Random 中的实现很类似。
java.util.concurrent.ThreadLocalRandom#nextSeed
private static final long GAMMA = 0x9e3779b97f4a7c15L;
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;
}
r = UNSAFE.getLong(t, SEED) + GAMMA
获取旧种子,加上 GAMMA 作为新种子。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: MyBatis 动态 SQL
下一篇: 谈谈自己对于 AOP 的了解
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论