使用SecureRandom生成验证码随机数,线程阻塞问题记录
背景:
针对于Random的低效,采用SecureRandom.getInstanceStrong() 来生成短信验证码随机数,一段时间后linux环境下出现线程阻塞问题!
问题刨析:
Linux 内核采用熵来描述数据的随机性,熵(entropy)是描述系统混乱无序程度的物理量,一个系统的熵越大则说明该系统的有序性越差,即不确定性越大。内核维护了一个熵池用来收集来自设备驱动程序和其它来源的环境噪音。理论上,熵池中的数据是完全随机的,可以实现产生真随机数序列
Linux内核提供的随机数发生器:默认/dev/urandom
内核中随机数发生器 PRNG 为一个字符设备 random,random设备了提供了 2 个字符设备供用户态进程使用1:/dev/random:阻塞的随机数发生器 ;2:/dev/urandom:非阻塞的随机数发生器
- /dev/random 适用于对随机数质量要求比较高的请求,在熵池中数据不足时, 读取 dev/random 设备时会返回小于熵池噪声总数的随机字节。/dev/random 可生成高随机性的公钥或一次性密码本。若熵池空了,对/dev/random 的读操作将会被阻塞,直到收集到了足够的环境噪声为止。这样的设计使得/dev/random 是真正的随机数发生器,提供了最大可能的随机数据熵。
- /dev/urandom,非阻塞的随机数发生器,它会重复使用熵池中的数据以产生伪随机数据。这表示对/dev/urandom 的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random 的。它可以作为生成较低强度密码的伪随机数生成器,对大多数应用来说,随机性是可以接受的。
解决方案:
方案一:(真随机数)采用new SecureRandom() 使用 /dev/urandom 生成种子,不会产生阻塞。
SecureRandom random = new SecureRandom();
方案二:(伪随机数)ThreadLocalRandom.current()
ThreadLocalRandom random = ThreadLocalRandom.current();
// 获得指定范围内的随机数 [0,limit)
random.nextInt(limit);
补充:建议使用ThreadLocalRandom.current()生成伪随机数。原因:
通过固定算法产生的随机数都是伪随机数,Java语言的随机数生成器或者说整个计算机中的随机数函数,生成的都是伪随机数。只有通过真实的随机事件产生的随机数才是真随机数。比如,通过机器的硬件、CPU温度、当天天气、噪音等真随机事件产生随机数,如果想要产生真随机数,那么需要一定的物理手段,开销极大,或许得不偿失!因此目前的随机数都是通过一个非常复杂的算法计算出来的!