1. 程式人生 > 其它 >Java生成隨機數的4種方式

Java生成隨機數的4種方式

Random

Random 類誕生於 JDK 1.0,它產生的隨機數是偽隨機數,也就是有規則的隨機數。Random 使用的隨機演算法為 linear congruential pseudorandom number generator (LGC) 線性同餘法偽隨機數。在隨機數生成時,隨機演算法的起源數字稱為種子數(seed),在種子數的基礎上進行一定的變換,從而產生需要的隨機數字。

Random 物件在種子數相同的情況下,相同次數生成的隨機數是相同的。比如兩個種子數相同的 Random 物件,第一次生成的隨機數字完全相同,第二次生成的隨機數字也完全相同。預設情況下 new Random() 使用的是當前納秒時間作為種子數的。

// 生成 Random 物件

Random random = new Random();

for (int i = 0; i < 10; i++) {
// 生成 0-9 隨機整數

int number = random.nextInt(10);

System.out.println("生成隨機數:" + number);

}

優缺點分析

Random 使用 LGC 演算法生成偽隨機數的優點是執行效率比較高,生成的速度比較快。

它的缺點是如果 Random 的隨機種子一樣的話,每次生成的隨機數都是可預測的(都是一樣的)。如下程式碼所示,當我們給兩個執行緒設定相同的種子數的時候,會發現每次產生的隨機數也是相同的:

// 建立兩個執行緒for (int i = 0; i < 2; i++) {

new Thread(() -> {
// 建立 Random 物件,設定相同的種子

Random random = new Random(1024);

// 生成 3 次隨機數

for (int j = 0; j < 3; j++) {

// 生成隨機數

int number = random.nextInt();

// 列印生成的隨機數

System.out.println(Thread.currentThread().getName() + ":" +
number);
// 休眠 200 ms

try {
Thread.sleep(200);
} catch (InterruptedException e) {

e.printStackTrace();
}
System.out.println("---------------------");

}
}).start();}

ThreadLocalRandom

ThreadLocalRandom 是 JDK 1.7 新提供的類,它屬於 JUC(java.util.concurrent)下的一員。

通過 Random 的原始碼我們可以看出,Random 在生成隨機數時使用的 CAS 來解決執行緒安全問題的,然而 CAS 線上程競爭比較激烈的場景中效率是非常低的,原因是 CAS 對比時老有其他的執行緒在修改原來的值,所以導致 CAS 對比失敗,所以它要一直迴圈來嘗試進行 CAS 操作。所以在多執行緒競爭比較激烈的場景可以使用 ThreadLocalRandom 來解決 Random 執行效率比較低的問題。

ThreadLocalRandom 的實現原理與 ThreadLocal 類似,它相當於給每個執行緒一個自己的本地種子,從而就可以避免因多個執行緒競爭一個種子,而帶來的額外效能開銷了。

// //得到 ThreadLocalRandom 物件

ThreadLocalRandom random = ThreadLocalRandom.current();

for (int i = 0; i < 10; i++) {

// 生成 0-9 隨機整數

int number = random.nextInt(10);

// 列印結果

System.out.println("生成隨機數:" + number);
}

優缺點分析

ThreadLocalRandom 結合了 Random 和 ThreadLocal 類,並被隔離在當前執行緒中。因此它通過避免競爭操作種子數,從而在多執行緒執行的環境中實現了更好的效能,而且也保證了它的執行緒安全。

另外,不同於 Random, ThreadLocalRandom 明確不支援設定隨機種子。它重寫了 Random 的setSeed(long seed)方法並直接丟擲了 UnsupportedOperationException 異常,因此降低了多個執行緒出現隨機數重複的可能性。

SecureRandom

SecureRandom 繼承自 Random,該類提供加密強隨機數生成器。SecureRandom 不同於 Random,它收集了一些隨機事件,比如滑鼠點選,鍵盤點選等,SecureRandom 使用這些隨機事件作為種子。這意味著,種子是不可預測的,而不像 Random 預設使用系統當前時間的毫秒數作為種子,從而避免了生成相同隨機數的可能性。
// 建立 SecureRandom 物件,並設定加密演算法

SecureRandom random = SecureRandom.getInstance("SHA1PRNG");

for (int i = 0; i < 10; i++) {
// 生成 0-9 隨機整數
int number = random.nextInt(10);
// 列印結果
System.out.println("生成隨機數:" + number);}

SecureRandom 預設支援兩種加密演算法:

  1. SHA1PRNG 演算法,提供者 sun.security.provider.SecureRandom;
  2. NativePRNG 演算法,提供者 sun.security.provider.NativePRNG。

當然除了上述的操作方式之外,你還可以選擇使用 new SecureRandom()來建立 SecureRandom 物件,實現程式碼如下:

SecureRandom secureRandom = new SecureRandom();

通過 new 初始化 SecureRandom,預設會使用 NativePRNG 演算法來生成隨機數,但是也可以配置 JVM 啟動引數“-Djava.security”引數來修改生成隨機數的演算法,或選擇使用 getInstance("演算法名稱")的方式來指定生成隨機數的演算法。

Math

Math 類誕生於 JDK 1.0,它裡面包含了用於執行基本數學運算的屬性和方法,如初等指數、對數、平方根和三角函式,當然它裡面也包含了生成隨機數的靜態方法 Math.random(),此方法會產生一個 0 到 1 的 double 值

for (int i = 0; i < 10; i++) {  
// 產生隨機數
double number = Math.random();
System.out.println("生成隨機數:" + number);
}

擴充套件

當然如果你想用它來生成一個一定範圍的 int 值

for (int i = 0; i < 10; i++) {   
// 生成一個從 0-99 的整數

int number = (int) (Math.random() * 100);

System.out.println("生成隨機數:" + number);
}