当前位置: 首页 > 科技观察

Java中随机数生成RandomVSThreadLocalRandom性能对比

时间:2023-03-12 06:02:25 科技观察

前言如果你的项目中有生成随机数的需求,我想大部分都会选择使用Random来实现,也就是内部使用CAS来实现。其实在JDK1.7之后又提供了一个生成随机数的类ThreadLocalRandom,那么它们之间的性能如何呢?Random的使用Random类是JDK提供的一个生成随机数的类。这个类不是随机的,而是伪随机的。什么是伪随机?伪随机是指生成的随机数有一定的规律,这个规律的循环根据伪随机算法的优劣而不同。一般来说,周期比较长,但可以预见。我们可以通过以下代码简单地使用Random:Random中有很多方法。这里我们分析比较常见的nextInt()和nextInt(intbound)方法。nextInt()会计算一个int范围内的随机数,nextInt(intbound)会计算[0,bound]之间的一个随机数,左闭右开。实现原理Random类的构造函数如下图所示:可以看到在构造方法中,根据当前时间种子生成了一个AtomicLong类型的种子。publicintnextInt(){returnnext(32);}这里直接调用next()方法,传入32,其中32是指Int的位数。protectedintnext(intbits){longoldseed,nextseed;AtomicLongseed=this.seed;做{oldseed=seed.get();nextseed=(oldseed*multiplier+addend)&mask;}while(!seed.compareAndSet(oldseed,nextseed));return(int)(nextseed>>>(48-bits));}这里会根据seed的当前值,按照一定的规则(伪随机)计算出下一个seed,然后进行CAS.如果CAS失败,继续循环上述操作。最后根据我们需要的位数返回。总结:可以看出在next(intbits)方法中,对AtomicLong进行了CAS操作,如果失败则循环重试。很多人一看到CAS就立刻想到高性能和高并发,因为它不需要加锁。但是在这里,却成为了我们多线程并发性能的瓶颈。可以想象,当我们有多个线程执行CAS时,只有一个线程会失败,其他的会一直循环执行CAS操作。当并发线程很多时,性能会下降。ThreadLocalRandom使用JDK1.7之后,提供了一个新类ThreadLocalRandom来替代Random。实现原理我们先来看一下current()方法。publicstaticThreadLocalRandomcurrent(){if(UNSAFE.getInt(Thread.currentThread(),PROBE)==0)localInit();返回实例;}staticfinalvoidlocalInit(){intp=probeGenerator.addAndGet(PROBE_INCREMENT);int探针=(p==0)?1:p;//跳过0长种子=mix64(seeder.getAndAdd(SEEDER_INCREMENT));线程t=Thread.currentThread();UNSAFE.putLong(t,SEED,seed);UNSAFE.putInt(t,PROBE,probe);}如果没有初始化,先初始化,这里我们的seed不再是全局变量。我们的线程中有三个变量:/**ThreadLocalRandom的当前种子*/@sun.misc.Contended("tlr")longthreadLocalRandomSeed;/**Probehashvalue;如果threadLocalRandomSeed已初始化,则非零*/@sun.misc.Contended("tlr")intthreadLocalRandomProbe;/**与公共ThreadLocalRandom序列隔离的二级种子*/@sun.misc.Contended("tlr")intthreadLocalRandomSecondarySeed;threadLocalRandomSeed:这是我们用来控制随机数的种子。threadLocalRandomProbe:这个就是ThreadLocalRandom,用来控制初始化。threadLocalRandomSecondarySeed:这是二级种子。关键代码如下:UNSAFE.putLong(t=Thread.currentThread(),SEED,r=UNSAFE.getLong(t,SEED)+GAMMA);可以看出,由于每个线程都维护着自己的seed,所以这时候不需要CAS,直接放就行了。这里通过线程间的隔离来减少并发冲突,所以ThreadLocalRandom的性能非常高。性能对比使用基准工具JMH测试:@BenchmarkMode({Mode.AverageTime})@OutputTimeUnit(TimeUnit.NANOSECONDS)@Warmup(iteratinotallow=3,time=5,timeUnit=TimeUnit.SECONDS)@Measurement(iteratinotallow=3,time=5)@Threads(4)@Fork(1)@State(Scope.Benchmark)publicclassMyclass{Randomrandom=newRandom();ThreadLocalRandomthreadLocalRandom=ThreadLocalRandom.current();@BenchmarkpublicintmeasureRandom(){返回随机.nextInt();}@BenchmarkpublicintthreadLocalmeasureRandom(){返回threadLocalRandom.nextInt();}}运行结果如下图所示,最左边是并发线程数:很明显,无论线程数多少,ThreadLocalRandom的性能都远高于Random。小结本文介绍了JDK提供的两种生成随机数的方法。一个是JDK1.0引入的Random类,一个是JDK1.7引入的ThreadLocalRandom类。由于底层实现机制不同,ThreadLocalRandom的性能远高于Random,建议大家在选择技术时优先考虑ThreadLocalRandom。

最新推荐
猜你喜欢