2.ThreadLocalRandom

Random类及其局限性

一般情况下,我们都会使用java.util.Random来生成随机数(Math.random()也是使用Random实例生成随机数)。

示例

public static void main(String[] args) {Random random = new Random();for (int i = 0; i < 10; i++) {System.out.println(random.nextInt(10));}}

分析

下面以nextInt(int bound) 方法为例来分析Random的源码

public int nextInt(int bound) {//边界检测if (bound <= 0)throw new IllegalArgumentException(BadBound);//获取下一随机数int r = next(31);//(*)此处以特定算法根据r计算出最终结果...return r;
}
protected int next(int bits) {long oldseed, nextseed;AtomicLong seed = this.seed;//CAS操作更新seeddo {oldseed = seed.get();//根据老的种子计算新的种子nextseed = (oldseed * multiplier + addend) & mask;} while (!seed.compareAndSet(oldseed, nextseed));return (int)(nextseed >>> (48 - bits));
}

由此可见,生成新的随机数需要两步:

  • 根据老的种子生成新的种子
  • 由新的种子计算出新的随机数

单线程下每次调用nextInt都会根据老的种子计算出新的种子,可以保证随机性。

但多线程下,不同线程可能拿着同一个老的种子去计算新种子,如果next方法因此返回相同的值的话,由于( * )处的算法是固定的,这会导致不同线程生成相同的随机数,这并非我们想要的。所以next方法使用CAS操作保证每次只有一个线程可以更新老的种子,失败的线程则重新获取,这样就解决了上述问题。

但这样处理仍有一个缺陷:当多个线程同时计算随机数来计算新的种子时,多个线程会竞争同一个原子变量的更新操作,由于该操作为CAS操作,同时只有一个线程会成功,这样会造成大量的自旋重试,导致并发性能降低。而ThreadLocalRandom可以完美解决此问题。

ThreadLocalRandom

每个线程新增种子变量threadLocalRandomSeed,每个线程根据老的种子变量变成新的种子。

image-20230612141448179

ThreadLocalRandom类继承了Random类并重写了nextInt方法,在ThreadLocalRandom没有直接继承Random类的原子性种子变量。

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

ThreadLocalRandom类似在ThreadLocal类,是个工具类。当线程调用ThreadLocalRandom的current方法时,ThreadLocalRandom负责初始化调用线程的threadLocalRandomSeed变量,也就是初始化种子。

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

threadLocalRandomSeed变量就是Thread里的一个普通long变量,不是原子性变量。

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

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

当多线程通过ThreadLocalRandom的current方法获取ThreadLocalRandom实例时,其实获取的是一个实例。具体种子存放在线程里,所有ThreadLocalRandom实例里只包含与线程无关的通用算法,所以是线程安全的。

示例

public static void main(String[] args) {Random random = ThreadLocalRandom.current();for (int i = 0; i < 10; i++) {System.out.println(random.nextInt(10));}}

原理

Random的缺点在于多个线程会使用同一个原子性变量,从而导致对原子变量的竞争;而ThreadLocalRandom保证每个线程都维护一个种子变量,每个线程根据自己老的种子生成新的种子,避免了竞争问题,大大提高了并发性能。

源码分析

Unsafe机制

// Unsafe机制
private static final sun.misc.Unsafe UNSAFE;
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;
static {try {// 获取unsafe实例UNSAFE = sun.misc.Unsafe.getUnsafe();Class<?> tk = Thread.class;// 获取Thread类里threadLocalRandomSeed变量在Thread的偏移量SEED = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSeed"));// 获取Thread类里threadLocalRandomProbe变量在Thread的偏移量PROBE = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomProbe"));// 获取Thread类里threadLocalRandomSecondarySeed变量在Thread的偏移量,这个值在LongAdder中会用到SECONDARY = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSecondarySeed"));} catch (Exception e) {throw new Error(e);}
}

ThreadLocalRandom current()方法

// ThreadLocalRandom current()方法
// 获取ThreadLocalRandom实例,并初始化调用线程中的threadLocalRandomSeed和threadLocalRandomProb变量
static final ThreadLocalRandom instance = new ThreadLocalRandom();
public static ThreadLocalRandom current() {//检测是否初始化过//PROBE为Thread类中threadLocalRandomProb偏移if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)// 说明当前线程是第一次调用ThreadLocalRandom的current方法localInit();// 计算当前线程的初始化种子变量 目的为了延迟初始化,在不需要使用随机数功能时就不初始化Thread类中的种子变量,是一种优化return instance;
}static final void localInit() {int p = probeGenerator.addAndGet(PROBE_INCREMENT);// 计算当前线程中threadLocalRandomProb的初始化值int probe = (p == 0) ? 1 : p; // skip 0long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));// 根据sedder计算当前线程的初始化种子Thread t = Thread.currentThread();//SEED为Thread类中threadLocalRandomSeed内存偏移UNSAFE.putLong(t, SEED, seed);UNSAFE.putInt(t, PROBE, probe);
}

如果线程中第一次调用current()方法,则调用localInit()进行初始化设置当前线程中的threadLocalRandomProb和threadLocalRandomSeed变量

这个是静态方法多次调用的是同一个ThreadLocalRandom实例。

下面来看int nextInt(int bound)方法

public int nextInt(int bound) {if (bound <= 0)throw new IllegalArgumentException(BadBound);//根据当前Thread中的threadLocalRandomSeed变量生成新种子    int r = mix32(nextSeed());//根据新种子和bound计算随机数int m = bound - 1;if ((bound & m) == 0) // power of twor &= m;else { // reject over-represented candidatesfor (int u = r >>> 1;u + m - (r = u % bound) < 0;u = mix32(nextSeed()) >>> 1);}return r;
}final long nextSeed() {Thread t; long r;//生成并存入新种子UNSAFE.putLong(t = Thread.currentThread(), SEED,r = UNSAFE.getLong(t, SEED) + GAMMA);return r;
}

如上,首先调用nextSeed()根据当前Thread中的threadLocalRandomSeed变量生成并存入新种子,然后经过特定算法得出了nextInt的值。

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LED显示屏结构

LED显示屏通常由以下几个主要组成部分构成&#xff1a; LED模块&#xff1a;LED模块是构成LED显示屏的基本单元。它包含多个LED点阵&#xff0c;每个点阵包含红色、绿色和蓝色LED灯珠&#xff0c;通过不同的亮度和颜色组合来呈现图像和视频。LED模块的尺寸和像素密度可以根据需…

训练自己的ChatGPT 语言模型(一).md

0x00 Background 为什么研究这个&#xff1f; ChatGPT在国内外都受到了广泛关注&#xff0c;很多高校、研究机构和企业都计划推出类似的模型。然而&#xff0c;ChatGPT并没有开源&#xff0c;且复现难度非常大&#xff0c;即使到现在&#xff0c;没有任何单位或企业能够完全复…

利用低代码平台实现协同办公,助力企业提升效益

概要&#xff1a;本文介绍了协同办公的作用&#xff0c;以及利用低代码平台实现协同办公的优势。同时也分享了天翎为华晨汽车打造的低代码协同工具&#xff0c;帮助企业提高管理效率&#xff0c;改善运营模式&#xff0c;提升产品质量及生产精益化。展示了咨询库、原料质量录入…

实习工作之定时任务

需求&#xff1a;在当天晚上12点定时将过期数据的状态置为冻结状态 版本一代码实现[相当于是一个死代码&#xff0c;因为不能自动调度] public void updateStatus() throws CommonException, ParseException {String date_str "2023-07-01 00:00:00";Date expireT…

阿里云盘如何实现 大文件 秒上传?

文章目录 Intro极速上传的原因隐私保护 Intro 今天把几个软件上传到阿里云盘进行分享&#xff0c;文件大小将近1GB&#xff0c;按理说上传需要个2~3分钟吧。 之前上传一个压缩包看到上传速度大概是4~5MB/s。 但是我刚到别的软件看了一圈&#xff0c;回来发现文件居然已经上传…

华为云专家出品《从零到一•Python图像处理入门》电子书

《华为云云享.书库》系列电子书来啦&#xff01; 本系列电子书旨在帮助开发者成长&#xff0c;汇聚华为云内外部专家技术精华制作而成。 本书《从零到一•Python图像处理》是该系列电子书第3部。 我们在华为开发者即将到来之际&#xff0c;开放电子书免费下载。 点击下方链接…

AI时代,职场人如何转“危”为“机”

AIGC技术的蓬勃发展正在深刻影响着各行各业&#xff0c;关于AI是否会取代人类工作也引发了职场人的担忧。6月29日&#xff0c;奇富科技邀请祺邻HR发起人孙祺做客直播间&#xff0c;分享职场人在AI技术变革中如何应对挑战与机遇。孙祺认为&#xff0c;AI在当前阶段不会对工作产生…

深入了解 OkHttp 协议:优雅的网络请求框架

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。&#x1f60a; 座右铭&#xff1a;不想…

基于机器学习算法:朴素贝叶斯和SVM 分类-垃圾邮件识别分类系统(含Python工程全源码)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境安装pytesseract注册百度云账号 模块实现1. 数据模块2. 模型构建3. 附加功能 系统测试1. 文字邮件测试准确率2. 网页测试结果 工程源代码下载其它资料下载 前言 本项目采用朴素贝叶斯和支持向量机&#xff08;S…

2023网络安全 -- 正向连接与反向连接

一、正向连接&#xff0c;Linux服务器主动控制windows服务器 1、上传nc到windows服务器上运行 2、以管理员身份运行cmd 3、执行下面命令&#xff0c;监听任意来自8899端口的数据&#xff0c;等待服务器来连接 nc -e cmd -lvvp 8899 4、Linux服务器执行如下命令&#xff0c;i…

STM32模拟I2C获取TCS34725光学颜色传感器数据

STM32模拟I2C获取TCS34725光学颜色传感器数据 TCS34725是RGB三色颜色传感器&#xff0c;和TCS34727都属于TCS3472系列&#xff0c;在电气特性上略有差别&#xff0c;TCS34727相比TCS34725在I2C总线的访问电平上可以更低&#xff0c;而在I2C软件访问地址方面则一致。 TCS3472内…

一文了解云计算

目录 &#x1f34e;云服务 &#x1f34e;云计算类型 &#x1f352;公有云 &#x1f352;私有云 &#x1f352;混合云 &#x1f34e;云计算服务模式 &#x1f352;IaaS基础设施即服务 &#x1f352;PaaS平台即服务 &#x1f352;SaaS软件即服务 &#x1f352;三者之间区别 &…