ThreadLocalRandom的正确用法

2023-12-21 05:58

本文主要是介绍ThreadLocalRandom的正确用法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

java里有伪随机型和安全型两种随机数生成器,伪随机生成器根据特定公式将seed转换成新的伪随机数据的一部分,安全随机生成器在底层依赖到操作系统提供的随机事件来生成数据。

安全随机生成器

  • 需要生成加密性强的随机数据的时候才用它
  • 生成速度慢
  • 如果需要生成大量的随机数据,可能会产生阻塞需要等待外部中断事件

而伪随机生成器,只依赖于“seed”的初始值,如果给生成算法提供相同的seed,可以得到一样的伪随机序列。一般情况下,由于它是计算密集型的(不依赖于任何IO设备),因此生成速度更快。以下是伪随机生成器的进化史。

java.util.Random 
自1.0就已经存在,是一个线程安全类,理论上可以通过它同时在多个线程中获得互不相同的随机数,这样的线程安全是通过AtomicLong实现的。 
Random使用AtomicLong CAS(compare and set)操作来更新它的seed,尽管在很多非阻塞式算法中使用了非阻塞式原语,CAS在资源高度竞争时的表现依然糟糕。

java.util.concurrent.ThreadLocalRandom 
1.7增加该类,企图将它和Random结合以克服所有的性能问题,该类继承自Random。

Random用到了compareAndSet + synchronized来解决线程安全问题,虽然可以使用ThreadLocal<Random>来避免竞争,但是无法避免synchronized/compareAndSet带来的开销。考虑到性能还是建议替换使用ThreadLocalRandom(有3倍以上提升),这不是ThreadLocal包装后的Random,而是真正的使用ThreadLocal机制重新实现的Random。

ThreadLocalRandom的主要实现细节:

  • 使用一个普通的long而不是使用Random中的AtomicLong作为seed
  • 不能自己创建ThreadLocalRandom实例,因为它的构造函数是私有的,可以使用它的静态工厂ThreadLocalRandom.current()
  • 它是CPU缓存感知式的,使用8个long虚拟域来填充64位L1高速缓存行

ThreadLocalRandom使用不当多线程下产生相同随机数

import java.util.concurrent.ThreadLocalRandom;public class ThreadLocalRandomDemo {private static final ThreadLocalRandom RANDOM =ThreadLocalRandom.current();public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Player().start();}}private static class Player extends Thread {@Overridepublic void run() {System.out.println(getName() + ": " + RANDOM.nextInt(100));}}
}

运行该代码,结果如下:

Thread-0: 4
Thread-1: 4
Thread-2: 4
Thread-3: 4
Thread-4: 4
Thread-5: 4
Thread-6: 4
Thread-7: 4
Thread-8: 4
Thread-9: 4

原因如下:

除了初始化 ThreadLocalRandom 的主线程获取的随机值是无模式的(调用者不可预测下个返回值,满足我们对伪随机的要求)之外,其他线程获得随机值都不是相互独立的(本质上来说,是因为他们用于生成随机数的种子 seed 的值可预测的,为 i*gamma,其中 i 是当前线程调用随机数生成方法次数,而 gamma 是 ThreadLocalRandom 类的一个 long 静态字段值)。例如,一个有趣的现象是,所有非初始化 ThreadLocalRandom 实例的线程如果调用相同次数的 nextInt() 方法,他们得到的随机数串是完全相同的。
造成这样现象的原因在于,ThreadLocalRandom 类维护了一个类单例字段,线程通过调用 ThreadLocalRandom#current() 方法来获取 ThreadLocalRandom 单例,然后以线程维护的实例字段 threadLocalRandomSeed 为种子生成下一个随机数和下一个种子值。
那么既然是单例模式,为什么多线程共用主线程初始化的实例就会出问题呢。问题就在于 current 方法,线程在调用 current() 方法的时候,会根据用每个线程的 thread 的一个实例字段 threadLocalRandomProbe 是否为 0 来判断是否当前线程实例是否为第一次调用随机数生成方法,从而决定是否要给当前线程初始化一个随机的 threadLocalRandomSeed 种子值。因此,如果其他线程绕过 current 方法直接调用随机数方法,那么它的种子值就是 0, 1*gamma, 2*gamma... 因此也就是可预测的了。

 正确用法:

ThreadLocalRandom的正确使用方式是ThreadLocalRandom.current().nextX(...),不能在多线程之间共享ThreadLocalRandom

import java.util.concurrent.ThreadLocalRandom;public class ThreadLocalRandomDemo {public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Player().start();}}private static class Player extends Thread {@Overridepublic void run() {System.out.println(getName() + ": " + ThreadLocalRandom.current().nextInt(100));}}
}

 

这篇关于ThreadLocalRandom的正确用法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/518859

相关文章

Python中logging模块用法示例总结

《Python中logging模块用法示例总结》在Python中logging模块是一个强大的日志记录工具,它允许用户将程序运行期间产生的日志信息输出到控制台或者写入到文件中,:本文主要介绍Pyt... 目录前言一. 基本使用1. 五种日志等级2.  设置报告等级3. 自定义格式4. C语言风格的格式化方法

SpringBoot 获取请求参数的常用注解及用法

《SpringBoot获取请求参数的常用注解及用法》SpringBoot通过@RequestParam、@PathVariable等注解支持从HTTP请求中获取参数,涵盖查询、路径、请求体、头、C... 目录SpringBoot 提供了多种注解来方便地从 HTTP 请求中获取参数以下是主要的注解及其用法:1

Java中如何正确的停掉线程

《Java中如何正确的停掉线程》Java通过interrupt()通知线程停止而非强制,确保线程自主处理中断,避免数据损坏,线程池的shutdown()等待任务完成,shutdownNow()强制中断... 目录为什么不强制停止为什么 Java 不提供强制停止线程的能力呢?如何用interrupt停止线程s

Java中HashMap的用法详细介绍

《Java中HashMap的用法详细介绍》JavaHashMap是一种高效的数据结构,用于存储键值对,它是基于哈希表实现的,提供快速的插入、删除和查找操作,:本文主要介绍Java中HashMap... 目录一.HashMap1.基本概念2.底层数据结构:3.HashCode和equals方法为什么重写Has

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

Python异步编程之await与asyncio基本用法详解

《Python异步编程之await与asyncio基本用法详解》在Python中,await和asyncio是异步编程的核心工具,用于高效处理I/O密集型任务(如网络请求、文件读写、数据库操作等),接... 目录一、核心概念二、使用场景三、基本用法1. 定义协程2. 运行协程3. 并发执行多个任务四、关键

Python中yield的用法和实际应用示例

《Python中yield的用法和实际应用示例》在Python中,yield关键字主要用于生成器函数(generatorfunctions)中,其目的是使函数能够像迭代器一样工作,即可以被遍历,但不会... 目录python中yield的用法详解一、引言二、yield的基本用法1、yield与生成器2、yi

深度解析Python yfinance的核心功能和高级用法

《深度解析Pythonyfinance的核心功能和高级用法》yfinance是一个功能强大且易于使用的Python库,用于从YahooFinance获取金融数据,本教程将深入探讨yfinance的核... 目录yfinance 深度解析教程 (python)1. 简介与安装1.1 什么是 yfinance?

Python库 Django 的简介、安装、用法入门教程

《Python库Django的简介、安装、用法入门教程》Django是Python最流行的Web框架之一,它帮助开发者快速、高效地构建功能强大的Web应用程序,接下来我们将从简介、安装到用法详解,... 目录一、Django 简介 二、Django 的安装教程 1. 创建虚拟环境2. 安装Django三、创

python中update()函数的用法和一些例子

《python中update()函数的用法和一些例子》update()方法是字典对象的方法,用于将一个字典中的键值对更新到另一个字典中,:本文主要介绍python中update()函数的用法和一些... 目录前言用法注意事项示例示例 1: 使用另一个字典来更新示例 2: 使用可迭代对象来更新示例 3: 使用