Java如何生成随机数 - Random、ThreadLocalRandom、SecureRandom


Java7 的Random伪随机数和线程安全的ThreadLocalRandom
一、Random伪随机数:

Random 类专门用于生成一个伪随机数,它有两个构造器: 一个构造器使用默认的种子(以当前时间作为种子) ,另 个构造器需要程序员显式传入一个 long 整数的种子.
当使用默认的种子或传入相同的种子构造 Random 对象时,它们属于同一个种子,只要两个 Random 对象的种子相同,而且方法的调用顺序也相同,它们就会产生相同的数字序列 也就是说, Random 产生的数字并不是真正随机的,而是一种伪随机。

![](https://img2020.cnblogs.com/blog/1438655/202102/1438655-20210208160430455-983724190.png)


常用解决方案:

为了避免两个 Random 对象产生相同的数字序列,通常推荐使用当前时间作为 Random 对象的种子:

Random rand = new Random(System.currentTimeMi11is());

二、ThreadLocalRandom

ThreadLocalRandom 类是 Java 新增的 ,它 Random 的增强版 在并发访 问的环境下,使用ThreadLocalRandom 来代替 Random 可以减少多线程资源竞争,最终保证系统具有更好的线程安全性。

多线程环境下使用 ThreadLocalRandom 的方式与使用 Random 基本一样,示例如下:

ThreadLocalRandom random = ThreadLocalRandom.current();
// 生成一个 3-10 之间(包括3,不包括10)的伪随机整数
int num1 = random.nextInt(3, 10);
// 生成一个 2.0-10.0 之间的伪随机浮点数
int num2 = random.nextDouble(2.0, 10.0);
Random即:java.util.Random,
ThreadLocalRandom 即:java.util.concurrent.ThreadLocalRandom
SecureRandom即:java.security.SecureRandom


Q:Random是不是线程安全的?
A:Random是线程安全的,但是多线程下可能性能比较低。
参考:
http://docs.oracle.com/javase/7/docs/api/java/util/Random.html
http://stackoverflow.com/questions/5819638/is-random-class-thread-safe

Q:ThreadLocalRandom为什么这么快?
A:其实这个看下源码就知道了。。因为Random用了很多CAS的类,ThreadLocalRandom根本没有用到。

Q:为什么在高强度要求的情况下,不要用Random?
A:特别是在生成验证码的情况下,不要使用Random,因为它是线性可预测的。记得有个新闻说的是一个赌博网站,为了说明其公平,公开的它的源代码,结果因为随机数可预测漏洞被攻击了。所以在安全性要求比较高的场合,应当使用SecureRandom。

update 2014-4-22:  http://news.cnblogs.com/n/206074/

参考:http://www.inbreak.net/archives/349

Q:从理论上来说计算机产生的随机数都是伪随机数,那么如何产生高强度的随机数?
A:产生高强度的随机数,有两个重要的因素:种子和算法。当然算法是可以有很多的,但是如何选择种子是非常关键的因素。如Random,它的种子是System.currentTimeMillis(),所以它的随机数都是可预测的。那么如何得到一个近似随机的种子?这里有一个很别致的思路:收集计算机的各种信息,如键盘输入时间,CPU时钟,内存使用状态,硬盘空闲空间,IO延时,进程数量,线程数量等信息,来得到一个近似随机的种子。这样的话,除了理论上有破解的可能,实际上基本没有被破解的可能。而事实上,现在的高强度的随机数生成器都是这样实现的。
比如Windows下的随机数生成器:
http://blogs.msdn.com/b/michael_howard/archive/2005/01/14/353379.aspx
http://msdn.microsoft.com/en-us/library/aa379942%28VS.85%29.aspx
Linux下的 /dev/random:
http://zh.wikipedia.org/wiki//dev/random
据SecureRandom的Java doc,说到在类unix系统下,有可能是利用 /dev/random,来实现的。


其它的一些有意思的东东:
最快的安全性要求不高的生成UUID的方法(注意,强度不高,有可能会重复):
new UUID(ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong());
在一个网站上看到的,忘记出处了。


随机生成产生随机数的函数?
是否可以利用一个随机数生成器来生成一系列的随机代码,然后作为一个新的随机数生成器?貌似强度是传递的,似乎没意义。

综述
生成随机数是很常见的任务。 这也是 JAVA 提供 Random 的原因。但是它在多线程环境中性能并不高。

简单来说,Random 之所以在多线程环境中性能不高的原因是多个线程共享同一个 Random 实例并进行争夺。

为了解决这个限制,JAVA 在 JDK 7 中引入了 ThreadLocalRandom 类,用于在多线程环境下生产随机数。

ThreadLocalRandom 强于 Random
ThreadLocalRandom 结合了 Random 和 ThreadLocal 类,并被隔离在当前线程中。因此它通过避免任何对 Random 对象的并发访问,从而在多线程环境中实现了更好的性能。

一个线程获取到的随机数不受另一个线程影响,而 Random 提供全局的随机数。

另外,不同于 Random, ThreadLocalRandom 明确的不支持设置随机种子。 它重写了 Random 的
setSeed(long seed) 方法并直接抛出了 UnsupportedOperationException 异常。

现在让我们来看看几种生产随机 int、long、double 的方式。

使用 ThreadLocalRandom 生产随机数
根据 Oracle 的文档,我们只需要调用 ThreadLocalRandom.current() 方法,它就会返回当前线程的 ThreadLocalRandom 的实例。
然后我们就可以调用这个实例的方法获取随机数

让我们生成一个没有任何限制的 int 值

int unboundedRandomValue = ThreadLocalRandom.current().nextInt());
现在再生成一个有界的 int, 即介于两个数之间。

这是一个生成 0 ~ 100 的 int 值的例子

int boundedRandomValue = ThreadLocalRandom.current().nextInt(0, 100);
请注意:0 是包含在界限内, 而 100 是不在范围内的。

我们可以使用与示例中相似的方式,调用 nextLong() 和 nextDouble() 方法来生产 long 和 double 值。

JAVA 8 还添加了一个 nextGaussian() 方法来生成正态分布的值,与生成器序列生成的值偏差 0.0 ~ 1.0 。

和 Random 类一样,我们可以使用 doubles(), ints(), longs()方法来生成随机数流。

作者:yangc91
链接:https://www.jianshu.com/p/29ae27e401d1
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。