返回顶部
首页 > 资讯 > 后端开发 > Python >你知道jdk竟有4个random吗
  • 354
分享到

你知道jdk竟有4个random吗

2024-04-02 19:04:59 354人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

我们从jdk8说起。主要是四个随机数生成器。神马?有四个? 接下来我们简单说下这几个类的使用场景,来了解其中的细微差别,和api设计者的良苦用心。 java.util.Random

我们从jdk8说起。主要是四个随机数生成器。神马?有四个?

接下来我们简单说下这几个类的使用场景,来了解其中的细微差别,和api设计者的良苦用心。

java.util.Randomjava.util.concurrent.ThreadLocalRandomjava.security.SecureRandomjava.util.SplittableRandom

Random

最常用的就是Random。

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

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

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

Random实例不具有加密安全性。  相反,请考虑使用SecureRandom来获取加密安全的伪随机数生成器,以供安全敏感应用程序使用。

Random是最常用的随机数生成类,适用于绝大部分场景。


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实例处于代码一致的tasks线程中,可能这些分布式进程或者线程,产出的序列值是一样的。这也是在JDK 7引入ForkJoin的同时,也引入了ThreadLocalRandom类。

ThreadLocalRandom

这个类的作用,使得随机数的生成器隔离到当前线程。此类继承自java.util.Random,与Math类使用的全局Random生成器一样,ThreadLocalRandom使用内部生成的种子进行初始化,否则可能无法修改。

在并发程序中使用ThreadLocalRandom,通常会有更少的开销和竞争。当多个任务(例如,每个ForkJoinTask)在线程池中并行使用随机数时,ThreadLocalRandom是特别合适的。

切记,在多个线程中不应该共享ThreadLocalRandom实例。

ThreadLocalRandom初始化是private的,所以无法通过构造器设定seed,此外其setSeed方法也被重写而不支持(抛出异常)。默认情况下,每个ThreadLocalRandom实例的seed主导变量值为系统时间(纳秒):


private static long initialSeed() {    String sec = VM.getSavedProperty("java.util.secureRandomSeed");    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()));}

根据其初始化seed的实现,我们也可以通过JVM启动参数增加“-Djava.util.secureRandomSeed=true”,此时初始seed变量将不再是系统时间,而是由SecureRandom类生成一个随机因子,以此作为ThreadLoalRandom的初始seed。

真是够绕的。

源码中,我并没有看到Thread-ID作为变量生成seed,而且nextX方法中随机数生成算法也具有一致性。这意味着,如果多个线程初始ThreadLocalRandom的时间完全一致,在调用方法和过程相同的情况下,产生的随机序列也是相同的;在一定程度上“-Djava.util.secureRandom=true”可以规避此问题。

ThreadLocalRandom并没有使用ThreadLocal来支持内部数据存储等,而是直接使用UnSafe操作当前Thread对象引用中seed属性的内存地址并进行数据操作,我比较佩服SUN的这种巧妙的做法。

SecureRandom

它也继承自Random,该类提供加密强随机数生成器(RNG),加密强随机数最低限度符合FIPS 140-2“加密模块的安全要求”。此外,SecureRandom必须产生非确定性输出。因此,传递给SecureRandom对象的任何种子材料必须是不可预测的,并且所有SecureRandom输出序列必须具有加密强度。(官文,其实我也一知半解)

SecureRandom默认支持两种RNG加密算法实现:

”SHA1PRNG”算法提供者sun.security.provider.SecureRandom

”NativePRNG”提供者sun.security.provider.NativePRNG

默认情况下,是“SHA1PRNG”,即SUN提供的实现。此外可以通过

“-Djava.security=file:/dev/urandom”

(推荐)或者

“-Djava.security=file:/dev/random”

指定使用linux本地的随机算法,

即NativePRNG;

其中“/dev/random”与“/dev/urandom”在不同unix-*平台中实现有所不同,性能也有所差异,建议使用“/dev/urandom”。

/dev/random的一个副本是/dev/urandom (”unlocked”,非阻塞的随机数发生器),它会重复使用熵池中的数据以产生伪随机数据。这表示对/dev/urandom的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random的。它可以作为生成较低强度密码的伪随机数生成器,不建议用于生成高强度长期密码。

算法的内部实现,比较复杂;本人测试,其实性能差不不太大(JDK 8环境)。SecureRandom也是线程安全的。

从输出结果上分析,无论是否指定SecureRandom的初始seed,单个实例多次运行的结果也完全不同 ;多个不同的SecureRandom实例无论是否指定seed,即使指定一样的初始seed,同时运行的结果也完全不同。

SecureRandom继承自Random,但是对nextX方法中的底层方法进行的重写覆盖,不过仍然基于Random的CAS且SecureRandom的底层方法还使用的同步,所以在并发环境下,性能比Random差了一些。

SplittableRandom

JDK 8 新增的API,主要适用于Fork/join形式的跨线程操作中。它并没有继承java.util.Random类。

具有相同seed的不同SplittableRandom实例或者同一个SplittableRandom,多次运行结果是一致的。这和Random是一致的。

非线程安全,不能被并发使用。 (不会报错,但是并发时可能多个线程同时得到相同的随机数)

同ThreadLocalRandom,对“-Djava.util.secureRandom=true”参数支持,但是只有使用默认构造器的时候,才会使用SecureRandom辅助生成初始seed。即不指定初始seed时,同一个SplittableRandom实例多次运行,或者不同的实例运行,结果是不同的。

其中有一个split()方法,用来构造并返回与新的实例,这个实例共享了一些不可变状态。需要注意,split产生的新SplittableRandom实例,与原实例并不存在内部数据的并发竞争,也不会交替或者延续原实例的随机数生成序列(即两个实例产出随机序列的一致性,与原实例没有关系,只是在统计值层面更加接近);但是代码一致性的情况下,多次运行,其随机数序列的结果总是一致的(假如初始seed是指定的,而非默认),这一点与Random、ThreadLocalRandom表现相同。


public SplittableRandom split() {    return new SplittableRandom(nextLong(), mixGamma(nextSeed()));}

样例代码。


System.out.println("第一段");SplittableRandom random = new SplittableRandom(100);Thread thread = new Thread(new Runnable() {    @Override    public void run() {        SplittableRandom _random = random.split();        for (int i=0; i < 5; i++) {            System.out.println("---" + _random.nextInt(100));        }    }});thread.start();thread.join();for (int i=0; i < 5; i++) {    System.out.println("+++" + random.nextInt(100));}System.out.println("第二段");SplittableRandom _random = new SplittableRandom(100);for (int i=0; i < 10; i++) {    System.out.println("..." + _random.nextInt(100));}

执行结果。

第一段---71---85---10---60---98+++44+++87+++77+++67+++72第二段...92...30...44...87...77...67...72...23...9...64

从执行结果上看,split产生的random实例与原实例执行结果上没有相似之处;但是不同SplittableRandom实例(无论是否执行过split),其产出随机数序列是一致的。

性能检测

简析,基准:100000随机数,单线程

1、 Random :2毫秒

2、 ThreadLocalRandom :1毫秒

3、 SecureRandom

1)默认算法,即SHAR1PRNG:80毫秒左右。
2)NativePRNG:90毫秒左右。

4、 SplittableRandom :1毫秒

End

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

jdk竟然有这么多随机数生成器。有没有大吃一精?我反正是跪了。

到此这篇关于jdk竟有4个random的文章就介绍到这了,更多相关jdk有4个random内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 你知道jdk竟有4个random吗

本文链接: https://lsjlt.com/news/128284.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 你知道jdk竟有4个random吗
    我们从jdk8说起。主要是四个随机数生成器。神马?有四个? 接下来我们简单说下这几个类的使用场景,来了解其中的细微差别,和api设计者的良苦用心。 java.util.Random...
    99+
    2024-04-02
  • 15个Vue技巧,你都知道吗
    目录前言1、将一个prop限制在一个类型的列表中2、使用引号来监听嵌套属性3、知道何时使用v-if4、单个作用域插槽的简写5、将局部和全局的style混合在一起6、重写子组件的样式7...
    99+
    2024-04-02
  • JavaScript的这5个技巧你知道了吗
    JavaScript的这5个技巧你知道了吗,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。谁要是说 JavaScript 是世界上比较好的语言,...
    99+
    2024-04-02
  • Java ThreadLocal有什么作用你知道吗
    目录ThreadLocal有什么作用测试代码当前线程只能操作当前ThreadLocal定义的局部变量,其他线程是访问不了。测试结果总结ThreadLocal有什么作用 ThreadL...
    99+
    2024-04-02
  • 香港cdn有何好处你知道吗
    CDN(Content Delivery Network)是一种分布式网络架构,通过将网站的静态资源(如图片、视频、脚本等)缓存在全...
    99+
    2023-09-09
    香港cdn
  • 4个Java8中你需要知道的函数式接口分享
    目录前言为什么需要知道这几个函数式接口Function 接口说明apply 方法andThen 和 compose 方法identify 方法Consumer 接口说明accept ...
    99+
    2023-05-14
    Java8函数式接口 Java 函数式接口 Java8 接口
  • 你知道Go语言的另一个称呼吗?
    Go语言,又称Golang,是一种由谷歌公司开发的开源编程语言。它的设计目标是提高程序员的生产力,降低代码的复杂性,并使程序更加稳定可靠。Go语言具有并发编程的特性,可以轻松地进行并发...
    99+
    2024-04-02
  • Java为什么占用四个字节你知道吗
    目录简单了解计算机为什么要采用二进制表示信息:二进制位分析那么为什么int就占了4个字节呢?问:所有的int都是占四个字节吗?总结简单了解计算机为什么要采用二进制表示信息: 因为计算...
    99+
    2024-04-02
  • Spring异步编程的5个技巧:你知道吗?
    在现代的Web应用程序中,异步编程已经成为了不可或缺的一部分。Spring框架是一个非常流行的Java开发框架,它提供了多种异步编程的方式来帮助开发人员构建高效的Web应用程序。在本文中,我们将介绍Spring异步编程的5个技巧,让你更好...
    99+
    2023-10-08
    django spring 异步编程
  • 你知道JVM中GC Root对象有哪些吗
    目录JVM中GC Root对象有哪些(一)虚拟机栈中引用的对象(二)方法区中类静态属性引用的对象(三)方法区中常量引用的对象(四)本地方法栈中引用的对象JVM 中的 GC Roots...
    99+
    2023-01-28
    JVM GC Root对象 JVM GC Root JVM对象
  • JavaScript循环遍历的24个方法,你都知道吗
    目录前言一、数组遍历方法1. forEach()2. map()3. for of4. filter()5. some()、every()6. reduce()、reduceRigh...
    99+
    2024-04-02
  • 在Python中f-string的几个技巧,你都知道吗
    目录最基础用法自记录表达式多行f-string在f-string中格式化日期控制浮点数精度标准化显示宽度修改为左对齐设置科学计数法格式控制有效数字位数f-string想必很多Pyth...
    99+
    2024-04-02
  • 你知道怎么基于 React 封装一个组件吗
    目录antd 是如何封装组件的仓库地址divider 组件源代码如何暴露组件属性如何设置统一类名前缀如何处理样式与类名divider 组件样式源代码总结前言 很多小伙伴在第一次尝试封...
    99+
    2024-04-02
  • Python 这么皮的五个隐藏彩蛋,你知道吗?
    当编程语言是开源的时候,就会发生有趣的事情。通常,这意味着社区的贡献者会为该语言添加一些有趣和特别的彩蛋以及隐藏的特性(当然前提是不会增加在生产环境中使用的风险)。Python就是一个很好的例子。作为一个开源语言,它的社区就贡献了一些非常有...
    99+
    2023-06-02
  • 新人同学你知道win7和win10哪个流畅吗?
    最好在完全相同的环境中对比 Windows 7 和 Windows 10 的性能。 在使用游戏模式或支持 DirectX 12 的游戏中,更新操作系统的优势立即值得注意。本文将讨论哪个版本的游戏最适合 Windows 7 或 Windows...
    99+
    2023-07-10
  • Spring Boot 和 Spring 到底有啥区别你知道吗
    目录什么是Spring?什么是Spring Boot?1、Maven依赖2、MVC配置3、配置模板引擎4、Spring Security 配置总结对于Spring和SpringBoo...
    99+
    2024-04-02
  • “你知道如何使用Python和Git吗?这个教程能帮到你!”
    Python和Git是现代软件开发中非常重要的工具。Python是一种高级编程语言,被广泛用于数据分析、机器学习、Web应用程序开发等领域;Git是一种版本控制系统,用于跟踪文件的更改历史和协作开发。在本教程中,我们将介绍如何使用Pytho...
    99+
    2023-10-16
    git 教程 响应
  • C++类中隐藏的几个默认函数你知道吗
    目录1.构造函数2.析构函数3.拷贝构造函数4.赋值运算符的重载函数5.一般对象取地址函数7.移动构造函数8.移动赋值函数补充:总结Test类中隐藏的六个默认的函数 class Te...
    99+
    2024-04-02
  • Java Runnable和Thread实现多线程哪个更好你知道吗
    目录1.避免由于Java单继承带来的局限性2.可以实现业务执行逻辑和数据资源的分离3.可以与线程池配合使用,从而管理线程的生命周期总结实现Runnable 接口比继承Thread 类...
    99+
    2024-04-02
  • Java函数编程的5个必备算法,你都知道吗?
    Java是一种非常流行的编程语言,具有强大的函数编程能力。在函数编程中,算法是至关重要的。本文将介绍Java函数编程的5个必备算法。 阶乘算法 阶乘是一个非常基本的数学概念,表示一个自然数的所有小于等于它的正整数的乘积。阶乘算法的实现可...
    99+
    2023-08-31
    函数 编程算法 git
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作