【譯文】,作者:baeldung, 原文鏈接:https://www.baeldung.com/java-thread-local-random
譯文地址:github, 如有問題,歡迎指正。
綜述
生成隨機數(shù)是很常見的任務。 這也是 JAVA 提供 Random 的原因。但是它在多線程環(huán)境中性能并不高。
簡單來說,Random 之所以在多線程環(huán)境中性能不高的原因是多個線程共享同一個 Random 實例并進行爭奪。
為了解決這個限制,JAVA 在 JDK 7 中引入了 ThreadLocalRandom 類,用于在多線程環(huán)境下生產隨機數(shù)。
ThreadLocalRandom 強于 Random
ThreadLocalRandom 結合了 Random 和 ThreadLocal 類,并被隔離在當前線程中。因此它通過避免任何對 Random 對象的并發(fā)訪問,從而在多線程環(huán)境中實現(xiàn)了更好的性能。
一個線程獲取到的隨機數(shù)不受另一個線程影響,而 Random 提供全局的隨機數(shù)。
另外,不同于 Random, ThreadLocalRandom 明確的不支持設置隨機種子。 它重寫了 Random 的
setSeed(long seed) 方法并直接拋出了 UnsupportedOperationException 異常。
現(xiàn)在讓我們來看看幾種生產隨機 int、long、double 的方式。
使用 ThreadLocalRandom 生產隨機數(shù)
根據(jù) Oracle 的文檔,我們只需要調用 ThreadLocalRandom.current() 方法,它就會返回當前線程的 ThreadLocalRandom 的實例。
然后我們就可以調用這個實例的方法獲取隨機數(shù)
讓我們生成一個沒有任何限制的 int 值
int unboundedRandomValue = ThreadLocalRandom.current().nextInt());
現(xiàn)在再生成一個有界的 int, 即介于兩個數(shù)之間。
這是一個生成 0 ~ 100 的 int 值的例子
int boundedRandomValue = ThreadLocalRandom.current().nextInt(0, 100);
請注意:0 是包含在界限內, 而 100 是不在范圍內的。
我們可以使用與示例中相似的方式,調用 nextLong() 和 nextDouble() 方法來生產 long 和 double 值。
JAVA 8 還添加了一個 nextGaussian() 方法來生成正態(tài)分布的值,與生成器序列生成的值偏差 0.0 ~ 1.0 。
和 Random 類一樣,我們可以使用 doubles(), ints(), longs()方法來生成隨機數(shù)流。
使用 JMH 比較
讓我們來看看怎樣在多線程環(huán)境下使用這兩個類來獲取隨機數(shù),并使用 JMH 比較性能。
首先, 讓我們創(chuàng)建一個多個線程共享一個 Random 實例的例子。
我們提交使用 Random 實例生成隨機數(shù)的任務至 ExecutorService 中:
ExecutorService executor = Executors.newWorkStealingPool();
List<Callable<Integer>> callables = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 1000; i++) {
callables.add(() -> {
return random.nextInt();
});
}
executor.invokeAll(callables);
接下來使用 JMH benchmarking 來檢測上述代碼的性能:
# Run complete. Total time: 00:00:36
Benchmark Mode Cnt Score Error Units
ThreadLocalRandomBenchMarker.randomValuesUsingRandom avgt 20 771.613 ± 222.220 us/op
相似的,現(xiàn)在讓我們使用 ThreadLocalRandom 代替 Random
ExecutorService executor = Executors.newWorkStealingPool();
List<Callable<Integer>> callables = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
callables.add(() -> {
return ThreadLocalRandom.current().nextInt();
});
}
executor.invokeAll(callables);
下面是 ThreadLocalRandom 的測試結果:
# Run complete. Total time: 00:00:36
Benchmark Mode Cnt Score Error Units
ThreadLocalRandomBenchMarker.randomValuesUsingThreadLocalRandom avgt 20 624.911 ± 113.268 us/op
最后比較上面的測試結果, 我們可以清晰的看到生成 1000 個隨機數(shù),Random 耗時 772 毫秒, 而 ThreadLocalRandom 耗時 625 毫秒。
因此,我們可以得出 ThreadLocalRandom 在高并發(fā)環(huán)境下更有效率
為了學習 JMH, 可以參考之前的文章
結論
本文講述了 Random 和 ThreadLocalRandom 之間的區(qū)別。
我們也看到了在 ThreadLocalRandom 比 ThreadLocalRandom 在多線程環(huán)境下的優(yōu)勢和性能,以及如何使用等。
ThreadLocalRandom 是 JDK 的一個簡單補充,但是能在高并發(fā)應用中產生顯著的影響。
然后,跟往常一樣,所有的示例都可以在 GitHub project 中看到。