专业游戏门户,分享手游网游单机游戏百科知识攻略!

嗨游网
嗨游网

java随机数怎么生成,java随机数生成的4种方法

来源:小嗨整编  作者:小嗨  发布时间:2023-02-24 04:03
摘要:java随机数怎么生成,java随机数生成的4种方法在Java中,生成随机数的场景有很多,所以本文我们就来盘点一下4种生成随机数的方式,以及它们之间的区别和每种生成方式所对应的场景。RandomRandom类诞生于JDK1.0,它产生的随机...

我们来看 Random 的实现源码:

public Random() {    this(seedUniquifier() ^ System.nanoTime());} public int nextInt() {    return next(32);} protected int next(int bits) {    long oldseed, nextseed;    AtomicLong seed = this.seed;    do {        oldseed = seed.get();        nextseed = (oldseed * multiplier + addend) & mask;    } while (!seed.compareAndSet(oldseed, nextseed)); // CAS(Compare and Swap)生成随机数    return (int)(nextseed >>> (48 - bits));}

PS:本文所有源码来自于 JDK 1.8.0_211。

从以上源码可以看出,Random 底层使用的是 CAS(Compare and Swap,比较并替换)来解决线程安全问题的,因此对于绝大数随机数生成的场景,使用 Random 不乏为一种很好的选择。

PS:Java 并发机制实现原子操作有两种:一种是锁,一种是 CAS。

CAS 是 Compare And Swap(比较并替换)的缩写,java.util.concurrent.atomic 中的很多类,如(AtomicInteger AtomicBoolean AtomicLong等)都使用了 CAS 机制来实现。

ThreadLocalRandom

ThreadLocalRandom 是 JDK 1.7 新提供的类,它属于 JUC(java.util.concurrent)下的一员,为什么有了 Random 之后还会再创建一个 ThreadLocalRandom?

原因很简单,通过上面 Random 的源码我们可以看出,Random 在生成随机数时使用的 CAS 来解决线程安全问题的,然而 CAS 在线程竞争比较激烈的场景中效率是非常低的,原因是 CAS 对比时老有其他的线程在修改原来的值,所以导致 CAS 对比失败,所以它要一直循环来尝试进行 CAS 操作。所以在多线程竞争比较激烈的场景可以使用 ThreadLocalRandom 来解决 Random 执行效率比较低的问题。

当我们第一眼看到 ThreadLocalRandom 的时候,一定会联想到一次类 ThreadLocal,确实如此。ThreadLocalRandom 的实现原理与 ThreadLocal 类似,它相当于给每个线程一个自己的本地种子,从而就可以避免因多个线程竞争一个种子,而带来的额外性能开销了。

① 基础使用

接下来我们使用 ThreadLocalRandom 来生成一个 0 到 10 的随机数(不包含 10),实现代码如下:

// 得到 ThreadLocalRandom 对象ThreadLocalRandom random = ThreadLocalRandom.current();for (int i = 0; i < 10; i++) {    // 生成 0-9 随机整数    int number = random.nextInt(10);    // 打印结果    System.out.println("生成随机数:" + number);}

以上程序的执行结果为:

java随机数怎么生成,java随机数生成的4种方法

② 实现原理

ThreadLocalRandom 的实现原理和 ThreadLocal 类似,它是让每个线程持有自己的本地种子,该种子在生成随机数时候才会被初始化,实现源码如下:

public int nextInt(int bound) {    // 参数效验    if (bound <= 0)        thrownew IllegalArgumentException(BadBound);    // 根据当前线程中种子计算新种子    int r = mix32(nextSeed());    int m = bound - 1;    // 根据新种子和 bound 计算随机数    if ((bound & m) == 0) // power of two        r &= m;    else { // reject over-repsented candidates        for (int u = r >>> 1;             u + m - (r = u % bound) < 0;             u = mix32(nextSeed()) >>> 1)            ;    }    return r;} final long nextSeed() {    Thread t; long r; // read and update per-thread seed    // 获取当前线程中 threadLocalRandomSeed 变量,然后在种子的基础上累加 GAMMA 值作为新种子    // 再使用 UNSAFE.putLong 将新种子存放到当前线程的 threadLocalRandomSeed 变量中    UNSAFE.putLong(t = Thread.currentThread(), SEED,                   r = UNSAFE.getLong(t, SEED) + GAMMA);     return r;}

③ 优缺点分析

ThreadLocalRandom 结合了 Random 和 ThreadLocal 类,并被隔离在当前线程中。因此它通过避免竞争操作种子数,从而在多线程运行的环境中实现了更好的性能,而且也保证了它的线程安全。

另外,不同于 Random, ThreadLocalRandom 明确不支持设置随机种子。它重写了 Random 的setSeed(long seed) 方法并直接抛出了 UnsupportedOperationException 异常,因此降低了多个线程出现随机数重复的可能性。

源码如下:

public void setSeed(long seed) {    // only allow call from super() constructor    if (initialized)        thrownew UnsupportedOperationException();}

只要程序中调用了 setSeed() 方法就会抛出 UnsupportedOperationException 异常,如下图所示:

java随机数怎么生成,java随机数生成的4种方法

ThreadLocalRandom 缺点分析

虽然 ThreadLocalRandom 不支持手动设置随机种子的方法,但并不代表 ThreadLocalRandom 就是完美的,当我们查看 ThreadLocalRandom 初始化随机种子的方法 initialSeed() 源码时发现,默认情况下它的随机种子也是以当前时间有关,源码如下:

private static long initialSeed() {    // 尝试获取 JVM 的启动参数    String sec = VM.getSavedProperty("java.util.secureRandomSeed");    // 如果启动参数设置的值为 true,则参数一个随机 8 位的种子    if (Boolean.parseBoolean(sec)) {        byte[] seedBytes = java.security.SecureRandom.getSeed(8);        long s = (long)(seedBytes[0]) & 0xffL;        for (int i = 1; i < 8; ++i)            s = (s << 8) | ((long)(seedBytes[i]) & 0xffL);        return s;    }    // 如果没有设置启动参数,则使用当前时间有关的随机种子算法    return (mix64(System.currentTimeMillis()) ^            mix64(System.nanoTime()));}

从上述源码可以看出,当我们设置了启动参数“-Djava.util.secureRandomSeed=true”时,ThreadLocalRandom 会产生一个随机种子,一定程度上能缓解随机种子相同所带来随机数可预测的问题,然而默认情况下如果不设置此参数,那么在多线程中就可以因为启动时间相同,而导致多个线程在每一步操作中都会生成相同的随机数。

SecureRandom

SecureRandom 继承自 Random,该类提供加密强随机数生成器。SecureRandom 不同于 Random,它收集了一些随机事件,比如鼠标点击,键盘点击等,SecureRandom 使用这些随机事件作为种子。这意味着,种子是不可预测的,而不像 Random 默认使用系统当前时间的毫秒数作为种子,从而避免了生成相同随机数的可能性。

基础使用

// 创建 SecureRandom 对象,并设置加密算法SecureRandom random = SecureRandom.getInstance("SHA1PRNG");for (int i = 0; i < 10; i++) {    // 生成 0-9 随机整数    int number = random.nextInt(10);    // 打印结果    System.out.println("生成随机数:" + number);}


本文地址:软件教程频道 https://www.eeeoo.cn/ruanjian/903352_2.html,嗨游网一个专业手游免费下载攻略知识分享平台,本站部分内容来自网络分享,不对内容负责,如有涉及到您的权益,请联系我们删除,谢谢!


软件教程
小编:小嗨整编
相关文章相关阅读
  • javascript教程网(javascript教程推荐)

    javascript教程网(javascript教程推荐)

    javascript教程网(javascript教程推荐)JavaScript作为一种广泛应用于网页开发的技术,已经成为前端开发不可或缺的一部分。对于刚接触编程的菜鸟来说,选择一份合适的JavaScript教程至关重要。本文将为您推荐几款优...

  • java软件叫什么(java软件安装教程详细)?

    java软件叫什么(java软件安装教程详细)?

    java软件叫什么(java软件安装教程详细)?Java语言是由美国Sun(StanfordUniversityNetwork)公司在1995年推出的,2009年Oracle甲骨文公司收购了Sun公司,如果想开发一个全新的Java程序,必须...

  • 我的世界java版官网(我的世界java版怎么下载)?

    我的世界java版官网(我的世界java版怎么下载)?

    我的世界java版官网(我的世界java版怎么下载)?我的世界java版早在2011年就已发布,而当时的中文补丁使许多人并未购买正版。而如今的网易版让许多人觉得没有童年的味道,那么如何下载java版呢?我的世界java版官网:minecra...

  • 什么是构造函数?详解JavaScript中的构造函数

    什么是构造函数?详解JavaScript中的构造函数

    作为原型和原型链的基础,先了解清楚构造函数以及它的执行过程才能更好地帮助我们学习原型和原型链的知识。本篇文章带大家详细了解一下javascript中的构造函数,介绍一下怎么利用构造函数创建一个js对象,希望对大家有所帮助!一个普通的函数被用...

  • Java 中的各种锁有哪些?

    Java 中的各种锁有哪些?

      Java中15种锁的介绍  在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类。介绍的内容如下:  公平锁/非公平锁  可重入锁/不可重入锁  独享锁/共享锁  互斥锁/读写锁  乐观锁...

  • Java中的Scanner操作详解

    Java中的Scanner操作详解

    scanner是java中的一个常用类,用来读取控制台或文件中的输入数据。它提供了一种简单的方式来解析基本类型和字符串,并支持对正则表达式进行匹配。Scanner类位于java.util包中,因此在编写程序时需要import...

  • java8新特性有哪些

    java8新特性有哪些

    java8新特性有:1、lambda表达式;2、方法引用;3、默认方法;4、新编译工具;5、streamapi;6、datetimeapi;7、option;8、nashornjavascript引擎。Java8新增了非常多的特性...

  • java中tostring方法的作用是什么

    java中tostring方法的作用是什么

    java中tostring方法的作用是会返回一个【以文本方式表示】此对象的字符串,结果是一个简明但易于读懂的信息表达式。java中tostring方法的作用是toString方法会返回一个“以文本方式表示”此对象的字符串。结果是一个简明但易...

  • 周排行
  • 月排行
  • 年排行

精彩推荐