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

瘦風發表於2021-12-14
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
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

相關文章