Fork/Join模式(JSR166y)手記之ThreadLocalRandom
ThreadLocalRandom是一個可以獨立使用的、用于生成隨機數的類。繼承自Random,但性能超過Random,所謂“青出于藍而勝于藍”。其API所提供方法,不多,父類Random具有的,它也一樣具有。從表明看,是一個單例模式,其實不然:
private static final ThreadLocal采用ThreadLocal進行包裝的Random子類,每線程對應一個ThreadLocalRandom實例。測試代碼:localRandom =
new ThreadLocal() {
protected ThreadLocalRandom initialValue() {
return new ThreadLocalRandom();
}
};
ThreadLocalRandom() {
super();
initialized = true;
}
public static ThreadLocalRandom current() {
return localRandom.get();
}
@Test
public void testInstance() {
final ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
final ListrandomList = new ArrayList ();
final Phaser barrier = new Phaser(1);
new Thread() {
@Override
public void run() {
randomList.add(ThreadLocalRandom.current());
barrier.arrive();
}
}.start();
barrier.awaitAdvance(barrier.getPhase());
if (randomList.isEmpty()) {
throw new NullPointerException();
}
Assert.assertTrue(threadLocalRandom != randomList.get(0));
}
這么一包裝,在性能上可以趕超Math.random(),不錯。
再看Math.random(),其生成也是依賴于Random類:
更為詳細的機制研讀,請閱讀參考資料中鏈接。
@Test非規范的性能測試,某次輸出結果:
public void testSpeed() {
final int MAX = 100000;
ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
long start = System.nanoTime();
for (int i = 0; i < MAX; i++) {
threadLocalRandom.nextDouble();
}
long end = System.nanoTime() - start;
System.out.println("use time1 : " + end);
long start2 = System.nanoTime();
for (int i = 0; i < MAX; i++) {
Math.random();
}
long end2 = System.nanoTime() - start2;
System.out.println("use time2 : " + end2);
Assert.assertTrue(end2 > end);
}
use time1 : 3878481性能差別不止兩倍啊,哈哈。
use time2 : 8633080
再看Math.random(),其生成也是依賴于Random類:
private static Random randomNumberGenerator;很奇怪,性能為什么差那么遠呢?可能個各自的next函數不同造成。看一下Random中的next(int bits)方法實現:
private static synchronized void initRNG() {
if (randomNumberGenerator == null)
randomNumberGenerator = new Random();
}
public static double random() {
if (randomNumberGenerator == null) initRNG();
return randomNumberGenerator.nextDouble();
}
protected int next(int bits) {而ThreadLocalRandom的重寫版本為:
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));
}
protected int next(int bits) {相比ThreadLocalRandom的next(int bits)函數實現上更為簡練,不存在seed的CAS操作,并且少了很多的運算量。
rnd = (rnd * multiplier + addend) & mask;
return (int) (rnd >>> (48-bits));
}
更為詳細的機制研讀,請閱讀參考資料中鏈接。
另外,ThreadLocalRandom 也提供了易用的,兩個數字之間的隨機數生成方式。類似于:
nextDouble(double least, double bound)隨機數的生成范圍為 最小值 <= 隨機數 < 最大值??梢园钚≈?,但不包含最大值。
nextInt(int least, int bound)
nextLong(long least, long bound)
@Test
public void testHowtoUse(){
final ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
final int MAX = 100;
int result = threadLocalRandom.nextInt(0, 100);
Assert.assertTrue(MAX > result);
}
嗯,還有,不支持setSeed方法。
曾經JDK 7中,ThreadLocalRandom 存在隨機多個線程隨機數生成相同的bug,但最新版本中,已不存在,被修復了,可以放心使用。從現在開始,完全可以使用ThreadLocalRandom替代Random,尤其是在并發、并行、多任務等環境下,會比在多線程環境下使用公共共享的Random對象實例更為有效。
代碼清單:
posted on 2012-02-04 11:29 nieyong 閱讀(1573) 評論(0) 編輯 收藏 所屬分類: Java