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 ...
随机推荐
- 线程不安全(Arraylist,HashSet,HashMap)和写时复制
package com.yangyuanyuan.juc1205; import java.util.List; import java.util.Map; import java.util.Set; ...
- HDU6331 Problem M. Walking Plan【Floyd + 矩阵 + 分块】
HDU6331 Problem M. Walking Plan 题意: 给出一张有\(N\)个点的有向图,有\(q\)次询问,每次询问从\(s\)到\(t\)且最少走\(k\)条边的最短路径是多少 \ ...
- Codeforces ECR86 C. Yet Another Counting Problem(规律,区间)
题意:给你两个正整数a和b,询问q次,每次给你一个区间[l,r],问[l,r]中有多少数字满足:x%a%b!=a%b%a. 题解:看公式无从下手的题,一般都是要找规律的.首先,我们知道,假如x%a%b ...
- HDU2732 Leapin' Lizards 最大流
题目 题意: t组输入,然后地图有n行m列,且n,m<=20.有一个最大跳跃距离d.后面输入一个n行的地图,每一个位置有一个值,代表这个位置的柱子可以经过多少个猴子.之后再输入一个地图'L'代表 ...
- 【Java】位操作符
位运算符 java支持的位运算符有7个,分为两类:位逻辑运算和移位运算.位逻辑运算符包括按位取反(~).按位与(&).按位或(|)和按位异或(^)4种,.移位运算符包括左移(<<) ...
- 数理统计9:完备统计量,指数族,充分完备统计量法,CR不等式
昨天我们给出了统计量是UMVUE的一个必要条件:它是充分统计量的函数,且是无偏估计,但这并非充分条件.如果说一个统计量的无偏估计函数一定是UMVUE,那么它还应当具有完备性的条件,这就是我们今天将探讨 ...
- Java魔法堂:调用外部程序
前言 Java虽然五脏俱全但总有软肋,譬如获取CPU等硬件信息,当然我们可以通过JNI调用C/C++来获取,但对于对C/C++和Windows API不熟的码农是一系列复杂的学习和踩坑过程.那能不能通 ...
- python xml转excle
<?xml version="1.0" encoding="UTF-8"?> <RECORDS xmlns:xsi="http:// ...
- 无需扫描即可查找和攻击域SQL Server (SPN)
无扫描SQL Server发现简介 当您没有凭据或正在寻找不在域中的SQL Server时,使用各种扫描技术来查找SQL Server可能非常有用.但是,此过程可能很嘈杂,耗时,并且可能由于子网未知, ...
- golang1.16新特性速览
今天是假期最后一天,明天起大家也要陆续复工了.golang1.16也在今天正式发布了. 原定计划是2月1号年前发布的,不过迟到也是golang的老传统了,正好也趁着最后的假期快速预览一下golang1 ...