Java并发编程ThreadLocalRandom类详解

 更新时间:2022年06月10日 14:52:05   作者:​ 派大大大星   ​  
这篇文章主要介绍了Java并发编程ThreadLocalRandom类详解,通过提出问题为什么需要ThreadLocalRandom展开详情,感兴趣的朋友可以参考一下

为什么需要ThreadLocalRandom

java.util.Random一直都是使用比较广泛的随机数生成工具类,而且java.lang.Math中的随机数生成也是使用的java.util.Random实例。

我们下面看一下java.util.Random的使用方法:

import java.util.Random;
public class code_4_threadRandom {
    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;
}

由上面代码可见,一个新的随机数生成需要两个步骤:首先根据老的种子生成新的种子,然后根据新的种子来计算新的随机数。如果在单线程的情况下每次调用nextInt都是根据老的种子计算出新的种子。但是在多线程下多个线程都可能都拿到同一个老的种子去生成新种子,这回导致多个线程生成的新随机数是相同的。我们需要当多个线程通过同一个老种子计算新种子时,当第一个线程的新种子被计算出来后,第二个线程要丢弃掉老种子,用第一个线程计算出的新种子来计算自己的新种子。在Random类中,对象初始化时的种子就被保存到了种子原子变量里。

下面看一下next()的代码:

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));
}

在上面代码中,通过CAS操作来更新种子,在多线程情况下,多个线程同时计算随机数来计算新的种子,多个线程会竞争同一个原子变量的更新操作,会造成大量线程进行自旋重试,降低并发性能。所以ThreadLocalRandom应运而生。

ThreadRandom原理详解

import java.util.Random;
public class code_4_threadLocalRandom {
    public static void main(String[] args) {
        Random random = new ThreadLocalRandom.current();
        for(int i = 0; i < 10; i++) {
            System.out.println(
                    random1.nextInt(5)
            );
        }
    }
}

如果每个线程都维护一个种子变量,则每个线程生成随机数时都根据自己老的种子计算新的种子,并使用新的种子更新老种子,再根据新种子计算随机数,这就不会存在竞争问题了。ThreadLocalRandom 类 继 承 了 Random 类 并 重 写 了 nextlnt方法,在 ThreadLocalRandom 类中并没有使用继承自Random 类的原子性种子变量。

在ThreadLocalRandom中并没有存放具体的种子,具体的种子存放在具体的调用线程的 threadLocalRandomSeed 变量里面。ThreadLocalRandom 类似于 ThreadLocal 类,就是个工具类。当线程调用 ThreadLocalRandom的current 方法时,ThreadLocalRandom 负责初始化调用线程的threadLocalRandomSeed 变量,也就是初始化种子。当 调 用 ThreadLocalRandom 的 nextInt 方 法 时, 实际 上 是 获 取 当前 线 程的threadLocalRandomSeed 变量作为当前种子来计算新的种子,然后更新新的种子到当前线程的threadLocalRandomSeed 变量,而后再根据新种子并使用具体算法计算随机数。这里需要注意的是,threadLocalRandomSeed 变量就是 Thread 类里面的一个普通 long 变量,它并不是原子性变量。其实道理很简单,因为这个变量是线程级别的,所以根本不需要使用原子性变量。

变量instance是ThreadLocalRandom的一个实例,该变量是static的。当多线程通过ThreadLocalRandom的current方法获取ThreadLocalRandom的实例时,其实是同一个实例。但是由于具体的种子是存放在线程里面的,所以在ThreadLocalRandom的实例里面只包含与线程无关的通用算法,所以它是线程安全的。

到此这篇关于Java并发编程ThreadLocalRandom类详解的文章就介绍到这了,更多相关Java ThreadLocalRandom 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 超详细讲解Java异常

    超详细讲解Java异常

    Java 异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。本文超详细讲解了Java异常,感兴趣的小伙伴可以参考一下这篇文章
    2021-09-09
  • Java并发编程之ConcurrentLinkedQueue解读

    Java并发编程之ConcurrentLinkedQueue解读

    这篇文章主要介绍了Java并发编程之ConcurrentLinkedQueue解读,非阻塞的实现方式则可以使用循环CAS的方式来实现,而ConcurrentLinkedQueue就是juc包中自带的经典非堵塞方式实现的工具类,需要的朋友可以参考下
    2023-12-12
  • SpringBoot获取配置文件的简单实现方法

    SpringBoot获取配置文件的简单实现方法

    这篇文章主要给大家介绍了关于SpringBoot如何获取配置文件的简单实现方法,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring Boot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2020-05-05
  • Java实现两人五子棋游戏(四) 落子动作的实现

    Java实现两人五子棋游戏(四) 落子动作的实现

    这篇文章主要为大家详细介绍了Java实现两人五子棋游戏,落子动作的实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • Java中的HttpServletRequest接口详细解读

    Java中的HttpServletRequest接口详细解读

    这篇文章主要介绍了Java中的HttpServletRequest接口详细解读,是一个接口,全限定名称为Jakarta.Serclet.http.HttpServletRequest
    HttpServletRequest接口是Servlet规范的一员,需要的朋友可以参考下
    2023-11-11
  • Java实现格式化打印慢SQL日志的方法详解

    Java实现格式化打印慢SQL日志的方法详解

    不管我们使用何种语言开发,一旦程序发生异常,日志是一个很重要的数据,下面这篇文章主要给大家介绍了关于Java实现格式化打印慢SQL日志的相关资料,需要的朋友可以参考下
    2022-10-10
  • java中ssj框架的项目搭建流程

    java中ssj框架的项目搭建流程

    这篇文章主要介绍了java中ssj框架的项目搭建流程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • spring WebSocket示例详解

    spring WebSocket示例详解

    websocket是Html5新增加特性之一,目的是浏览器与服务端建立全双工的通信方式,本篇文章主要介绍了spring WebSocket示例详解 ,有兴趣的可以了解一下。
    2017-01-01
  • Mybatis框架及原理实例分析

    Mybatis框架及原理实例分析

    这篇文章主要介绍了Mybatis框架及原理实例分析,需要的朋友可以参考下
    2017-08-08
  • 详解MyBatis延迟加载是如何实现的

    详解MyBatis延迟加载是如何实现的

    MyBatis 的延迟加载(懒加载)特性允许在需要使用关联对象数据时才进行加载,而不是在执行主查询时就加载所有相关数据,我们将通过以下几个方面来深入了解MyBatis的延迟加载实现机制,需要的朋友可以参考下
    2024-07-07

最新评论