ThreadLocalRandom - 为多线程而生

今天要说的 ThreadLocalRandom 是Java7引入的随机数生成器, 在此之前我们生成随机数都是使用

  1. 创建java.util.Random实例
  2. 使用Math.random(), 其实内部也是创建了一个java.util.Random

使用Random没有问题, 其seed使用AtomicLong来存储, 在多线程下也是线程安全的.
但是, 如果在多线程下使用Random, 会导致多线程的竞争开销, 也会降低Random的随机性, 于是便有了ThreadLocalRandom.

ThreadLocalRandom


那么, 对于前面的问题, ThreadLocalRandom对这些做了哪些工作呢 ?

假设使用以下代码获取随机数 :

int randomInt = ThreadLocalRandom.current().nextInt();

ThreadLocalRandom.current():


我们来看一下ThreadLocalRandom的内部实现:

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
long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
Thread t = Thread.currentThread();
UNSAFE.putLong(t, SEED, seed);
UNSAFE.putInt(t, PROBE, probe);
}

其中, SEEDPROBE可以参考下面定义:

Class<?> tk = Thread.class;
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));

Thread.java

@sun.misc.Contended("tlr")
int threadLocalRandomProbe;

UNSAFE.getInt(Thread.currentThread(), PROBE)就是获取Thread对象中offset偏移地址对应的field threadLocalRandomProbe的值, threadLocalRandomProbenonzero if threadLocalRandomSeed initialized.
所以, 如果PROBE值为零, 将进行localInit(), 当前Thread对象的threadLocalRandomSeed 和 threadLocalRandomProbe将会被赋值. (`@sun.misc.Contended("tlr")`是为了解决伪共享的问题, 将另起一篇博客介绍)

nextInt()


public int nextInt() {
return mix32(nextSeed());
}

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是field的偏移量
GAMMA是seed的increment
这里会更新当前Thread的SEED, 最终mix32()生成int随机数.

最后


从上可知ThreadLocalRandom的工作方式, 它为每个线程分配了一个SEED(被Thread对象持有), 故而减少了多线程下的竞争开销, 也使随机性更加好.

参考


http://www.jdon.com/concurrent/concurrent-random.html