Java如何生成隨機數 - Random、ThreadLocalRandom、SecureRandom

Java7 的Random僞隨機數和線程安全的ThreadLocalRandom
一、Random僞隨機數:

Random 類專門用於生成一個僞隨機數,它有兩個構造器: 一個構造器使用默認的種子(以當前時間作爲種子) ,另 個構造器需要程序員顯式傳入一個 long 整數的種子.
當使用默認的種子或傳入相同的種子構造 Random 對象時,它們屬於同一個種子,只要兩個 Random 對象的種子相同,而且方法的調用順序也相同,它們就會產生相同的數字序列 也就是說, Random 產生的數字並不是真正隨機的,而是一種僞隨機。

![](https://img2020.cnblogs.com/blog/1438655/202102/1438655-20210208160430455-983724190.png)


常用解決方案:

爲了避免兩個 Random 對象產生相同的數字序列,通常推薦使用當前時間作爲 Random 對象的種子:

Random rand = new Random(System.currentTimeMi11is());

二、ThreadLocalRandom

ThreadLocalRandom 類是 Java 新增的 ,它 Random 的增強版 在併發訪 問的環境下,使用ThreadLocalRandom 來代替 Random 可以減少多線程資源競爭,最終保證系統具有更好的線程安全性。

多線程環境下使用 ThreadLocalRandom 的方式與使用 Random 基本一樣,示例如下:

ThreadLocalRandom random = ThreadLocalRandom.current();
// 生成一個 3-10 之間(包括3,不包括10)的僞隨機整數
int num1 = random.nextInt(3, 10);
// 生成一個 2.0-10.0 之間的僞隨機浮點數
int num2 = random.nextDouble(2.0, 10.0);
Random即:java.util.Random,
ThreadLocalRandom 即:java.util.concurrent.ThreadLocalRandom
SecureRandom即:java.security.SecureRandom


Q:Random是不是線程安全的?
A:Random是線程安全的,但是多線程下可能性能比較低。
參考:
http://docs.oracle.com/javase/7/docs/api/java/util/Random.html
http://stackoverflow.com/questions/5819638/is-random-class-thread-safe

Q:ThreadLocalRandom爲什麼這麼快?
A:其實這個看下源碼就知道了。。因爲Random用了很多CAS的類,ThreadLocalRandom根本沒有用到。

Q:爲什麼在高強度要求的情況下,不要用Random?
A:特別是在生成驗證碼的情況下,不要使用Random,因爲它是線性可預測的。記得有個新聞說的是一個賭博網站,爲了說明其公平,公開的它的源代碼,結果因爲隨機數可預測漏洞被攻擊了。所以在安全性要求比較高的場合,應當使用SecureRandom。

update 2014-4-22:  http://news.cnblogs.com/n/206074/

參考:http://www.inbreak.net/archives/349

Q:從理論上來說計算機產生的隨機數都是僞隨機數,那麼如何產生高強度的隨機數?
A:產生高強度的隨機數,有兩個重要的因素:種子和算法。當然算法是可以有很多的,但是如何選擇種子是非常關鍵的因素。如Random,它的種子是System.currentTimeMillis(),所以它的隨機數都是可預測的。那麼如何得到一個近似隨機的種子?這裏有一個很別緻的思路:收集計算機的各種信息,如鍵盤輸入時間,CPU時鐘,內存使用狀態,硬盤空閒空間,IO延時,進程數量,線程數量等信息,來得到一個近似隨機的種子。這樣的話,除了理論上有破解的可能,實際上基本沒有被破解的可能。而事實上,現在的高強度的隨機數生成器都是這樣實現的。
比如Windows下的隨機數生成器:
http://blogs.msdn.com/b/michael_howard/archive/2005/01/14/353379.aspx
http://msdn.microsoft.com/en-us/library/aa379942%28VS.85%29.aspx
Linux下的 /dev/random:
http://zh.wikipedia.org/wiki//dev/random
據SecureRandom的Java doc,說到在類unix系統下,有可能是利用 /dev/random,來實現的。


其它的一些有意思的東東:
最快的安全性要求不高的生成UUID的方法(注意,強度不高,有可能會重複):
new UUID(ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong());
在一個網站上看到的,忘記出處了。


隨機生成產生隨機數的函數?
是否可以利用一個隨機數生成器來生成一系列的隨機代碼,然後作爲一個新的隨機數生成器?貌似強度是傳遞的,似乎沒意義。

綜述
生成隨機數是很常見的任務。 這也是 JAVA 提供 Random 的原因。但是它在多線程環境中性能並不高。

簡單來說,Random 之所以在多線程環境中性能不高的原因是多個線程共享同一個 Random 實例並進行爭奪。

爲了解決這個限制,JAVA 在 JDK 7 中引入了 ThreadLocalRandom 類,用於在多線程環境下生產隨機數。

ThreadLocalRandom 強於 Random
ThreadLocalRandom 結合了 Random 和 ThreadLocal 類,並被隔離在當前線程中。因此它通過避免任何對 Random 對象的併發訪問,從而在多線程環境中實現了更好的性能。

一個線程獲取到的隨機數不受另一個線程影響,而 Random 提供全局的隨機數。

另外,不同於 Random, ThreadLocalRandom 明確的不支持設置隨機種子。 它重寫了 Random 的
setSeed(long seed) 方法並直接拋出了 UnsupportedOperationException 異常。

現在讓我們來看看幾種生產隨機 int、long、double 的方式。

使用 ThreadLocalRandom 生產隨機數
根據 Oracle 的文檔,我們只需要調用 ThreadLocalRandom.current() 方法,它就會返回當前線程的 ThreadLocalRandom 的實例。
然後我們就可以調用這個實例的方法獲取隨機數

讓我們生成一個沒有任何限制的 int 值

int unboundedRandomValue = ThreadLocalRandom.current().nextInt());
現在再生成一個有界的 int, 即介於兩個數之間。

這是一個生成 0 ~ 100 的 int 值的例子

int boundedRandomValue = ThreadLocalRandom.current().nextInt(0, 100);
請注意:0 是包含在界限內, 而 100 是不在範圍內的。

我們可以使用與示例中相似的方式,調用 nextLong() 和 nextDouble() 方法來生產 long 和 double 值。

JAVA 8 還添加了一個 nextGaussian() 方法來生成正態分佈的值,與生成器序列生成的值偏差 0.0 ~ 1.0 。

和 Random 類一樣,我們可以使用 doubles(), ints(), longs()方法來生成隨機數流。

作者:yangc91
鏈接:https://www.jianshu.com/p/29ae27e401d1
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章