16

Java 生成随机数的 5 种方式,你知道几种?

 3 years ago
source link: https://my.oschina.net/javaroad/blog/4772683
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

77B77nM.jpg!mobile

Java技术栈

www.javastack.cn

关注阅读更多优质文章

1. Math.random() 静态方法

产生的随机数是 0 - 1 之间的一个 double ,即 0 <= random <= 1

使用:

for (int i = 0; i < 10; i++) {
System.out.println(Math.random());
}

结果:

0.3598613895606426 0.2666778145365811 0.25090731064243355 0.011064998061666276 0.600686228175639 0.9084006027629496 0.12700524654847833 0.6084605849069343 0.7290804782514261 0.9923831908303121

实现原理:

When this method is first called, it creates a single new pseudorandom-number generator, exactly as if by the expression new java.util.Random() This new pseudorandom-number generator is used thereafter for all calls to this method and is used nowhere else.

当第一次调用 Math.random() 方法时,自动创建了一个 伪随机数生成器 ,实际上用的是 new java.util.Random() 。当接下来继续调用 Math.random() 方法时,就会使用这个新的 伪随机数生成器

源码如下:

public static double random() {
Random rnd = randomNumberGenerator;
if (rnd == null) rnd = initRNG(); // 第一次调用,创建一个伪随机数生成器
return rnd.nextDouble();
}

private static synchronized Random initRNG() {
Random rnd = randomNumberGenerator;
return (rnd == null) ? (randomNumberGenerator = new Random()) : rnd; // 实际上用的是new java.util.Random()
}
This method is properly synchronized to allow correct use by more than one thread. However, if many threads need to generate pseudorandom numbers at a great rate, it may reduce contention for each thread to have its own pseudorandom-number generator.

initRNG() 方法是 synchronized 的,因此在多线程情况下,只有一个线程会负责创建 伪随机数生成器 (使用当前时间作为种子),其他线程则利用该 伪随机数生成器 产生随机数。 Java生成随机数的几种高级用法 ,这篇推荐看一下。

因此 Math.random() 方法是线程安全的。

什么情况下随机数的生成线程不安全:

  • 线程1在第一次调用 random() 时产生一个生成器 generator1 ,使用当前时间作为种子。
  • 线程2在第一次调用 random() 时产生一个生成器 generator2 ,使用当前时间作为种子。
  • 碰巧 generator1generator2 使用相同的种子,导致 generator1 以后产生的随机数每次都和 generator2 以后产生的随机数相同。

什么情况下随机数的生成线程安全: Math.random() 静态方法使用

  • 线程1在第一次调用 random() 时产生一个生成器 generator1 ,使用当前时间作为种子。
  • random()
    generator1
    generator1
    
public class JavaRandom {
public static void main(String args[]) {
new MyThread().start();
new MyThread().start();
}
}
class MyThread extends Thread {
public void run() {
for (int i = 0; i < 2; i++) {
System.out.println(Thread.currentThread().getName() + ": " + Math.random());
}
}
}

结果:

Thread-1: 0.8043581595645333 Thread-0: 0.9338269554390357 Thread-1: 0.5571569413128877 Thread-0: 0.37484586843392464

2. java.util.Random 工具类

基本算法:linear congruential pseudorandom number generator (LGC) 线性同余法伪随机数生成器 缺点:可预测

An attacker will simply compute the seed from the output values observed. This takes significantly less time than 2^48 in the case of java.util.Random. 从输出中可以很容易计算出种子值。It is shown that you can predict future Random outputs observing only two(!) output values in time roughly 2^16. 因此可以预测出下一个输出的随机数。 You should never use an LCG for security-critical purposes. 在注重信息安全的应用中,不要使用 LCG 算法生成随机数,请使用 SecureRandom。

使用:

Random random = new Random();

for (int i = 0; i < 5; i++) {
System.out.println(random.nextInt());
}

结果:

-24520987 -96094681 -952622427 300260419 1489256498

Random类默认使用当前系统时钟作为种子:

public Random() {
this(seedUniquifier() ^ System.nanoTime());
}

public Random(long seed) {
if (getClass() == Random.class)
this.seed
= new AtomicLong(initialScramble(seed));
else {
// subclass might have overriden setSeed
this.seed = new AtomicLong();
setSeed(seed);
}
}

Random类提供的方法:API

  • nextBoolean()
    true
    false
    
  • nextBytes(byte[] bytes)
  • nextDouble() - 返回 0.0 到 1.0 之间的均匀分布的 double
  • nextFloat() - 返回 0.0 到 1.0 之间的均匀分布的 float
  • nextGaussian() - 返回 0.0 到 1.0 之间的高斯分布(即正态分布)的 double
  • nextInt() - 返回均匀分布的 int
  • nextInt(int n) - 返回 0 到 n 之间的均匀分布的 int (包括 0,不包括 n)
  • nextLong() - 返回均匀分布的 long
  • setSeed(long seed) - 设置种子

只要种子一样,产生的随机数也一样:因为种子确定,随机数算法也确定,因此输出是确定的!

Random random1 = new Random(10000);
Random random2 = new Random(10000);

for (int i = 0; i < 5; i++) {
System.out.println(random1.nextInt() + " = " + random2.nextInt());
}

结果:

-498702880 = -498702880 -858606152 = -858606152 1942818232 = 1942818232 -1044940345 = -1044940345 1588429001 = 1588429001

3. java.util.concurrent.ThreadLocalRandom 工具类

ThreadLocalRandom 是 JDK 7 之后提供, 也是继承至 java.util.Random。

private static final ThreadLocal<ThreadLocalRandom> localRandom =
new ThreadLocal<ThreadLocalRandom>() {
protected ThreadLocalRandom initialValue() {
return new ThreadLocalRandom();
}
};

每一个线程有一个独立的 随机数生成器 ,用于并发产生随机数,能够解决多个线程发生的竞争争夺。 效率更高! 关注公众号Java技术栈回复 java 获取更多 Java 工具类教程。

ThreadLocalRandom 不是直接用 new 实例化,而是第一次使用其静态方法 current() 得到 ThreadLocal<ThreadLocalRandom> 实例,然后调用 java.util.Random 类提供的方法获得各种随机数。

使用:

public class JavaRandom {
public static void main(String args[]) {
new MyThread().start();
new MyThread().start();
}
}
class MyThread extends Thread {
public void run() {
for (int i = 0; i < 2; i++) {
System.out.println(Thread.currentThread().getName() + ": " + ThreadLocalRandom.current().nextDouble());
}
}
}

结果:

Thread-0: 0.13267085355389086 Thread-1: 0.1138484950410098 Thread-0: 0.17187774671469858 Thread-1: 0.9305225910262372

4. java.Security.SecureRandom

也是继承至 java.util.Random。

Instances of java.util.Random are not cryptographically secure.Consider instead using SecureRandom to get a cryptographically secure pseudo-random number generator for use by security-sensitive applications. SecureRandom takes Random Data from your os (they can be interval between keystrokes etc - most os collect these data store them in files - /dev/random and /dev/urandom in case of linux/solaris) and uses that as the seed. 操作系统收集了一些随机事件,比如鼠标点击,键盘点击等等,SecureRandom 使用这些随机事件作为种子。

SecureRandom 提供加密的强随机数生成器 (RNG),要求种子必须是 不可预知 的,产生 非确定性 输出。 SecureRandom 也提供了与实现无关的算法,因此,调用方(应用程序代码)会请求特定的 RNG 算法并将它传回到该算法的 SecureRandom 对象中。

  • 如果仅指定算法名称,如下所示: SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
  • 如果既指定了算法名称又指定了包提供程序,如下所示: SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");

使用:

SecureRandom random1 = SecureRandom.getInstance("SHA1PRNG");
SecureRandom random2 = SecureRandom.getInstance("SHA1PRNG");

for (int i = 0; i < 5; i++) {
System.out.println(random1.nextInt() + " != " + random2.nextInt());
}

结果:

704046703 != 2117229935 60819811 != 107252259 425075610 != -295395347 682299589 != -1637998900 -1147654329 != 1418666937

5. 随机字符串

可以使用 Apache Commons-Lang 包中的 RandomStringUtils 类。Maven 依赖如下:

<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>

API 参考:https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/RandomStringUtils.html

示例:

public class RandomStringDemo {
public static void main(String[] args) {
// Creates a 64 chars length random string of number.
String result = RandomStringUtils.random(64, false, true);
System.out.println("random = " + result);

// Creates a 64 chars length of random alphabetic string.
result = RandomStringUtils.randomAlphabetic(64);
System.out.println("random = " + result);

// Creates a 32 chars length of random ascii string.
result = RandomStringUtils.randomAscii(32);
System.out.println("random = " + result);

// Creates a 32 chars length of string from the defined array of
// characters including numeric and alphabetic characters.
result = RandomStringUtils.random(32, 0, 20, true, true, "qw32rfHIJk9iQ8Ud7h0X".toCharArray());
System.out.println("random = " + result);

}
}

RandomStringUtils 类的实现上也是依赖了 java.util.Random 工具类:

EBBny23.png!mobile

RandomStringUtils 类的定义

参考:

  • http://yangzb.iteye.com/blog/325264

  • http://stackoverflow.com/questions/11051205/difference-between-java-util-random-and-java-security-securerandom

作者:专职跑龙套
链接:https://www.jianshu.com/p/2f6acd169202

AzUnUrz.gif!mobile

Yri6Jfy.png!mobile

UBJrqaz.png!mobile

RJ3I7v.png!mobile

F32qeaZ.png!mobile

YnIvma2.png!mobile

AzMrUze.png!mobile

关注Java技术栈看更多干货

jieeym.jpg!mobile

6nui6jq.gif!mobile

戳原文,获取精选面试题!

本文分享自微信公众号 - Java技术栈(javastack)。

如有侵权,请联系 [email protected] 删除。

本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。


Recommend

  • 77

    前言对于随机数平时我们还是比较常用的,一般都会直接通过各种语言原生自带的随机函数,比如 c++ 中有random()函数,java 中有 Random 类,python 有 random 模块等等。都能很方便生成随机变量,但它们有一个特点,那就是都服从均匀分布,而有些场景需要要生成不同...

  • 18

    在以太坊上使用的随机数来源主要有链上和链下两种途径,其中链上生成核心要解决随机数生成种子的不可预测性。 一、什么是随机数 随机数都是由随机数生成器(Random Number Generator)生成的。随机数分为”...

  • 8

    出品|MS08067实验室(www.ms08067.com) 本文作者: BlackCat (Ms08067内网安全小组成员) BlackCat 微信(欢迎骚扰交流):

  • 4
    • 微信 mp.weixin.qq.com 3 years ago
    • Cache

    Java异步非阻塞编程的几种方式

    一  从一个同步的Http调用说起 一个很简单的业务逻辑,其他后端服务提供了一个接口,我们需要通过接口调用,获...

  • 6
    • www.mghio.cn 3 years ago
    • Cache

    Java 异步编程的几种方式

    Java 异步编程的几种方式 发表于 2021-08-01 | 分类于 Java , ...

  • 4
    • segmentfault.com 3 years ago
    • Cache

    Java调用外部REST请求的几种方式

    1 restTemplate — spring 提供1、RestOperations 提供了各种封装方法,非常方便直接将返回转成实体类。2、默认使用JDK 的HttpURLConnection进行通信,但是可以通过RestTemplate.setRequestFactory 切换到不同的HTTP源:如Apache HttpComponents...

  • 12

    爱码爱生活 spring boot controller 参数接收方式一....

  • 6

    采集 Nginx 日志的几种方式,你知道几种? 来源:cnblogs.com/xiejava/p/12452434.html 由于nginx功能强大,性能突出,越来越多的web应用采用nginx作为http和反向代理的web服务器。而nginx的访问日志不管是做用户行为分析还是安全分析都是非常...

  • 1
    • jasonkayzk.github.io 1 year ago
    • Cache

    Java订阅Binlog的几种方式

    通过 MySQL 提供的 Binlog,我们可以对 MySQL 中的数据进行同步; 本文总结了 Binlog 同步的两种方式:mysql-binlog-connector-java库、Canal组件;

  • 4
    • blog.51cto.com 1 year ago
    • Cache

    Java实现本地缓存的几种方式

    Java实现本地缓存的几种方式 精选 原创 引入缓存,主要用于实现系统的高性能,高并发。将

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK