先看看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 setSeedthis.seed = new AtomicLong();setSeed(seed);}}

在Random类中,构造方法有2个:

  • 一个带long型参数,
  • 一个不带参数;实际是调用带参的构造;如果不带参数,Random会设置一个默认参数seed;

1.Random设置默认seed

首先研究下Random如何设置默认的seed;

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

分为2部分:

  • seedUniquifier()
  • System.nanoTime():获取系统时间(纳秒 1ns = 10-6ms)

1.1seedUniquifier源码:

    private static long seedUniquifier() {for (;;) {long current = seedUniquifier.get();long next = current * 181783497276652981L;if (seedUniquifier.compareAndSet(current, next))return next;}}private static final AtomicLong seedUniquifier= new AtomicLong(8682522807148012L);

代码过程:

  • 获取一个Random固定的long值current ;
  • 利用current 计算出next
  • 利用CAS修改 seedUniquifier的值,修改成功就返回;因为利用了CAS所以这个seedUniquifier是线程安全的,可以在多线程中使用;

总结一下:利用random的一个固定属性值,通过乘一个数(固定值),计算出了一个值 然后返回;可以看出这都是固定值;也就是说,只要初始状态确定,利用这个函数生成的一系列的数字顺序也就确定了;

1.2生成seed

我们知道默认的seed:由方法:seedUniquifier() 生成数字与系统当前时间这2个数字通过 异或位运算 生成的;其中通过方法:seedUniquifier()生成的是一个固定数字序列;而产生变化的是获取的系统时间,通过这2个数做一个位运算生成一个新数字,作为起始的random的起始值seed;因此每次调用Random类的无参构造都能获取到一个不同seed看起来还是比较像随机了;

2.Random初始化处理seed

在上面我们提到可以自定义seed或者系统自动生成;在random拿到参数seed之后其实还做了处理,并不是将传进来的seed直接保存;

    public Random(long seed) {if (getClass() == Random.class)this.seed = new AtomicLong(initialScramble(seed));else {// subclass might have overriden setSeedthis.seed = new AtomicLong();setSeed(seed);}}private static long initialScramble(long seed) {return (seed ^ multiplier) & mask;}private static final long multiplier = 0x5DEECE66DL;
private static final long mask = (1L << 48) - 1;

在处理seed时候,首先判断了一下是不是Random的子类,如果是Random子类,那么处理seed的方法可能被重写,这个时候seed就会交给子类处理;如果子类没重写,那还是用的Random类的处理方法:initialScramble();

在initialScramble()中处理seed: (seed ^ multiplier) & mask 生成的值作为Random的真正的初始值seed;

3.next(int bit)生成随机数

这个方法是random比较核心 的方法,不同类型值的获取随机数都是直接或间接利用了这个方法;

    private final AtomicLong seed;private static final long multiplier = 0x5DEECE66DL;private static final long addend = 0xBL;private static final long mask = (1L << 48) - 1;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));return (int)(nextseed >>> (48 - bits));}
  • 获取到旧值 :oldseed
  • 利用旧值oldseed ,计算一个新值 nextseed ;(oldseed * multiplier + addend) & mask;
  • 将nextseed 更新为新值,更新成功结束循环;返回 (nextseed >>> (48 - bits))

这个算法本身也不难,就是利用一个固定的算法: (oldseed * multiplier + addend) & mask; 其中:multiplier ,addend,mask都是固定值,而oldseed 也是在生成random对象之后就已经初始化了,在这些数字都确定之后,利用这个算法生成的所有数字序列也就确定了,因此random的这个算法被大家称伪随机;因为在初始值确定之后,每次得到的返回值都已经确定了,生成的都是可预测的数字;

4.Random生成随机数的测试

测试1:传参指定相同的seed

我们经过上面的分析知道,当初始值seed相同时,生成的随机数序列是相同的;我们可以通过带参构造指定相同的seed;

  • 测试代码:2个Random,都指定相同的seed:1;
long multiplier = 0x5DEECE66DL;//Random中的multiplierlong mask  =(1L << 48) - 1;//Random中的masklong seed = (1 ^ multiplier) & mask;//初始化seed的算法System.out.println("由1生成的真正的初始seed:"+seed);System.out.println("**************************");Random r1 = new Random(1);Random r2 = new Random(1);for (int i = 0; i < 5; i++) {System.out.println("[r1]"+r1.nextInt());System.out.println("[r2]"+r2.nextInt());System.out.println("++++++++++++++++++++++++++++++++");}
  • 测试结果

由1生成的真正的初始seed:25214903916
**************************
[r1]-1155869325
[r2]-1155869325
++++++++++++++++++++++++++++++++
[r1]431529176
[r2]431529176
++++++++++++++++++++++++++++++++
[r1]1761283695
[r2]1761283695
++++++++++++++++++++++++++++++++
[r1]1749940626
[r2]1749940626
++++++++++++++++++++++++++++++++
[r1]892128508
[r2]892128508
++++++++++++++++++++++++++++++++

测试2:不传参由Random自动生成初始seed

由于调用Random对象构造方法的时间不同,就会导致获取到的系统时间不同;这种情况下初始的seed就会不同;生成的随机数序列也就不同;

  • 测试代码(1)
      Random r1 = new Random();Random r2 = new Random();for (int i = 0; i < 3; i++) {System.out.println("[r1]"+r1.nextInt());System.out.println("[r2]"+r2.nextInt());System.out.println("++++++++++++++++++++++++++++++++");}
  • 测试结果(1)
[r1]1283182367
[r2]-1068120877
++++++++++++++++++++++++++++++++
[r1]-816566372
[r2]735383144
++++++++++++++++++++++++++++++++
[r1]-2110975477
[r2]1838318920
++++++++++++++++++++++++++++++++
  • 测试代码(2)
    2个Random在多线程中使用
new Thread(() ->{Random r1 = new Random();System.out.println("[r1:nanoTime]"+System.nanoTime());for (int i = 0; i < 3; i++) {System.out.println("[r1]"+r1.nextInt());}}).start();new Thread(() ->{Random r2 = new Random();System.out.println("[r2:nanoTime]"+System.nanoTime());for (int i = 0; i < 3; i++) {System.out.println("[r2]"+r2.nextInt());}}).start();
  • 测试结果
    测试了好几次,2个Random对象都没能获取到相同的系统时间;因此结果也都不同;不过必须指出的是,在多线程的环境中使用2个或多个不同的Random对象生成的伪随机数字序列是可能相同的;如果想要确保在多线程环境下,各个线程生成的随机数序列相同,只要使用指定参数的构造方法,指定相同参数即可;

[r1:nanoTime]3745251056800
[r1]2110737154
[r1]-360918307
[r1]-779547580
[r2:nanoTime]3745251443500
[r2]55610651
[r2]503726359
[r2]36689994

Random类生成随机数详解相关推荐

  1. Random()类生成随机数详解

    Random类介绍 生成随机数的两种方法 是用Math类里的Random方法生成0到1内的随机数,返回是double 是用Random类方法生成随机数 Random()函数生成随机数介绍 在Java的 ...

  2. Python中random模块生成随机数详解

    Python中random模块生成随机数详解 本文给大家汇总了一下在Python中random模块中最常用的生成随机数的方法,有需要的小伙伴可以参考下 Python中的random模块用于生成随机数. ...

  3. python中产生随机数模块_Python中random模块生成随机数详解

    Python中的random模块用于生成随机数.下面介绍一下random模块中最常用的几个函数. random.random random.random()用于生成一个0到1的随机符点数: 0 < ...

  4. python中sn的意思_Python中random模块生成随机数详解

    Python中的random模块用于生成随机数.下面介绍一下random模块中最常用的几个函数. random.random random.random()用于生成一个0到1的随机符点数: 0 < ...

  5. python中random模块中包含了随机数相关的功能函数_Python中random模块生成随机数详解...

    print random.randint(12, 20) #生成的随机数n: 12 <= n <= 20 print random.randint(20, 20) #结果永远是20 #pr ...

  6. Java学习笔记【10】常用类 - - Math类、Random类及随机数详解

       ⛵ ⛵ ⛵ ⛵ ⛵

  7. VB.NET学习笔记:使用Random类生成随机数(不重复、数字、字母)

    VB6.0升级到VB.NET后,发现随机数函数也发生了变化,在VB.NET中Random类是一种能够产生满足某些随机性统计需求的数字序列的伪随机数生成器. 在代码把光标定位到单词Random,点F1键 ...

  8. java random函数用法_JAVA的Random类的用法详解

    Random类主要用来生成随机数,本文详解介绍了Random类的用法,希望能帮到大家. Random类 (java.util) Random类中实现的随机算法是伪随机,也就是有规则的随机.在进行随机时 ...

  9. c++生成随机数详解(包含可执行代码)

    1.rand c++中生成随机数最简单的方式就是rand函数.而且rand函数位于stdlib.h头文件中,不需要额外的再引入. int rand(void) __swift_unavailable( ...

最新文章

  1. ros 配置udev
  2. 解决Windows下栈内存过小的问题
  3. python的excel库_Python-Excel 模块哪家强?
  4. 压力大根源不在教育本身
  5. Atitit SpringCache缓存使用 艾提拉 attilax总结 1. Spring的抽象已经做得够好了,适合于大多数场景,非常复杂的就需要自己AOP实现了。 1 1.1. 设置配置文件支持
  6. 美丽的回测 —— 教你定量计算过拟合概率
  7. PLC编程软件等工具打包下载1.0【好用绿色三菱plc编程软件】
  8. Linux系统安装教程之一:VM14虚拟机+Ubuntu16安装
  9. 我对“硬盘分区”的愚见
  10. 七日年化收益率计算器_定投收益率该怎么算?
  11. cvpr论文阅读之Deep Spatio-Temporal Random Fields for Efficient Video Segmentation(用于视频分割的深度时空随机场)
  12. 点击查看详情显示更多布局
  13. 方案分享丨基于海思Hi3519智能 IP 摄像机解决方案
  14. Python基础实战之文字游戏——模拟武侠类场景中的两派战斗场面
  15. 牛客练习赛41:球的体积并【球缺】
  16. 跨境电商:独立站如何品牌化运营?
  17. 微生物群落基于KEGG预测功能的丰度分布图绘制
  18. 伍迷随想冷饭集 之 北国冬天之随想
  19. 【20220926】html综合案例世纪佳缘
  20. RTSP 协议详细介绍

热门文章

  1. 深入浅出学习透析Nginx服务器的基本原理和配置指南「进阶实践篇」
  2. jenkins构建自动化脚本遇到的问题及解决方式
  3. 全流程重构京东服务市场系统
  4. PHP phar详解
  5. 追风筝的人 第十一章
  6. 帝国cms建立自定义页面来创建网站地图sitemap.html
  7. 【FAQ】应用内支付服务无法拉起支付页面常见原因分析和解决方法
  8. 科普文章|一文读懂Moonbeam收集人及质押机制
  9. regex主要规则与学习笔记
  10. cere学习一(安装)