ThreadLocalRandom ---- 提升Random在大并发下的效率
本博客系列是学习并发编程过程中的记录总结。由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅。
随机数
随机数在科学研究与工程实际中有着极其重要的应用!
简单来说,随机数就是一个数列,这个数列可能满足一定的概率分布,又获取其满足的分布并不为我们所知。
数学方法产生随机数应该称之为“伪随机数”,只有使用物理方法才能得到真正的随机数!因此我们使用计算机产生的随机数都是"伪随机数"。那么计算机到底是怎么产生随机数的呢?这时就要提到随机数发生器了。
随机数发生器
我们高中的时候都学过数列的知识,上面提到随机数可以看成是一个数列,那么我们可以将随机数发生器看成是一个数列表达式。比如现在有下面两个随机说发生器
//发生器1
X(n+1)= a * X(n) + b
//发生器2
X(n+1)= a * X(n)
当然还有很多随机数发生器,现实生产中使用的发生器也并不是像上面的那么简单,这边只是为了说明随机数发生器到底是什么列了两个例子。
随机数种子
我们在产生随机数的时候经常会听到随机数种子这个名词,那随机数种子到底是什么?我们还是以上面的发生器为例。
//发生器1
X(n+1)= a * X(n) + b
显然通过上式我们能够得到一个数列,前提是X(0)应该给出,依次我们就可以算出X(1),X(2)...;当然不同的X(0)就会得到不同的数列。
可以说X(0)的值就是随机数的种子,只要这个种子给的一样,产生的随机数序列就是一样的。下面给出一个使用Java中Random产生随机数的列子证明下这个说法。
Random random1 = new Random(100);
for (int i = 0; i < 10 ; i++) {
System.out.println(random1.nextInt(5));
}
System.out.println("-------------");
Random random2 = new Random(100);
for (int i = 0; i < 10 ; i++) {
System.out.println(random2.nextInt(5));
}
执行结果如下:
0
0
4
3
1
1
1
3
3
3
-------------
0
0
4
3
1
1
1
3
3
3
--------------
上面代码中新建了两个随机数发生器,都设置了同样的随机数种子100,产生10个随机数。从上面的结果中可以看出两个发生器产生的序列是一样的。
对于一个应用级的伪随机数发生器,所有的“伪随机数”,均匀的分布于一个“轨道”上,几乎所有的数都可以做为种子。数字“0”,有时是一个特例,不能作为种子,当然它取决于你使用的随机数发生器!
Random类的局限性
Random类是JDK提供的一个随机数发生器。 我们看下Random类中nextInt方法的源代码:
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;
}
那我们看下上面的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机制更新种子的值,保证多线程竞争的情况下只有一个能更新成功。最后使用固定算法根据新的种子计算随机数。
每个Random实例里面都有一个原子性的种子变量用来记录当前的种子值,当要生成新的随机数时需要根据当前种子计算新的种子并更新回原子变量。在多线程下使用单个Random实例生成随机数时,当多个线程同时计算随机数来计算新的种子时,多个线程会竞争同一个原子变量的更新操作,由于原子变量的更新是CAS操作,同时只有一个线程会成功,所以会造成大量线程进行自旋重试,这会降低并发性能。
分析到这里我们可以看出Random的局限性并不是线程安全的问题,而是在大量线程并发的时候,通过CAS机制更新随机数种子会导致大量线程自旋,耗费CPU性能,导致系统吞吐量下降。
ThreadLocalRandom
ThreadLocalRandom类是JDK 7在JUC包下新增的随机数生成器,它弥补了Random类在多线程下的缺陷。下面来分析下ThreadLocalRandom的实现原理。
从名字上看它会让我们联想到ThreadLocal。ThreadLocal通过让每一个线程复制一份变量,使得在每个线程对变量进行操作时实际是操作自己本地内存里面的副本,从而避免了对共享变量进行同步。
实际上ThreadLocalRandom的实现也是这个原理,Random的缺点是多个线程会使用同一个原子性种子变量,从而导致对原子变量更新的竞争。

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

ThreadLocalRandom提升性能的原理就是这样的。具体的源代码也比较简单,这边就不贴代码了。感兴趣的可以自己看下。下面贴下ThreadLocalRandom的简单使用方法
ThreadLocalRandom random = ThreadLocalRandom.current();
random.nextInt();
参考
- 《Java并发编程之美》
ThreadLocalRandom ---- 提升Random在大并发下的效率的更多相关文章
- Random在高并发下的缺陷以及JUC对其的优化
Random可以说是每个开发都知道,而且都用的很6的类,如果你说,你没有用过Random,也不知道Random是什么鬼,那么你也不会来到这个技术类型的社区,也看不到我的博客了.但并不是每个人都知道Ra ...
- .net的页面在大并发下出现503错误
.net的页面在大并发下偶尔出现503错误 我们开发了一个回调页面,由一个工具负责调用,由于压力非常大,回调页面通过6台服务器负载均衡的: 最近业务系统又再次扩容,回调页面压力成倍增加,在高峰时间段偶 ...
- Atitit 提升开发进度大方法--高频功能与步骤的优化 类似性能优化
Atitit 提升开发进度大方法--高频功能与步骤的优化 类似性能优化 1. 通用功能又可以组合成crud模块1 1.1. 查询(包括步骤,发送查询dsl,通讯返回结果,绑定到表格控件)2 1.2. ...
- Atitit 提升进度的大原则与方法 高层方法 attilax总结
Atitit 提升进度的大原则与方法 高层方法 attilax总结 生产力的提升点 1.1. 管理,管理的发展发展非常缓慢,1 1.2. 方法论(前后分离,dsl等)1 1.3. 工具( 工具链 ...
- PHP使用redis防止大并发下二次写入(如如何防止重复下订单)
php调用redis进去读写操作,大并发下会出现:读取key1,没有内容则写入内容,但是大并发下会出现同时多个php进程写入的情况,这个时候需要加一个锁,即获取锁的php进程有权限写. $lock_k ...
- 大并发下TCP内存消耗优化小记(86万并发业务正常服务)
转自:http://blog.csdn.net/u010954257/article/details/54178160 最近在做一个大并发服务的测试(目前测到86万,当然有大量长连接,每天打的日志高到 ...
- PHP使用redis防止大并发下二次写入
php调用redis进去读写操作,大并发下会出现:读取key1,没有内容则写入内容,但是大并发下会出现同时多个php进程写入的情况,这个时候需要加一个锁,即获取锁的php进程有权限写. $lock_k ...
- 利用job提升马哈鱼数据血缘分析效率
利用job提升马哈鱼数据血缘分析效率 一.Job基本知识 前面文章中已介绍马哈鱼的基本功能,其中一个是job,job其实是一个任务集合处理的概念,就是让用户通过job,可以一次递交所有需要处理的 SQ ...
- 为什么要使用ThreadLocalRandom代替Random生成随机数
799 java里有伪随机型和安全型两种随机数生成器,伪随机生成器根据特定公式将seed转换成新的伪随机数据的一部分,安全随机生成器在底层依赖到操作系统提供的随机事件来生成数据. 安全随机生成器 需要 ...
随机推荐
- $CF949D\ Curfew$ 二分/贪心
正解:二分/贪心 解题报告: 传送门$QwQ$ 首先这里是二分还是蛮显然的?考虑二分那个最大值,然后先保证一个老师是合法的再看另一个老师那里是否合法就成$QwQ$. 发现不太会搞这个合不合法的所以咕了 ...
- $[NOIp2017]$ 逛公园 $dp$/记搜
\(Des\) 给定一个有向图,起点为\(1\),终点为\(n\),求和最短路相差不超过\(k\)的路径数量.有\(0\)边.如果有无数条,则输出\(-1\). \(n\leq 10^5,k\leq ...
- Spring Boot 配置文件中使用变量、使用随机数
参数引用 在application.properties中的各个参数之间可以直接通过是使用placeHolder的方式进行引用,如: book.author=Clark book.name=C++ b ...
- Head First设计模式——组合模式
最近比较忙,有段时间没有更新设计模式的进度了.今天继续学习组合设计模式. 组合模式的例子我们继续延续上篇<Head First设计模式——迭代器模式>的菜单例子,首先声明下迭代器和组合模式 ...
- 洛谷P2602 [ZJOI2010]数字计数 题解 数位DP
题目链接:https://www.luogu.com.cn/problem/P2602 题目大意: 计算区间 \([L,R]\) 范围内 \(0 \sim 9\) 各出现了多少次? 解题思路: 使用 ...
- 极光推送SDK通过泰尔终端实验室检测,符合统一推送接口标准
1月7日,中国深圳--国内领先的开发者服务提供商极光(Aurora Mobile, NASDAQ:JG)宣布其旗下产品极光推送SDK通过中国信息通信研究院泰尔终端实验室的检测,其性能和接口标准符合统一 ...
- 交换机广播风暴,STP生成树协议,端口聚合
交换机(工作在数据链路层)具有学习功能: 一台刚重启的交换机上的mac地址表为空,根据数据包的来源,目的地来学习MAC地址与端口的映射关系映射关系,对于MAC地址表之中已有的就不管了,对未知端 ...
- .net core 3.0 搭建 IdentityServer4 验证服务器
叙述 最近在搞 IdentityServer4 API接口认证部分,由于之前没有接触过 IdentityServer4 于是在网上一顿搜搜搜,由于自己技术水平也有限,看了好几篇文章才搞懂,想通过博客 ...
- Java并发-几种常见的锁
这几天在忙着投提前批内推,前面看的好多东西没有总结,正好这两天补上顺带复习一下 synchronized:Java之重型锁
- PHP高级程序员必看知识点:目录大全(不定期更新)
面试题系列: 分享一波腾讯PHP面试题 2019年PHP最新面试题(含答案) Redis 高级面试题 学会这些还怕进不了大厂? 阿里面试官三年经验PHP程序员知识点汇总,学会你就是下一个阿里人! ph ...