java.util.Random 实现原理
概述
该类的实例被用于生成伪随机数的流。该类使用一个 48 位的种子,它被一个线性同余公式所修改。如果 Random 的两个实例用同一种子创建,对每个实例完成同方法调用序列它们将生成和返回相同的数序列成同一方法调用序列,它们将生成和返回相同的数序列。
示例
public class RandomTest {public static void main(String[] args) {testRandom();System.out.println("---------------------");testRandom();System.out.println("---------------------");testRandom();}public static void testRandom(){Random random = new Random(1);for(int i=0; i<5; i++){System.out.print(random.nextInt()+"\t");}System.out.println("");}
}
输出结果:
从结果中发现,只要种子一样,获取的随机数的序列就是一致的。是一种伪随机数的实现,而不是真正的随机数。
Random 源码分析
Random 类结构
class Random implements java.io.Serializable {private final AtomicLong seed;private static final long multiplier = 0x5DEECE66DL;private static final long addend = 0xBL;private static final long mask = (1L << 48) - 1;private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L);
有参构造方法
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;
}
通过传入一个种子,来生成随机数,通过上面的例子发现,种子一样产生的随机数序列一样,如果每次使用想产生不一样的序列,那就只能每次传入一个不一样的种子。
无参构造方法
public Random() {this(seedUniquifier() ^ System.nanoTime());}
private static long seedUniquifier() {// L'Ecuyer, "Tables of Linear Congruential Generators of// Different Sizes and Good Lattice Structure", 1999for (;;) {long current = seedUniquifier.get();long next = current * 181783497276652981L;if (seedUniquifier.compareAndSet(current, next))return next;}
}
通过源码发现,无参的构造方法,里面帮我们自动产生了一个种子,并通过CAS自旋方式保证,每次获取的种子不一样,从而保证每次new Random()获取的随机序列不一致。
nextInt() 方法:获取 int 随机数
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));return (int)(nextseed >>> (48 - bits));
}
从代码中我们可以发现,只要种子确定后,每次产生的数,都是采用固定的算法进行产生的,所以只要种子确定后,每次产生的序列就是固定的。
每次更新种子的时候是使用的CAS来更新的,如果高并发的环境下,性能是个问题。
安全性问题
试想下,如果这是一个摇奖平台,只要种子确定后,每次产生的序列都一样。这样就可利用这个漏洞来预测下一次开奖的号码,这样容易被一些人钻空子。
jdk建议大家尽量要使用 SecureRandom 来实现随机数的生成。
SecureRandom
SecureRandom是强随机数生成器,主要应用的场景为:用于安全目的的数据数,例如生成秘钥或者会话标示(session ID),在上文《伪随机数安全性》中,已经给大家揭露了弱随机数生成器的安全问题,而使用SecureRandom这样的强随机数生成器将会极大的降低出问题的风险。
产生高强度的随机数,有两个重要的因素:种子和算法。算法是可以有很多的,通常如何选择种子是非常关键的因素。 如Random,它的种子是System.currentTimeMillis(),所以它的随机数都是可预测的, 是弱伪随机数。
强伪随机数的生成思路:收集计算机的各种信息,键盘输入时间,内存使用状态,硬盘空闲空间,IO延时,进程数量,线程数量等信息,CPU时钟,来得到一个近似随机的种子,主要是达到不可预测性。
说的简单点就是,使用加密算法生成很长的一个随机种子,让你无法猜测出种子,也就无法推导出随机序列数。
Random性能问题
从 Random 源码中我们发现,每次获取随机数的时候都是使用CAS的方式进行更新种子的值。这样在高并发的环境中会存在大量的CAS重试,导致性能下降。这时建议大家使用ThreadLocalRandom类来实现随机数的生成。
ThreadLocalRandom 实现原理
Thread 类
Thread 类中有一个 threadLocalRandomSeed 属性。
ThreadLocalRandom 结构
SEED 变量是 threadLocalRandomSeed 在 Thread 对象中的偏移量。
ThreadLocalRandom.nextSeed() 方法
从这个方法中,我们发现,每个线程的种子值都存储在Thread对象的threadLocalRandomSeed 属性中。
结论
因为ThreadLocalRandom 中的种子存储在Thread对象中,所以高并发获取Random对象时,不会使用CAS来保证每次获取的值不一致。
每个线程维护一个它自己的种子,每个线程需要获取随机数的时候,从当前的Thread对象中获取当前线程的种子,进行获取随机数,性能大大提高。
本人简书blog地址:http://www.jianshu.com/u/1f0067e24ff8
点击这里快速进入简书
GIT地址:http://git.oschina.net/brucekankan/
点击这里快速进入GIT
java.util.Random 实现原理相关推荐
- Java.util.Random 各种方法介绍
Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法.它可以产生int.long.float.double以及Goussian等类型的随机数.这也是它与java.l ...
- 随机加解密java_JAVA随机数生成 Math.random和java.util.Random使用简介
一.Math.random 1Math.random内部使用java.util.Random实现 2 直接调用Math.random是产生一个[0,1)之间的随机数 public static voi ...
- Java 8中的java.util.Random
Java 8中java.util.Random类的简洁功能之一是对其进行了改进,现在可以返回随机的数字流 . 例如,要生成一个介于0(含)和1(不含)之间的随机双精度数的无限流: Random ran ...
- java nextgaussian(),java.util.Random.nextGaussian()
描述 所述nextGaussian()方法用于从该随机数生成器的序列平均值是0.0,标准偏差1.0,以获得下一个伪高斯("正常")分布的双精度值. 声明 以下是java.util. ...
- java.util.Random 类的 nextInt(int num )
随机产生3个67~295的整数并找出数值居中的数 并输出中间的数 例如:100,225和200,输出200 要随机产生某个范围内的整数,用 java.util.Random 类的 nextInt(in ...
- Java 随机数生成器 Random SecureRandom 原理分析
文章目录 java.util.Random java.Security.SecureRandom /dev/random 与 /dev/urandom 资料 Java 里提供了一些用于生成随机数的工具 ...
- java random.nextbyte_java.util.Random.next()方法实例
全屏 next(int bits)方法用于生成下一个伪随机数. 声明 以下是java.util.Random.next()方法的声明.protected int next(int bits) 参数bi ...
- java random函数原理_JAVA Random 详解
Java中存在着两种Random函数: 一.java.lang.Math.Random; 调用这个Math.Random()函数能够返回带正号的double值,该值大于等于0.0且小于1.0,即取值范 ...
- java.util.concurrent包API学习笔记
newFixedThreadPool 创建一个固定大小的线程池. shutdown():用于关闭启动线程,如果不调用该语句,jvm不会关闭. awaitTermination():用于等待子线程结束, ...
最新文章
- 退出出库复核是什么意思_干货 | 电商仓储与传统仓储有什么不同?
- openSuse 13.1 的触摸板,回来了
- 固定顶部指定div不滑动
- C语言深度剖析书籍学习记录 第六章 函数
- java对象组合_java并发编程(三): 对象的组合
- python 函数参数多个逗号不报错_python笔记7-多线程threading之函数式
- Python实现定时自动关闭的tkinter窗口
- 基于队列的迷宫求解实现
- 小程序源码:首席省钱赚钱专家微信小程序源码下载,淘宝客 外卖侠 外卖cps -多玩法安装简单
- C# ZPL打印标签
- 抖音康辉机器人_央视主持人太会玩!康辉录抖音,笑死了!
- 模COMSOL Multiphysics v5.3 Win64 Linux64 MacOSX64 1DVD
- 优动漫PAINT提高创作效率的小技巧——中间色与近似色
- Github优秀Android开源项目,值得引用与学习(注意!里面有巨图! )
- [技巧]如何让Opera 在浏览淘宝时调用阿里旺旺聊天
- html css制作简单优惠卷
- 中继器、集线器;网桥、交换机;路由器及网关之间的区别
- 【李佳辉_周报_2022.10.16】
- matlab数表数据类型转换,[转载]Matlab 数据类型 五、表
- 踩坑记--Your connection to this site is not secure