1、java.util.Random()
偽隨機,如果不傳入種子,以當前系統時間為種子,通過一系列計算得出隨機值,種子相同的情況下,每次調用得到的隨機值是固定的
2、Math.random()
public static double random() { return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble(); }
private static final class RandomNumberGeneratorHolder { static final Random randomNumberGenerator = new Random(); }
Math內部也是通過java.util.Random()實現的
3、java.security.SecureRandom()
new SecureRandom(); SecureRandom.getInstance(""); SecureRandom.getInstanceStrong();
可以使用以上三種方式生成對象,推薦使用new SecureRandom(),使用系統默認的算法並由系統生成種子
SecureRandom繼承於Random類,其具體的實現類為SecureRandomSpi,與平台相關,以JDK版本jdk-8u251-linux-x64為例,在Linux系統下,其默認的實現類為NativePRNG,隨機源為/dev/random以及/dev/urandom
/dev/random和/dev/urandom是Linux系統中提供的隨機偽設備,使用這兩個設備可以為應用提供不為空的隨機字節流,兩者的區別是/dev/random在系統噪聲不足時,會產生阻塞,而/dev/urandom是/dev/random的一個副本,它會重復使用熵池中的數據以產生偽隨機數據,因此不會有阻塞的問題
NativePRNG基於混合模式,為生成隨機數與隨機種子提供了不同的支持
case MIXED: File var4 = null; URL var3; if ((var3 = NativePRNG.getEgdUrl()) != null) { try { var4 = SunEntries.getDeviceFile(var3); } catch (IOException var7) { ; } } if (var4 != null && var4.canRead()) { var1 = var4; } else { var1 = new File("/dev/random"); } var2 = new File("/dev/urandom"); break;
對於隨機數的生成,NativePRNG總是使用/dev/urandom作為隨機源,而對於種子的生成,其隨機源則取決於配置。在NativePRNG.getEgdUrl()方法中,程序會讀取配置java.security.egd以及securerandom.source配置
比如配置-Djava.security.egd=file:/dev/urandom,則采用非阻塞模式,而如果配置-Djava.security.egd=file:/dev/urandom,則采用阻塞模式,系統默認也是采用阻塞模式生成種子,以保證足夠的隨機性
private static final String seedSource = (String)AccessController.doPrivileged(new PrivilegedAction<String>() { public String run() { String var1 = System.getProperty("java.security.egd", ""); if (var1.length() != 0) { return var1; } else { var1 = Security.getProperty("securerandom.source"); return var1 == null ? "" : var1; } } });
需要注意的是,SecureRandom.getInstanceStrong()方法,並不遵從上述方式,而是讀取配置securerandom.strongAlgorithms,並以此構建隨機生成器,在Linux系統下,該配置默認為NativePRNGBlocking:SUN,即采用NativePRNG.Blocking類作為其實現,該方法與SecureRandom.getInstance("NativePRNGBlocking","SUN")語義相同
case BLOCKING: var1 = new File("/dev/random"); var2 = new File("/dev/random"); break;
由代碼可知,getInstanceStrong()返回的隨機生成器,對於生成隨機種子以及隨機數都是訪問/dev/random,這個方法性能上是非常差的,沒有特殊需求,不推薦使用