JUC-ThreadLocalRandom
这个类是在JDK7中新增的随机数生成器,它弥补了Random类在多线程下的缺陷。
Radndom类的局限性
在JDK7之前包括现在java.util.Random都是使用比较广泛的随机数生成工具。为什么说它在多线程中有缺陷,看下面一个例子:
public class RandomTest {
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;
}
由此可见生成随机数需要两步:
- 首先根据老的种子来生成新的种子
- 然后根据新的种子来计算新的随机数
但是在多线程环境中,有可能多个线程同时拿到同一个老种子来计算新种子,这样多线程会产生相同值得随机数。
要解决这个问题,首先我们得保证原子性,也就是说当多个线程去拿老种子的时候,第一个线程的新种子被计算出来后,第二个线程要丢弃自己的老种子,使用第一个线程的新种子来计算自己的新种子。
Random函数使用了一个原子变量达到了这个效果。
private final AtomicLong seed;
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));
}
while (!seed.compareAndSet(oldseed, nextseed))这一步使用CAS操作,它使用新种子来更新老种子,但是可能有多个线程同时拿到了老种子,然后根据nextseed = (oldseed * multiplier + addend) & mask;算出来的新种子也是一样的。但是在while操作中由于是CAS操作那么只会有一个线程更新种子成功,失败的线程会通过循环重新去获取更新过后的种子,这样通过原子变量和CAS操作就解决了上诉问题。保证了随机数的随机性。但是我们都知道CAS操作在多线程中必然会造成自旋重试,这将会降低并发性能,所以ThreadLocalRandom应运而生。
ThreadLocalRandom
public class ThreadLocalRandomTest {
static void random(){
ThreadLocalRandom random=ThreadLocalRandom.current();
for (int i = 0; i <10 ; i++) {
System.out.println(random.nextInt(5));
}
System.out.println("--------------");
}
public static void main(String[] args) throws Exception{
Thread thread1=new Thread(() -> {
random();
});
Thread thread2=new Thread(() -> {
random();
});
Thread thread3=new Thread(() -> {
random();
});
Thread thread4=new Thread(() -> {
random();
});
thread1.start();
Thread.sleep(3000);
thread2.start();
Thread.sleep(3000);
thread3.start();
Thread.sleep(3000);
thread4.start();
}
}
其实从名字上我们可以联想到ThreadLocal这个类,实际上这个类也是这个原理,Random的缺点是多个线程会使用同一个原子变量,从而导致对原子变量的更新竞争,导致大量的自旋重试。
那么我们可以让每一个线程维护一个种子变量,每个线程生成随机数的时候根据自己老的种子来计算新的种子。就不会存在竞争问题了,这会大大提高并发性能。
JUC-ThreadLocalRandom的更多相关文章
- JUC源码分析-其它工具类(一)ThreadLocalRandom
JUC源码分析-其它工具类(一)ThreadLocalRandom ThreadLocalRandom 是 JDK7 在 JUC 包下新增的随机数生成器,它解决了 Random 在多线程下多个线程竞争 ...
- Random在高并发下的缺陷以及JUC对其的优化
Random可以说是每个开发都知道,而且都用的很6的类,如果你说,你没有用过Random,也不知道Random是什么鬼,那么你也不会来到这个技术类型的社区,也看不到我的博客了.但并不是每个人都知道Ra ...
- Java并发编程笔记之ThreadLocalRandom源码分析
JDK 并发包中 ThreadLocalRandom 类原理剖析,经常使用的随机数生成器 Random 类的原理是什么?及其局限性是什么?ThreadLocalRandom 是如何利用 ThreadL ...
- JUC包中的分而治之策略-为提高性能而生
一.前言 本次分享我们来共同探讨JUC包中一些有意思的类,包含AtomicLong & LongAdder,ThreadLocalRandom原理. 二.AtomicLong & Lo ...
- ThreadLocalRandom原理
原文链接:https://www.jianshu.com/p/9c2198586f9b 2.2. 并发包中ThreadLocalRandom类原理剖析 ThreadLocalRandom类是JDK7在 ...
- JUC源码分析-集合篇(十)LinkedTransferQueue
JUC源码分析-集合篇(十)LinkedTransferQueue LinkedTransferQueue(LTQ) 相比 BlockingQueue 更进一步,生产者会一直阻塞直到所添加到队列的元素 ...
- JUC源码分析-集合篇(一)ConcurrentHashMap
JUC源码分析-集合篇(一)ConcurrentHashMap 1. 概述 <HashMap 源码详细分析(JDK1.8)>:https://segmentfault.com/a/1190 ...
- JUC中的原子操作类及其原理
昨天简单的看了看Unsafe的使用,今天我们看看JUC中的原子类是怎么使用Unsafe的,以及分析一下其中的原理! 一.简单使用AtomicLong 还记的上一篇博客中我们使用了volatile关键字 ...
- ThreadLocalRandom ---- 提升Random在大并发下的效率
本博客系列是学习并发编程过程中的记录总结.由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅. 并发编程系列博客传送门 随机数 随机数在科学研究与工程实际中有着极其重要的应用! ...
- JUC——检视阅读
JUC--检视阅读 参考资料 JUC知识图参考 JUC框架学习顺序参考 J.U.C学习总结参考,简洁直观 易百并发编程,实践操作1,不推荐阅读,不及格 JUC文章,带例子讲解,可以学习2 Doug L ...
随机推荐
- Gym 2009-2010 ACM ICPC Southwestern European Regional Programming Contest (SWERC 2009) A. Trick or Treat (三分)
题意:在二维坐标轴上给你一堆点,在x轴上找一个点,使得该点到其他点的最大距离最小. 题解:随便找几个点画个图,不难发现,答案具有凹凸性,有极小值,所以我们直接三分来找即可. 代码: int n; lo ...
- CQRS Event Sourcing介绍
什么是CQRS模式? CQRS是Command and Query Responsibility Segregation的缩写,直译就是命令与查询责任分离的意思. 命令会改变对象的状态,但不返回任何数 ...
- 排序算法 以及HKU的一些数据结构 相关题目 以及 K叉树,二叉树 排列
冒泡排序.选择排序.快速排序.插入排序.希尔排序.归并排序.基数排序以及堆排序,桶排序 https://www.cnblogs.com/Glory-D/p/7884525.html https://b ...
- 关于HashMap遍历,为什么要用entry
Map.entrySet() 这个方法返回的是一个Set<Map.Entry<K,V>>,Map.Entry 是Map中的一个接口,他的用途是表示一个映射项(里面有Key和Va ...
- HDU 6395 Sequence(分段矩阵快速幂)题解
题意: 已知\(A,B,C,D,P,n\)以及 \[\left\{ \begin{aligned} & F_1 = A \\ & F_2 = B\\ & F_n = C*F_{ ...
- msf 信息收集
MSF信息收集 转载自天堂空气 一 MSF主机发现 0x1:搜索arp 0x2:使用use auxiliary/scanner/discovery/arp_sweep 模块,然后show option ...
- npm config set registry
npm config set registry $ npm config set registry $ npm config set registry https://registry.your-re ...
- Github access token
Github access token https://github.com/settings/tokens https://docs.github.com/en/free-pro-team@late ...
- angular-2-tutorial-2017
# angular-2-tutorial-2017https://www.sitepoint.com/understanding-component-architecture-angular/http ...
- ADN vs CDN All In One
ADN vs CDN All In One Netlify & JAMstack https://app.netlify.com/teams/xgqfrms/sites ADN Applica ...