Random 的線程安全實(shí)現(xiàn)方式
生成隨機(jī)數(shù)大致需要兩個(gè)步驟:
- 首先用老的種子生成一個(gè)新的種子。
- 然后用新的種子次绘,計(jì)算生成隨機(jī)數(shù)邮偎。
因?yàn)榈诙剿惴ㄊ枪潭ǖ暮探嗤姆N子生成相同的隨機(jī)數(shù)泻云。在多線程環(huán)境下狐蜕,有可能有多個(gè)線程都拿同一個(gè)老種子去生成隨機(jī)數(shù)婆瓜,產(chǎn)生相同的值。
要想實(shí)現(xiàn) Random 的線程安全廉白,需要保證多個(gè)線程用同一老種子生成新種子時(shí)猴蹂,如果有一個(gè)線程先生成了楣嘁,那么其他線程需要丟棄老種子晕讲,用第一個(gè)線程生成的新種子來計(jì)算自己的新種子,一次類推马澈。
原生的 Random 類是通過原子變量 + CAS 算法 + 自旋實(shí)現(xiàn)的瓢省。
// Random.java 部分代碼
public
class Random implements java.io.Serializable {
// 原子的種子變量
private final AtomicLong seed;
// 構(gòu)造方法
public Random(long seed) {
if (getClass() == Random.class)
this.seed = new AtomicLong(initialScramble(seed));
else {
// subclass might have overriden setSeed
this.seed = new AtomicLong();
setSeed(seed);
}
}
// 生成隨機(jī)數(shù)
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
// 使用 CAS + 自旋更新種子
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
}
上面的的實(shí)現(xiàn),存在一個(gè)性能問題痊班,由于種子是的更新是通過 CAS 算法實(shí)現(xiàn)的勤婚,多個(gè)線程同時(shí)更新只會(huì)有一個(gè)成功,其他線程會(huì)重試涤伐,如果并發(fā)量大的話馒胆,會(huì)有大量線程自旋重試缨称。
ThreadLocalRandom 的線程安全實(shí)現(xiàn)方式
Random 的問題在于更新原子變量的競(jìng)爭導(dǎo)致了多線程高并發(fā)情況的的性能缺陷。ThreadLocalRandom 通過讓每個(gè)線程都持有一個(gè)種子祝迂,因?yàn)榫€程和種子是一一對(duì)應(yīng)的睦尽,就不用對(duì)更新種子的操作進(jìn)行同步,也就不會(huì)有競(jìng)爭問題了。這樣大大提高了并發(fā)性能。
public class ThreadLocalRandom extends Random {
private static final AtomicInteger probeGenerator = new AtomicInteger();
private static final AtomicLong seeder = new AtomicLong(initialSeed());
// 種子是在是存放在線程中的朴则,ThreadLocalRandom 的實(shí)例只有算法,所以即使是同一個(gè)實(shí)例也沒問題
static final ThreadLocalRandom instance = new ThreadLocalRandom();
private ThreadLocalRandom() {
initialized = true; // false during super() call
}
// current 方法獲取的其實(shí)都是同一個(gè)實(shí)例
public static ThreadLocalRandom current() {
// 第一次調(diào)用坐榆,初始化種子
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);
}
// 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);
}
}
// 獲取挂绰,并更新種子
final long nextSeed() {
Thread t; long r; // read and update per-thread seed
// 先用 UNSAFE.getLong(t, SEED) 獲取當(dāng)前線程中的 seed
// 然后再用 seed + GAMMA 作為新種子
// 再使用 UNSAFE.putLong() 更新
UNSAFE.putLong(t = Thread.currentThread(), SEED,
r = UNSAFE.getLong(t, SEED) + GAMMA);
return r;
}
}