首页 > 编程语言 >JAVA中生成随机数Random VS ThreadLocalRandom性能比较

JAVA中生成随机数Random VS ThreadLocalRandom性能比较

时间:2022-12-14 23:55:17浏览次数:63  
标签:JAVA ThreadLocalRandom int Random seed 线程 随机数

前言

大家项目中如果有生成随机数的需求,我想大多都会选择使用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

如果本文对你有帮助的话,请留下一个赞吧
欢迎关注个人公众号——JAVA旭阳
更多学习资料请移步:程序员成神之路

标签:JAVA,ThreadLocalRandom,int,Random,seed,线程,随机数
From: https://www.cnblogs.com/alvinscript/p/16983997.html

相关文章

  • javaweb7
    JQuery1.介绍1.1web前端知识点回顾HTML决定了网页的内容CSS决定了网页样式JavaScript决定了网页的功能标签属性,样式的操作DOM节点的操作异步交互的操作但是......
  • Java后端获取前端传过来header中的值
    Java后端获取前端传过来header中的值比如获取前端传值token值:方法一:不需要在参数中写HttpServletRequestrequestServletRequestAttributesattributes=(ServletRequ......
  • 肖sir___java自动化__selenium IDE下载和使用
    1.seleniumIDE简介seleniumIDE结合浏览器提供脚本的录制、回放以、编辑脚本功能、以及元素的定位,可以使用seleniumIDE将录制的脚本生成相应的带单元测试框架的自动化测......
  • 彻底搞懂JavaScript防抖与节流
    今天为大家带来一篇JS重难点的知识体系,这也是前端高薪必备的重难点知识,而且防抖与节流在各大企业前端面试过程中经常会考到的高频面试题! 为了更好的帮助大家理解防抖......
  • java中方法总结
    方法(method)是程序中最小的执行单元复用性,可维护性方法的格式简单格式publicstaticvoid方法名(){方法体(打包的代码)}调用:方法名()参数格式publ......
  • java学习笔记--类、函数重载、this、static、继承、重写、多态
    <5>类1)类和对象类是把一类事物的静态属性和动态操作组合在一起所得的概念,相当于模型,或者说一个设计图纸。对象是类的一个个体,是根据类这个设计图纸造出来的实物,会产生和......
  • java循环语句
    循环格式(下图)for(inti=0;i<=5;i++){number+=i;}System.out.println(number);过程:while循环格式初始化语句;while(条件判断语句){循环体语句......
  • java数据类型(基本数据类型)
    java语言的数据类型分为:基本数据类型,引用数据类型从内存角度来看:基础数据类型存储在自己的空间中,特点:赋值给其他变量,也是赋的真实的值引用数据类型数据值存储在其他......
  • java数组
    数组概念:是指一种容器,可以用来存储同种数据类型的多个值。建议容器的类型和存储的数据类型保持一致。数组定义:没区别数据类型 [] 数组名数据类型数组名[]......
  • Java大神常用Linux命令整理一
    1.cd用来改变目录案例cd进入用户的主目录cd/etc绝对目录模式cd./home相对目录模式cdhome相对目录模式cd..返回上一级目录2.ls列表显示文件和......