ThreadLocalRandom类原理剖析

ThreadLocalRandom 类是JDK7在JUC包下新增的随机数生成器,它弥补了Random类在多线程下的缺陷。

Random 类及其局限性

在JDK7之前包括现在,java.util.Random都是使用比较广泛的随机数生成工具类,而且java.lang.Math中的随机数生成也使用的是 java.util.Random的实例。

在这里插入图片描述代码(1)创建一个默认随机数生成器,并使用默认的种子。

代码(2)输出10个在0~5(包含0,不包含5)之间的随机数。随机数的生成需要一个默认的种子,这个种子其实是一个long类型的数字,你可以在创建Random对象时通过构造函数指定,如果不指定则在默认构造函数内部生成一个默认的值。有了默认的种子后,如何生成随机数呢?

在这里插入图片描述
新的随机数的生成需要两个步骤:

  • 首先根据老的种子生成新的种子。
  • 然后根据新的种子来计算新的随机数。

其中步骤(4)我们可以抽象为seed=f(seed),其中f是一个固定的函数,比如seed=f(seed)=a*seed+b。

步骤(5)也可以抽象为g(seed,bound),其中g是一个固定的函数,比如g(seed,bound)=(int)((bound*(long)seed)>>31)。

在单线程情况下每次调用nextInt都是根据老的种子计算出新的种子,这是可以保证随机数产生的随机性的。

但是在多线程下多个线程可能都拿同一个老的种子去执行步骤(4)以计算新的种子,这会导致多个线程产生的新种子是一样的,由于步骤(5)的算法是固定的,所以会导致多个线程产生相同的随机值,这并不是我们想要的。

所以步骤(4)要保证原子性,也就是说当多个线程根据同一个老种子计算新种子时,第一个线程的新种子被计算出来后,第二个线程要丢弃自己老的种子,而使用第一个线程的新种子来计算自己的新种子,依此类推,只有保证了这个,才能保证在多线程下产生的随机数是随机的。

Random函数使用一个原子变量达到了这个效果,在创建Random对象时初始化的种子就被保存到了种子原子变量里面,下面看next()的代码。

在这里插入图片描述
代码(6)获取当前原子变量种子的值。

代码(7)根据当前种子值计算新的种子。

代码(8)使用CAS操作,它使用新的种子去更新老的种子,在多线程下可能多个线程都同时执行到了代码(6),那么可能多个线程拿到的当前种子的值是同一个,然后执行步骤(7)计算的新种子也都是一样的,但是步骤(8)的CAS操作会保证只有一个线程可以更新老的种子为新的,失败的线程会通过循环重新获取更新后的种子作为当前种子去计算老的种子,这就解决了上面提到的问题,保证了随机数的随机性。

代码(9)使用固定算法根据新的种子计算随机数。

每个Random实例里面都有一个原子性的种子变量用来记录当前的种子值,当要生成新的随机数时需要根据当前种子计算新的种子并更新回原子变量。

在多线程下使用单个Random实例生成随机数时,当多个线程同时计算随机数来计算新的种子时,多个线程会竞争同一个原子变量的更新操作,由于原子变量的更新是CAS操作,同时只有一个线程会成功,所以会造成大量线程进行自旋重试,这会降低并发性能,所以ThreadLocalRandom应运而生。

ThreadLocalRandom

为了弥补多线程高并发情况下Random的缺陷,在JUC包下新增了ThreadLocalRandom类。

在这里插入图片描述
代码(10)调用ThreadLocalRandom.current()来获取当前线程的随机数生成器。

下面来分析下ThreadLocalRandom的实现原理。

ThreadLocal通过让每一个线程复制一份变量,使得在每个线程对变量进行操作时实际是操作自己本地内存里面的副本,从而避免了对共享变量进行同步。

实际上ThreadLocalRandom的实现也是这个原理,Random的缺点是多个线程会使用同一个原子性种子变量,从而导致对原子变量更新的竞争。

在这里插入图片描述

如果每个线程都维护一个种子变量,则每个线程生成随机数时都根据自己老的种子计算新的种子,并使用新种子更新老的种子,再根据新种子计算随机数,就不会存在竞争问题了,这会大大提高并发性能。

在这里插入图片描述

源码分析

首先看下ThreadLocalRandom的类图结构。
在这里插入图片描述
可以看出ThreadLocalRandom类继承了Random类并重写了nextInt方法,在ThreadLocalRandom类中并没有使用继承自Random类的原子性种子变量。

在ThreadLocalRandom中并没有存放具体的种子,具体的种子存放在具体的调用线程的threadLocalRandomSeed变量里面。

ThreadLocalRandom类似于ThreadLocal类,就是个工具类。

当线程调用ThreadLocalRandom的current方法时,ThreadLocalRandom负责初始化调用线程的threadLocalRandomSeed变量,也就是初始化种子。

当调用ThreadLocalRandom的nextInt方法时,实际上是获取当前线程的threadLocalRandomSeed变量作为当前种子来计算新的种子,然后更新新的种子到当前线程的threadLocalRandomSeed变量,而后再根据新种子并使用具体算法计算随机数。

这里需要注意的是,threadLocalRandomSeed变量就是Thread类里面的一个普通long变量,它并不是原子性变量。

其实道理很简单,因为这个变量是线程级别的,所以根本不需要使用原子性变量,如果你还是不理解可以思考下ThreadLocal的原理。

其中seeder和probeGenerator是两个原子性变量,在初始化调用线程的种子和探针变量时会用到它们,每个线程只会使用一次。

另外,变量instance是ThreadLocalRandom的一个实例,该变量是static的。

当多线程通过ThreadLocalRandom的current方法获取ThreadLocalRandom的实例时,其实获取的是同一个实例。

但是由于具体的种子是存放在线程里面的,所以在ThreadLocalRandom的实例里面只包含与线程无关的通用算法,所以它是线程安全的。

Unsafe机制

在这里插入图片描述

ThreadLocalRandom current()方法

该方法获取ThreadLocalRandom实例,并初始化调用线程中的threadLocalRandomSeed和threadLocalRandomProbe变量。

在这里插入图片描述
代码(12)中,如果当前线程中threadLocalRandomProbe的变量值为0(默认情况下线程的这个变量值为0),则说明当前线程是第一次调用ThreadLocalRandom的current方法,那么就需要调用locallnit方法计算当前线程的初始化种子变量。

这里为了延迟初始化,在不需要使用随机数功能时就不初始化Thread类中的种子变量,这是一种优化。

代码(13)首先根据probeGenerator计算当前线程中threadLocalRandomProbe的初始化值,然后根据seeder计算当前线程的初始化种子,而后把这两个变量设置到当前线程。

代码(14)返回ThreadLocalRandom的实例。需要注意的是,这个方法是静态方法,多个线程返回的是同一个ThreadLocalRandom实例。

int nextlnt(int bound)方法

计算当前线程的下一个随机数。

在这里插入图片描述如上代码的逻辑步骤与Random相似,我们重点看下nextSeed()方法。

在这里插入图片描述

首先使用r=UNSAFE.getLong(t,SEED)获取当前线程中threadLocalRandomSeed变量的值,然后在种子的基础上累加GAMMA值作为新种子,而后使用UNSAFE的putLong方法把新种子放入当前线程的threadLocalRandomSeed变量中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/301909.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网安面试三十到题(结束)

121 有文件上传了漏洞了,linux下怎么找xx.conf的文件 目录遍历,目录扫描 122 反序列化漏洞原理 ## 你要把别人序列化好的文件进行反序列化进行利用,但是在序列化的过程中,被别人注入了攻击代码、魔 法函数之类的,当你反序列化的时…

花为缘积萨伯爵名表工艺之美,传承卓越

腕表是时间的载体,也是品味的象征。在现代人眼中,它们不仅仅是时间的工具,更是一种艺术形式。在制表工艺的殿堂中,花为缘积萨伯爵名表以其独特的创造力和严谨缜密的要求,创作了一系列典范之作,将技术与美学…

伦敦银1盎司等于多少克?

1盎司的伦敦银大概等于31克,用于衡量伦敦银重量的“盎司”,是国际贵金属市场上专用的计量单位,它的全称是金衡盎司,英文的名字是troy ounce,它与西方日常用于计算重量的单位常衡盎司也不一样,一金衡盎司约等…

DTM分布式事务

DTM分布式事务 从内网看到了关于事务在业务中的讨论,评论区大佬有提及DTM开源项目[https://dtm.pub/],开学开学 基础理论 一、Why DTM ​ 项目产生于实际生产中的问题,涉及订单支付的服务会将所有业务相关逻辑放到一个大的本地事务&#xff…

【性能测试入门】:压力测试概念!

压力测试可以验证软件应用程序的稳定性和可靠性。压力测试的目标是评估软件在极端负载条件下的鲁棒性和错误处理能力,并确保软件在紧急情况下不会崩溃。它甚至可以进行超出软件正常工作条件的测试,并评估软件在极端条件下的工作方式。 在软件工程中&…

简单介绍Java 的内存泄漏

java最明显的一个优势就是它的内存管理机制。你只需简单创建对象,java的垃圾回收机制负责分配和释放内存。然而情况并不像想像的那么简单,因为在Java应用中经常发生内存泄漏。 本教程演示了什么是内存泄漏,为什么会发生内存泄漏以及如何预防…

2024年第十届计算机与技术应用国际会议(ICCTA 2024)即将召开!

​ ​ 2024年第十届计算机与技术应用国际会议(ICCTA 2024) 会议时间:2024年5月15-17日 会议地点:奥地利维也纳 (线上线下会议) 会议官网: Home_ICCTA 2024 | Vienna, Austria 组织单位: 奥地利FH JOANN…

狂拿offer,这12道性能测试面试题你会多少?不要再被挖坑了

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 1、性能测试包含了…

NFS 共享存储实验

一、服务器部署 第一步、安装nfs和rpcbind包 [rootserver ~]# yum install -y nfs-utils rpcbind截图: 第二步、这里选择一个 lvm 挂载点做 NFS 共享目录 [rootserver ~]# df -HT截图: 第三步、修改配置文件 [rootserver ~]# vi /etc/exports /home …

2024农历新年是什么时候?电脑如何设置农历新年提醒

元旦的钟声已经远去,2024年的阳历新年就这样悄无声息地开始了。但对于我们很多人来说,真正的“过年”氛围,还得等到农历新年的到来。那么,今年的农历新年究竟是什么时候呢?答案是2月10日。 每当想到农历新年&#xff…

【docker笔记】Docker容器数据卷

Docker容器数据卷 卷就是目录或者文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过Union File System提供一些用于持续存储或共享数据的特性 卷的设计目的就是数据的持久化,完全独…

Element-Puls Form表单内嵌套el-table表格,根据表格复选框多选或单选动态设置行的验证规则

需求 根据 Table 表格内的复选框来控制当前选中行是否添加必填校验规则 效果图 实现思想 我们需要设置一个 flag 来标识已勾选的行,el-table渲染数据结构是数组对象形式,我们可以在每个对象中手动加如一个标识,例如默认:selected …

密码输入检测 - 华为OD统一考试

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 题目描述 给定用户密码输入流input&#xff0c;输入流中字符 ‘<’ 表示退格&#xff0c;可以清除前一个输入的字符&#xff0c;请你编写程序&#xff0c;输出最终得到的…

fpmarkets盘点成功交易者的十个习惯(一)

在交易中能够盈利一次&#xff0c;fpmarkets认为这种情况100%的交易者都会做到&#xff0c;但是要做到每次交易都能盈利&#xff0c;即使是巴菲特也做到&#xff0c;我们只需要做到整体盈利就可以了&#xff0c;那么如何做到呢&#xff1f;今天fpmarkets就总结一下成功交易者的…

代码随想录算法训练营Day08|344.反转字符串、541. 反转字符串II、卡码网:替换数字、151.翻转字符串里的单词、卡码网:右旋字符串

文章目录 一、344.反转字符串1. 双指针法 二、541. 反转字符串II1. 字符串解法 三、卡码网&#xff1a;替换数字四、151.翻转字符串里的单词1.使用库函数2.自行编写函数3.创建字符数组填充3.双反转移位 五、卡码网&#xff1a;右旋字符串1. 自行编写函数 总结 一、344.反转字符…

【软件测试】白盒测试 / 逻辑覆盖法

《语句覆盖法》 使程序中的每个可执行语句至少执行一次 所有的可执行语句得到执行语句覆盖测试是较弱的一种测试发现错误能力最弱的逻辑覆盖 《判定覆盖法》 使每一个判定获得每一种可能的结果至少一次 每个判定得到真值和假值判断覆盖法满足了语句覆盖&#xff0c;因此比语…

【AI视野·今日Sound 声学论文速览 第四十期】Wed, 3 Jan 2024

AI视野今日CS.Sound 声学论文速览 Wed, 3 Jan 2024 Totally 4 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers Auffusion: Leveraging the Power of Diffusion and Large Language Models for Text-to-Audio Generation Authors Jinlong Xue, Yayue De…

十一、工具盒类(MyQQ)(Qt5 GUI系列)

目录 ​编辑 一、设计需求 二、实现代码 三、代码解析 四、总结 一、设计需求 抽屉效果是软件界面设计中的一种常用形式&#xff0c;可以以一种动态直观的方式在有限大小的界面上扩展出更多的功能。本例要求实现类似 QQ 抽屉效果。 二、实现代码 #include "dialog.…

2024年第二届语言、创新教育与文化交流国际学术会议(CLEC 2024)

2024年第二届语言、创新教育与文化交流国际学术会议(CLEC 2024) 2024 2nd International Conference on Language, Innovative Education and Cultural Communication 为迎接知识经济时代的挑战&#xff0c;创新教育被用来培养学生的创新精神与能力。知识的普遍性使得创新教育…

CSS同时使用背景图和渐变色

CSS同时使用背景图和渐变色 需求代码实现完整写法 需求 一个盒子&#xff0c;在拥有渐变色的前提下还需要同时拥有背景图层 类似如下的效果 代码实现 首先我们按照常规的写css的方式来写 <div class"box"></div>.box{width: 300px;height: 120px;bo…