网站首页 > 技术文章 正文
前言
大家项目中如果有生成随机数的需求,我想大多都会选择使用Random来实现,它内部使用了CAS来实现。 实际上,JDK1.7之后,提供了另外一个生成随机数的类ThreadLocalRandom,那么他们二者之间的性能是怎么样的呢?
Random的使用
Random类是JDK提供的生成随机数的类, 这个类不是随机的,而是伪随机的。什么是伪随机呢? 伪随机是指生成的随机数是有一定规律的,这个规律出现的周期因伪随机算法的优劣而异。 一般来说,周期比较长,但可以预见。 我们可以通过以下代码简单地使用 Random:
Random中有很多方法。 这里我们就分析比较常见的nextInt()和nextInt(int bound)方法。
- nextInt()会计算int范围内的随机数,
- nextInt(int bound)会计算[0,bound) 之间的随机数,左闭右开。
实现原理
Random类的构造函数如下图所示:
- 可以看到在构造方法中,根据当前时间seed生成了一个AtomicLong类型的seed。
public int nextInt() {
return next(32);
}
- 这里面直接调用了next()方法,传入了32,这里的32是指Int的位数。
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));
}
- 这里会根据seed的当前值,通过一定的规则(伪随机)计算出下一个seed,然后进行CAS。 如果CAS失败,继续循环上述操作。 最后根据我们需要的位数返回。
小结:可以看出在next(int bits)方法中,对AtomicLong进行了CAS操作,如果失败则循环重试。 很多人一看到CAS,因为不需要加锁,第一时间就想到了高性能、高并发。 但是在这里,却成为了我们多线程并发性能的瓶颈。 可以想象,当我们有多个线程执行CAS时,只有一个线程一定会失败,其他的会继续循环执行CAS操作。 当并发线程较多时,性能就会下降。
ThreadLocalRandom的使用
JDK1.7之后,提供了一个新类ThreadLocalRandom来替代Random。
实现原理
我们先来看下current()方法。
public static ThreadLocalRandom current() {
if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
localInit();
return instance;
}
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);
}
- 如果没有初始化,先进行初始化,这里我们的seed不再是全局变量了。 我们的线程中有三个变量:
/** The current seed for a ThreadLocalRandom */
@sun.misc.Contended("tlr")
long threadLocalRandomSeed;
/** Probe hash value; nonzero if threadLocalRandomSeed initialized */
@sun.misc.Contended("tlr")
int threadLocalRandomProbe;
/** Secondary seed isolated from public ThreadLocalRandom sequence */
@sun.misc.Contended("tlr")
int threadLocalRandomSecondarySeed;
- threadLocalRandomSeed:这是我们用来控制随机数的种子。
- threadLocalRandomProbe:这个就是ThreadLocalRandom,用来控制初始化。
- threadLocalRandomSecondarySeed:这是二级种子。
关键代码如下:
UNSAFE.putLong(t = Thread.currentThread(), SEED,r=UNSAFE.getLong(t, SEED) + GAMMA);
可以看出,由于每个线程都维护自己的seed,所以此时不需要CAS,直接进行put。 这里通过线程间的隔离来减少并发冲突,所以ThreadLocalRandom的性能非常高。
性能对比
通过基准工具JMH测试:
@BenchmarkMode({Mode.AverageTime})
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations=3, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations=3,time = 5)
@Threads(4)
@Fork(1)
@State(Scope.Benchmark)
public class Myclass {
Random random = new Random();
ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
@Benchmark
public int measureRandom(){
return random.nextInt();
}
@Benchmark
public int threadLocalmeasureRandom(){
return threadLocalRandom.nextInt();
}
}
运行结果如下图所示,最左边是并发线程的数量:
显而易见,无论线程数量是多少,ThreadLocalRandom性能是远高于Random。
总结
本文讲解了JDK中提供的两种生成随机数的方式,一个是JDK 1.0引入的Random类,另外一个是JDK1.7引入的ThreadLocalRandom类,由于底层的实现机制不同,ThreadLocalRandom的性能是远高于Random,建议后面大家在技术选型的时候优先使用ThreadLocalRandom。
猜你喜欢
- 2024-10-11 sklearn分类模型汇总 sklearn svm分类
- 2024-10-11 一款小众但实用的自动化特征选择工具:AutoFeatSelect
- 2024-10-11 基于决策树算法完成鸢尾花的分类并使用scikit-learn进行交叉验证
- 2024-10-11 「程序员」如何5分钟掌握,pandas数据累计与分组?
- 2024-10-11 Python 机器学习 特征降维 python tsne降维
- 2024-10-11 mooc机器学习第七天-分类支持向量机svm.svc
- 2024-10-11 模型选择之交叉验证(简单交叉验证、S折交叉验证和留一交叉验证)
- 2024-10-11 I2C七宗罪之第二罪 七宗罪二哥
- 2024-10-11 PCA 主成分分析 PCA主成分分析实现鸢尾花数据集分类-MATLAB代码
- 2024-10-11 Python实现机器学习算法——随机森林
你 发表评论:
欢迎- 最近发表
-
- 在 Spring Boot 项目中使用 activiti
- 开箱即用-activiti流程引擎(active 流程引擎)
- 在springBoot项目中整合使用activiti
- activiti中的网关是干什么的?(activiti包含网关)
- SpringBoot集成工作流Activiti(完整源码和配套文档)
- Activiti工作流介绍及使用(activiti工作流会签)
- SpringBoot集成工作流Activiti(实际项目演示)
- activiti工作流引擎(activiti工作流引擎怎么用)
- 工作流Activiti初体验及在数据库中生成的表
- Activiti工作流浅析(activiti6.0工作流引擎深度解析)
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)