新聞中心
?前言
大家項(xiàng)目中如果有生成隨機(jī)數(shù)的需求,我想大多都會(huì)選擇使用Random來實(shí)現(xiàn),它內(nèi)部使用了CAS來實(shí)現(xiàn)。實(shí)際上,JDK1.7之后,提供了另外一個(gè)生成隨機(jī)數(shù)的類ThreadLocalRandom,那么他們二者之間的性能是怎么樣的呢?

站在用戶的角度思考問題,與客戶深入溝通,找到衢州網(wǎng)站設(shè)計(jì)與衢州網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都網(wǎng)站制作、成都做網(wǎng)站、外貿(mào)營銷網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、空間域名、網(wǎng)頁空間、企業(yè)郵箱。業(yè)務(wù)覆蓋衢州地區(qū)。
Random的使用
Random類是JDK提供的生成隨機(jī)數(shù)的類, 這個(gè)類不是隨機(jī)的,而是偽隨機(jī)的。什么是偽隨機(jī)呢?偽隨機(jī)是指生成的隨機(jī)數(shù)是有一定規(guī)律的,這個(gè)規(guī)律出現(xiàn)的周期因偽隨機(jī)算法的優(yōu)劣而異。一般來說,周期比較長,但可以預(yù)見。我們可以通過以下代碼簡單地使用 Random:
Random中有很多方法。這里我們就分析比較常見的nextInt()和nextInt(int bound)方法。
- nextInt()會(huì)計(jì)算int范圍內(nèi)的隨機(jī)數(shù),
- nextInt(int bound)會(huì)計(jì)算[0,bound) 之間的隨機(jī)數(shù),左閉右開。
實(shí)現(xiàn)原理
Random類的構(gòu)造函數(shù)如下圖所示:
可以看到在構(gòu)造方法中,根據(jù)當(dāng)前時(shí)間seed生成了一個(gè)AtomicLong類型的seed。
public int nextInt() {
return next(32);
}這里面直接調(diào)用了next()方法,傳入了32,這里的32是指Int的位數(shù)。
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));
}這里會(huì)根據(jù)seed的當(dāng)前值,通過一定的規(guī)則(偽隨機(jī))計(jì)算出下一個(gè)seed,然后進(jìn)行CAS。如果CAS失敗,繼續(xù)循環(huán)上述操作。最后根據(jù)我們需要的位數(shù)返回。
小結(jié):可以看出在next(int bits)?方法中,對(duì)AtomicLong進(jìn)行了CAS操作,如果失敗則循環(huán)重試。很多人一看到CAS,因?yàn)椴恍枰渔i,第一時(shí)間就想到了高性能、高并發(fā)。但是在這里,卻成為了我們多線程并發(fā)性能的瓶頸??梢韵胂螅?dāng)我們有多個(gè)線程執(zhí)行CAS時(shí),只有一個(gè)線程一定會(huì)失敗,其他的會(huì)繼續(xù)循環(huán)執(zhí)行CAS操作。當(dāng)并發(fā)線程較多時(shí),性能就會(huì)下降。
ThreadLocalRandom的使用
JDK1.7之后,提供了一個(gè)新類ThreadLocalRandom?來替代Random。
實(shí)現(xiàn)原理
我們先來看下current()方法。
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);
}如果沒有初始化,先進(jìn)行初始化,這里我們的seed不再是全局變量了。我們的線程中有三個(gè)變量:
/** The current seed for a ThreadLocalRandom */
@sun.misc.Contended("tlr")
long threadLocalRandomSeed;
/** Probe hash value; nonzero if threadLocalRandomSeed initialized */
@sun.misc.Contended("tlr")
int threadLocalRandomProbe;
/** Secondary seed isolated from public ThreadLocalRandom sequence */
@sun.misc.Contended("tlr")
int threadLocalRandomSecondarySeed;
- threadLocalRandomSeed:這是我們用來控制隨機(jī)數(shù)的種子。
- threadLocalRandomProbe:這個(gè)就是ThreadLocalRandom,用來控制初始化。
- threadLocalRandomSecondarySeed:這是二級(jí)種子。
關(guān)鍵代碼如下:
UNSAFE.putLong(t = Thread.currentThread(), SEED,r=UNSAFE.getLong(t, SEED) + GAMMA);
可以看出,由于每個(gè)線程都維護(hù)自己的seed?,所以此時(shí)不需要CAS?,直接進(jìn)行put。這里通過線程間的隔離來減少并發(fā)沖突,所以ThreadLocalRandom的性能非常高。
性能對(duì)比
通過基準(zhǔn)工具JMH測(cè)試:
@BenchmarkMode({Mode.AverageTime})
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iteratinotallow=3, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iteratinotallow=3,time = 5)
@Threads(4)
@Fork(1)
@State(Scope.Benchmark)
public class Myclass {
Random random = new Random();
ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
@Benchmark
public int measureRandom(){
return random.nextInt();
}
@Benchmark
public int threadLocalmeasureRandom(){
return threadLocalRandom.nextInt();
}
}運(yùn)行結(jié)果如下圖所示,最左邊是并發(fā)線程的數(shù)量:
顯而易見,無論線程數(shù)量是多少,ThreadLocalRandom?性能是遠(yuǎn)高于Random。
總結(jié)
本文講解了JDK中提供的兩種生成隨機(jī)數(shù)的方式,一個(gè)是JDK 1.0引入的Random?類,另外一個(gè)是JDK1.7引入的ThreadLocalRandom?類,由于底層的實(shí)現(xiàn)機(jī)制不同,ThreadLocalRandom?的性能是遠(yuǎn)高于Random?,建議后面大家在技術(shù)選型的時(shí)候優(yōu)先使用ThreadLocalRandom。
新聞標(biāo)題:Java中生成隨機(jī)數(shù)RandomVSThreadLocalRandom性能比較
本文鏈接:http://www.5511xx.com/article/ccehepg.html


咨詢
建站咨詢
