jdk中的4种随机方法

133 阅读3分钟

jdk中主要是四个随机数生成器

java.util.Random
java.util.concurrent.ThreadLocalRandom
java.security.SecureRandom
java.util.SplittableRandom

Random

最常用的就是Random。用来生成伪随机数,默认使用48位种子、线性同余公式进行修改。我们可以通过构造器传入初始seed,或者通过setSeed重置(同步)。默认seed为系统时间的纳秒数。

如果两个(多个)不同的Random实例,使用相同的seed,按照相同的顺序调用相同方法,那么它们得到的数字序列也是相同的。这种设计策略,优点是“相同seed”生成的序列是一致的,使过程具有可回溯和校验性(平台无关、运行时机无关);缺点就是,这种一致性潜在引入其“可被预测”的风险。

Random random = new Random(100);
System.out.println(random.nextInt(10) + "," + random.nextInt(30) + "," + random.nextInt(50));

random = new Random(100);
System.out.println(random.nextInt(10) + "," + random.nextInt(30) + "," + random.nextInt(50));

random = new Random(100);
System.out.println(random.nextInt(10) + "," + random.nextInt(30) + "," + random.nextInt(50));

上述三个不同的random实例,使用了相同的seed。调用过程一样,其中产生的随机数序列也是完全一样的。多次执行结果也完全一致,简单而言,只要初始seed一样,即使实例不同,多次运行它们的结果都是一致的。如果Random构造器中不指定seed,而是使用默认的系统时间纳秒数作为主导变量,三个random实例执行的结果是不同的。多次执行结果也不一样。由此可见,seed是否具有随机性,在一定程度上,也决定了Random产生结果的随机性。

Random的实例是线程安全的。但是,跨线程并发使用相同的Random实例可能会遇到争用,从而导致性能稍欠佳(nextX方法中,在对seed赋值时使用了CAS,测试结果显示,其实性能损耗很小)。请考虑在多线程设计中使用ThreadLocalRandom。同时,我们在并发环境下,也没有必要刻意使用多个Random实例。

ThreadLocalRandom

继承自Random,与Random一样,ThreadLocalRandom使用内部生成的种子进行初始化,并且初始化是private的,所以无法通过构造器设定seed。在并发程序中使用ThreadLocalRandom,通常会有更少的开销和竞争

SecureRandom

继承自Random,无论是否指定SecureRandom的初始seed,单个实例多次运行的结果也完全不同,多个不同的SecureRandom实例无论是否指定seed,即使指定一样的初始seed,同时运行的结果也完全不同

SplittableRandom

JDK 8 新增的API,主要适用于Fork/join形式的跨线程操作中。它并没有继承Random类,具有相同seed的不同SplittableRandom实例或者同一个SplittableRandom,多次运行结果是一致的。这和Random是一致的。非线程安全,不能被并发使用。 (不会报错,但是并发时可能多个线程同时得到相同的随机数)

总结

平常使用Random,或者大多数时候使用,都是没有问题的,它也是线程安全的。SplittableRandom是内部使用的类,应用较少,即使它也是public的也掩饰不了偏门。ThreadLocalRandom是为了在高并发环境下节省一点细微的时间,追求性能的应用推荐使用。而对于有安全需求的,又希望更随机一些的,用SecureRandom再好不过了。

cloud.tencent.com/developer/a…