Java并发编程ThreadLocalRandom类详解
作者:??派大大大星???? 发布时间:2021-07-30 17:56:03
为什么需要ThreadLocalRandom
java.util.Random一直都是使用比较广泛的随机数生成工具类,而且java.lang.Math中的随机数生成也是使用的java.util.Random实例。
我们下面看一下java.util.Random的使用方法:
import java.util.Random;
public class code_4_threadRandom {
public static void main(String[] args) {
Random random = new Random();
for(int i = 0; i < 10; i++) {
System.out.println(
random.nextInt(5)
);
}
}
}
随机数的生成需要一个默认的种子,这个种子是一个long类型的数字,这可以通过创建Random对象时通过构造函数指定,如果不指定则在默认构造函数内部生成一个默认值。
public int nextInt(int bound) {
//参数检查
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
//根据老的种子生成新的种子
int r = next(31);
int m = bound - 1;
if ((bound & m) == 0) // i.e., bound is a power of 2
//根据新种子生成新的随机数
r = (int)((bound * (long)r) >> 31);
else {
for (int u = r;
u - (r = u % bound) + m < 0;
u = next(31);
}
return r;
}
由上面代码可见,一个新的随机数生成需要两个步骤:首先根据老的种子生成新的种子,然后根据新的种子来计算新的随机数。如果在单线程的情况下每次调用nextInt都是根据老的种子计算出新的种子。但是在多线程下多个线程都可能都拿到同一个老的种子去生成新种子,这回导致多个线程生成的新随机数是相同的。我们需要当多个线程通过同一个老种子计算新种子时,当第一个线程的新种子被计算出来后,第二个线程要丢弃掉老种子,用第一个线程计算出的新种子来计算自己的新种子。在Random类中,对象初始化时的种子就被保存到了种子原子变量里。
下面看一下next()的代码:
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操作来更新种子,在多线程情况下,多个线程同时计算随机数来计算新的种子,多个线程会竞争同一个原子变量的更新操作,会造成大量线程进行自旋重试,降低并发性能。所以ThreadLocalRandom应运而生。
ThreadRandom原理详解
import java.util.Random;
public class code_4_threadLocalRandom {
public static void main(String[] args) {
Random random = new ThreadLocalRandom.current();
for(int i = 0; i < 10; i++) {
System.out.println(
random1.nextInt(5)
);
}
}
}
如果每个线程都维护一个种子变量,则每个线程生成随机数时都根据自己老的种子计算新的种子,并使用新的种子更新老种子,再根据新种子计算随机数,这就不会存在竞争问题了。ThreadLocalRandom 类 继 承 了 Random 类 并 重 写 了 nextlnt方法,在 ThreadLocalRandom 类中并没有使用继承自Random 类的原子性种子变量。
在ThreadLocalRandom中并没有存放具体的种子,具体的种子存放在具体的调用线程的 threadLocalRandomSeed 变量里面。ThreadLocalRandom 类似于 ThreadLocal 类,就是个工具类。当线程调用 ThreadLocalRandom的current 方法时,ThreadLocalRandom 负责初始化调用线程的threadLocalRandomSeed 变量,也就是初始化种子。当 调 用 ThreadLocalRandom 的 nextInt 方 法 时, 实际 上 是 获 取 当前 线 程的threadLocalRandomSeed 变量作为当前种子来计算新的种子,然后更新新的种子到当前线程的threadLocalRandomSeed 变量,而后再根据新种子并使用具体算法计算随机数。这里需要注意的是,threadLocalRandomSeed 变量就是 Thread 类里面的一个普通 long 变量,它并不是原子性变量。其实道理很简单,因为这个变量是线程级别的,所以根本不需要使用原子性变量。
变量instance是ThreadLocalRandom的一个实例,该变量是static的。当多线程通过ThreadLocalRandom的current方法获取ThreadLocalRandom的实例时,其实是同一个实例。但是由于具体的种子是存放在线程里面的,所以在ThreadLocalRandom的实例里面只包含与线程无关的通用算法,所以它是线程安全的。
来源:https://juejin.cn/post/7030731161580077086
猜你喜欢
- # 前言在我们实际项目开发过程中,我们经常需要将不同的两个对象实例进行属性复制,从而基于源对象的属性信息进行后续操作,而不改变源对象的属性信
- 1、在java的构造方法中提供了 异常链.. 也就是我们可以通过构造方法不断的将 异常串联成一个异常链... 之所以需
- 本文实例为大家分享了C语言实现贪吃蛇游戏的具体代码,供大家参考,具体内容如下IDE用的是 VS2019先看效果 代码全览game.
- 一、注解注解(Annotation): 从jdk5.0开始引进,可以对程序进行解释或被其他程序读取。注解格式:"@注释名"
- java实现接口签名为了保证数据传输的安全性,跟其他系统进行数据交互时,双方应该约定好密钥,把数据进行加密,接口签名,这样双方调用接口时,验
- 本文主要是用到java中的swing技术,以及JMFjar中的API,为大家分享了java音乐播放器的具体实现代码,供大家参考,具体内容如下
- 一、题目描述题目实现:网络通信,实现信息的发送和接收。二、解题思路创建一个服务器类:ServerSocketFrame,继承JFrame类写
- 使用mybatis-plus自动生成了5个模块(xml/bean/mapper/service/controller)的代码,这里练习一下m
- Struts2简介Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts
- 一、API简介Thread.sleep()是Thread类的一个静态方法,使当前线程休眠,进入阻塞状态(暂停执行),如果线程在睡眠状态被中断
- 跨域问题,其实百度上面有一堆的解决方案针对普通的情况其实百度上面的方案都是可行的。我这里主要介绍2种情况。当然我这里的配置都是基于网关的,而
- 前言本文主要给大家介绍的是关于Java对xls文件进行读写操作的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍:wi
- 一、reservedcodecachesize参数介绍该参数是JvM虚拟机调优中调整内存大小的一个设置参数,值得大小设置直接影响到Code
- 前面讲了 Spock框架Mock对象方法经验总结一、静态方法Mock静态方法我们使用PowerMock结合Mockito的方案,首先在测试类
- 最近项目中使用springboot+jwt实现登录权限控制,所以在这里记录一下防止以后忘记,毕竟好记性不如烂笔头嘛~。首先我们需要导入使用到
- 前几天在看一个cameraCTSbug时,结果在一个java for循环上有点蒙。正好赶上这个点总结一下。java中的控制结构:条件结构这里
- java常量池技术java中常量池技术说的通俗点就是java级别的缓存技术,方便快捷的创建一个对象。当需要一个对象时,从池中去获取(如果池中
- 本文实例为大家分享了java使用influxDB数据库的具体代码,供大家参考,具体内容如下1.pom.xml中导入jar包依赖<!--
- Java是一门天然的面向对象的语言。而所有我们手动创造出来的类,都继承于同一个类,即Object类。可以看一下Object类的结构nativ
- 1 简介之前我们在文章《K8ssandra入门-详细记录在Linux上部署K8ssandra到Kubernetes》成功地在Ubuntu上安