当前位置:网站首页>并发高的情况下,试试用ThreadLocalRandom来生成随机数
并发高的情况下,试试用ThreadLocalRandom来生成随机数
2022-08-03 16:51:00 【Java进阶之路】
一:简述
如果我们想要生成一个随机数,通常会使用Random类。但是在并发情况下Random生成随机数的性能并不是很理想,今天给大家介绍一下JUC包中的用于生成随机数的类--ThreadLocalRandom.(本文基于JDK1.8)
二:Random的性能差在哪里
Random随机数生成是和种子seed有关,而为了保证线程安全性,Random通过CAS机制来保证线程安全性。从next()方法中我们可以看到seed是通过不断的CAS来进行修改值的。如果在高并发的场景下,那么可能会导致CAS失败,从而导致不断的自旋,这样可能会导致服务器CPU过高。
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));
}
三:ThreadLocalRandom的简单使用
使用的方法很简单,通过ThreadLocalRandom.current()获取到ThreadLocalRandom实例,然后通过nextInt(),nextLong()等方法获取一个随机数。
代码:
@Test
void test() throws InterruptedException{
new Thread(()->{
ThreadLocalRandom random = ThreadLocalRandom.current();
System.out.println(random.nextInt(100));
}).start();
new Thread(()->{
ThreadLocalRandom random = ThreadLocalRandom.current();
System.out.println(random.nextInt(100));
}).start();
Thread.sleep(100);
}
运行结果:
四:为什么ThreadLocalRandom能在保证线程安全的情况下还能有不错的性能
我们可以看一下ThreadLocalRandom的代码实现。
首先我们很容易看出这是一个饿汉式的单例
/** Constructor used only for static singleton */
private ThreadLocalRandom() {
initialized = true; // false during super() call
}
/** The common ThreadLocalRandom */
static final ThreadLocalRandom instance = new ThreadLocalRandom();
我们可以看到PROBE成员变量代表的是Thread类的threadLocalRandomProbe属性的内存偏移量,SEED成员变量代表的是Thread类的threadLocalRandomSeed属性的内存偏移量,SECONDARY成员变量代表的是Thread类的threadLocalRandomSecondarySeed属性的内存偏移量。
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 = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
} catch (Exception e) {
throw new Error(e);
}
}
可以看到Thread类中确实有这三个属性
Thread类:
@sun.misc.Contended("tlr")
//当前Thread的随机种子 默认值是0
long threadLocalRandomSeed;
/** Probe hash value; nonzero if threadLocalRandomSeed initialized */
@sun.misc.Contended("tlr")
//用来标志当前Thread的threadLocalRandomSeed是否进行了初始化 0代表没有,非0代表已经初始化 默认值是0
int threadLocalRandomProbe;
/** Secondary seed isolated from public ThreadLocalRandom sequence */
@sun.misc.Contended("tlr")
//当前Thread的二级随机种子 默认值是0
int threadLocalRandomSecondarySeed;
接下来我们看ThreadLocalRandom.current()方法。
ThreadLocalRandom.current()
ThreadLocalRandom.current()的作用是初始化随机种子,并且返回ThreadLocalRandom的实例。首先通过UNSAFE类获取当前线程的Thread对象的threadLocalRandomProbe属性,看随机种子是否已经初始化。没有初始化,那么调用localInit()方法进行初始化。
public static ThreadLocalRandom current() {
// 获取当前线程的
if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
localInit();
return instance;
}
localInit()
localInit()方法的作用就是初始化随机种子,可以看到代码很简单,就是通过UNSAFE类对当前Thread的threadLocalRandomProbe属性和threadLocalRandomSeed属性进行一个赋值。
static final void localInit() {
int p = probeGenerator.addAndGet(PROBE_INCREMENT);
int probe = (p == 0) ? 1 : p; // skip 0
long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
Thread t = Thread.currentThread();
UNSAFE.putLong(t, SEED, seed);
UNSAFE.putInt(t, PROBE, probe);
}
接下来以nextInt()方法为例,看ThreadLocalRandom是如何生成到随机数的。我们可以看出随机数正是通过nextSeed()方法获取到随机种子,然后通过随机种子而生成。所以重点看nextSeed()方法是如何获取到随机种子的。
public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
int r = mix32(nextSeed());
int m = bound - 1;
if ((bound & m) == 0) // power of two
r &= m;
else { // reject over-represented candidates
for (int u = r >>> 1;
u + m - (r = u % bound) < 0;
u = mix32(nextSeed()) >>> 1)
;
}
return r;
}
nextSeed()
nextSeed()方法的作用是获取随机种子,代码很简单,就是通过UNSAFE类获取当前线程的threadLocalRandomSeed属性,并且将原来的threadLocalRandomSeed加上GAMMA设置成新的threadLocalRandomSeed。
final long nextSeed() {
Thread t; long r; // read and update per-thread seed
UNSAFE.putLong(t = Thread.currentThread(), SEED,
r = UNSAFE.getLong(t, SEED) + GAMMA);
return r;
}
小结:
ThreadLocalRandom为什么线程安全?是因为它将随机种子保存在当前Thread对象的threadLocalRandomSeed变量中,这样每个线程都有自己的随机种子,实现了线程级别的隔离,所以ThreadLocalRandom也并不需要像Random通过自旋锁和cas来保证随机种子的线程安全性。在高并发的场景下,效率也会相对较高。
注:各位有没有发现ThreadLocalRandom保证线程安全的方式和ThreadLocal有点像呢
需要注意的点:
1.ThreadLocalRandom是单例的。
2.我们每个线程在获取随机数之前都需要调用一下ThreadLocalRandom.current()来初始化当前线程的随机种子。
3.理解ThreadLocalRandom需要对UnSafe类有所了解,它是Java提供的一个可以直接通过内存对变量进行获取和修改的一个工具类。java的CAS也是通过这个工具类来实现的。
边栏推荐
- 产品-Axure9英文版,轮播图效果
- EasyExcel implements dynamic column parsing and table storage
- C专家编程 第1章 C:穿越时空的迷雾 1.11 轻松一下---由编译器定义的Pragmas效果
- C专家编程 第3章 分析C语言的声明 3.1 只有编译器才会喜欢的语法
- C专家编程 第3章 分析C语言的声明 3.5 typedef可以成为你的朋友
- C专家编程 第3章 分析C语言的声明 3.6 typedef int x[10]和#define x int[10]的区别
- C专家编程 第3章 分析C语言的声明 3.4 通过图标分析C语言的声明
- “LaMDA 存在种族歧视,谷歌的 AI 伦理不过是‘遮羞布’!”
- 使用uniapp 封装一个request 请求
- 204. Count Primes
猜你喜欢
Huawei, Lenovo, BAIC, etc. were selected as the first batch of training bases for "Enterprise Digital Transformation and Security Capability Improvement" by the Ministry of Industry and Information Te
融云「音视频架构实践」技术专场【内含完整PPT】
Adobe是什么?
Hannah荣获第六季完美童模全球总决赛全球人气总冠军
B站回应HR称核心用户是Loser;微博回应宕机原因;Go 1.19 正式发布|极客头条
自动化部署+整合SSM项目
使用deepstream消息发送功能的时候,检测框没有检测标签,No text labels of bboxes displayed with osd for deepstream-test5
Component communication - parent-child component communication
工程仪器设备在线监测管理系统常见问题和注意事项
“68道 Redis+168道 MySQL”精品面试题(带解析),你背废了吗?
随机推荐
SQL中对 datetime 类型操作
高效的组织信息共享知识库是一种宝贵的资源
【Metaverse系列一】元宇宙的奥秘
protobuf 中数据编码规则
FinClip | July 2022 Product Highlights
C专家编程 第1章 C:穿越时空的迷雾 1.7 编译限制
关于oracle表空间在线碎片整理
【目标检测】Focal Loss for Dense Object Detection
华为、联想、北汽等入选工信部“企业数字化转型和安全能力提升”首批实训基地
Kubernetes 笔记 / 目录
sphinx error connection to 127.0.0.1:9312 failed (errno=0, msg=)
sibling component communication context
生产环境如何删除表呢?只能在SQL脚本里执行 drop table 吗
黄致绮 荣获第六季完美童模全球总决赛 全国总冠军
正向代理与反向代理
SwinIR实战:如何使用SwinIR和预训练模型实现图片的超分
[Unity Starter Plan] Making RubyAdventure01 - Player Creation & Movement
Excuse me this hologres dimension table is cached?How to Finished
leetcode:189. 轮转数组
[Unity Getting Started Plan] Basic Concepts (7) - Input Manager & Input Class