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来绕过这个问题。