Java提供兩種類型的隨機數發生器
java
1.僞隨機數發生器linux
僞隨機數發生器採用特定的算法,將隨機數種子seed轉換成一系列的僞隨機數。僞隨機數依賴於seed的值,給定相同的seed值老是生成相同的隨機數。僞隨機數的生成過程只依賴CPU,不依賴任何外部設備,生成速度快,不會阻塞。算法
Java提供的僞隨機數發生器有java.util.Random類和java.util.concurrent.ThreadLocalRandom類。安全
Random類採用AtomicLong實現,保證多線程的線程安全性,但正如該類註釋上說明的,多線程併發獲取隨機數時性能較差。多線程
多線程環境中可使用ThreadLocalRandom做爲隨機數發生器,ThreadLocalRandom採用了線程局部變量來改善性能,這樣就可使用long而不是AtomicLong,此外,ThreadLocalRandom還進行了字節填充,以免僞共享。併發
2.強隨機數發生器dom
強隨機數發生器依賴於操做系統底層提供的隨機事件。強隨機數生成器的初始化速度和生成速度都較慢,並且因爲須要必定的熵累積才能生成足夠強度的隨機數,因此可能會形成阻塞。熵累積一般來源於多個隨機事件源,如敲擊鍵盤的時間間隔,移動鼠標的距離與間隔,特定中斷的時間間隔等。因此,只有在須要生成加密性強的隨機數據的時候才用它。ide
Java提供的強隨機數發生器是java.security.SecureRandom類,該類也是一個線程安全類,使用synchronize方法保證線程安全,但jdk並無作出承諾在未來改變SecureRandom的線程安全性。所以,同Random同樣,在高併發的多線程環境中可能會有性能問題。高併發
在linux的實現中,可使用/dev/random和/dev/urandom做爲隨機事件源。因爲/dev/random是堵塞的,在讀取隨機數的時候,當熵池值爲空的時候會堵塞影響性能,尤爲是系統大併發的生成隨機數的時候,若是在隨機數要求不高的狀況下,能夠去讀取/dev/urandom來避免阻塞,方法是經過設置參數性能
-Djava.security.egd=file:/dev/urandom
但這樣因爲jdk的一個bug,實際上須要這樣指定這個值
-Djava.security.egd=file:/dev/./urandom
緣由是,在 sun.security.provider.SunEntries類,seedSource先讀取系統參數java.security.egd,若是值爲空的時候,讀取java.security配置文件中的參數securerandom.source, 在一般狀況下,就是讀取參數securerandom.source,默認值是/dev/urandom。
sun.security.provider.SeedGenerator final static String URL_DEV_RANDOM = SunEntries.URL_DEV_RANDOM; final static String URL_DEV_URANDOM = SunEntries.URL_DEV_URANDOM; if (egdSource.equals(URL_DEV_RANDOM) || egdSource.equals(URL_DEV_URANDOM)) { try { instance = new NativeSeedGenerator(); if (debug != null) { debug.println("Using operating system seed generator"); } } catch (IOException e) { if (debug != null) { debug.println("Failed to use operating system seed " + "generator: " + e.toString()); } } } else if (egdSource.length() != 0) { try { instance = new URLSeedGenerator(egdSource); if (debug != null) { debug.println("Using URL seed generator reading from " + egdSource); } } catch (IOException e) { if (debug != null) debug.println("Failed to create seed generator with " + egdSource + ": " + e.toString()); } }
在代碼中能夠看到當配置值是file:/dev/random或者file:/dev/urandom的時候,啓用NativeSeedGenerator, 而在linux下的NativeSeedGenerator類的實現是這樣的
class NativeSeedGenerator extends SeedGenerator.URLSeedGenerator { NativeSeedGenerator() throws IOException { super(); } }
而URLSeedGenerator的默認構造方法是
URLSeedGenerator() throws IOException { this(SeedGenerator.URL_DEV_RANDOM); }
也就是說哪怕設置了-Djava.security.egd=file:/dev/urandom,最後的結果同樣是讀取file:/dev/random,解決辦法就是使用linux的多種路徑表示法,即便用file:/dev/./urandom來繞過這個問題。