java.util.Random和concurrent.ThreadLocalRandom使用对比

 更新时间:2024年07月04日 10:33:14   作者:xindoo  
这篇文章主要介绍了java.util.Random和concurrent.ThreadLocalRandom使用对比,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

java.util.Random和concurrent.ThreadLocalRandom对比

最近工作中遇到了一个需求

需要以一定的概率过滤掉一部分的流量,想想只能用Random了,因为是在多线程环境下,我还特意确认了下Random在多线程是否能正常运行,Random的实现也比较简单,初始化的时候用当前的事件来初始化一个随机数种子,然后每次取值的时候用这个种子与有些MagicNumber运算,并更新种子。

最核心的就是这个next的函数,不管你是调用了nextDouble还是nextInt还是nextBoolean,Random底层都是调这个next(int bits)。

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

为了保证多线程下每次生成随机数都是用的不同

next()得保证seed的更新是原子操作,所以用了AtomicLong的compareAndSet(),该方法底层调用了sum.misc.Unsafe的compareAndSwapLong(),也就是大家常听到的CAS, 这是一个native方法,它能保证原子更新一个数。  

既然Random满足我的需求,又能在多线程下正常运行,所以我直接用了random,后来在codeReview中,同事提出用concurrent.ThreadLocalRandom来替代Random。

我脑子里立马冒出一个问题,既然random是线程安全的,为什么concurrent包里还要实现一个random。

在oracle的jdk文档里发现这样一句话:

use of ThreadLocalRandom rather than shared Random objects in concurrent programs will typically encounter much less overhead and contention. Use of ThreadLocalRandom is particularly appropriate when multiple tasks (for example, each a ForkJoinTask) use random numbers in parallel in thread pools.

大意就是用ThreadLocalRandom更适合用在多线程下,能大幅减少多线程并行下的性能开销和资源争抢。  

既然文档里说的牛,到底能有多少的性能提升?

我做了一个简单的测试。

测试环境:

24核 CPU, jdk8,每个随机生成100000个double数,分别测试不同线程数下rando和ThreadLocalRandom的运行时间,数据如下:

这里写图片描述

ThreadNum,Random,ThreadLocalRandom 
50,1192,575
100,4031,162
150,6068,223
200,8093,287
250,10049,248
300,12346,200
350,14429,212
400,16491,62
450,18475,96
500,11311,97
550,12421,90
600,13577,102
650,14718,111
700,15896,127
750,17101,129
800,17907,203
850,19261,226
900,21576,151
950,22206,147
1000,23418,174

ThreadLocalRandom虽然也有波动,但基本上是平的,而random随着线程数的增加一直在增加,在1000个线程时两者居然有百倍的性能差距。

不过这里有个让人百思不得其解的现象,为什么random的耗时在500个线程的时候又掉下来,测试多次都是这个情况,可见并不是偶发现象。

我也在本人的笔记本上测了下,我笔记本双核i7,ThreadLocalRandom和Random性能差距最高也有100倍,我发现我笔记本比公司服务器跑的快(数据如下)。。。。

我也在一台1核的阿里云ECS上测试了,按道理1核心的技术上,即便是多线程起始也是串行执行的,但ThreadLocalRandom和Random在1000个线程的情况下也有6倍的性能差距。

  这里写图片描述

既然ThreadLocalRandom在多线程下表现这么牛,它究竟是如何做到的?

我们来看下源码,它的核心代码是这个:

    final long nextSeed() {
        Thread t; long r; // read and update per-thread seed
        UNSAFE.putLong(t = Thread.currentThread(), SEED,
                       r = UNSAFE.getLong(t, SEED) + GAMMA);
        return r;
    }

起始ThreadLocalRandom是对每个线程都设置了单独的随机数种子,这样就不会发生多线程同时更新一个数时产生的资源争抢了,用空间换时间。  

附上Random和ThreadLocalRandom的性能测试代码

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

public class RandomTest {
    private static Random random = new Random();

    private static final int N = 100000;
//    Random from java.util.concurrent.
    private static class TLRandom implements Runnable {
        @Override
        public void run() {
            double x = 0;
            for (int i = 0; i < N; i++) {
                x += ThreadLocalRandom.current().nextDouble();
            }
        }
    }

//    Random from java.util
    private static class URandom implements Runnable {
        @Override
        public void run() {
            double x = 0;
            for (int i = 0; i < N; i++) {
                x += random.nextDouble();
            }
        }
    }

    public static void main(String[] args) {
        System.out.println("threadNum,Random,ThreadLocalRandom");
        for (int threadNum = 50; threadNum <= 2000; threadNum += 50) {
            ExecutorService poolR = Executors.newFixedThreadPool(threadNum);
            long RStartTime = System.currentTimeMillis();
            for (int i = 0; i < threadNum; i++) {
                poolR.execute(new URandom());
            }
            try {
                poolR.shutdown();
                poolR.awaitTermination(100, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String str = "" + threadNum +"," + (System.currentTimeMillis() - RStartTime)+",";

            ExecutorService poolTLR = Executors.newFixedThreadPool(threadNum);
            long TLRStartTime = System.currentTimeMillis();
            for (int i = 0; i < threadNum; i++) {
                poolTLR.execute(new TLRandom());
            }
            try {
                poolTLR.shutdown();
                poolTLR.awaitTermination(100, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(str + (System.currentTimeMillis() - TLRStartTime));
        }
    }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 聊一聊Java的JVM类加载机制

    聊一聊Java的JVM类加载机制

    这篇文章主要介绍了聊一聊Java的JVM类加载机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • java Random.nextInt()方法的具体使用

    java Random.nextInt()方法的具体使用

    这篇文章主要介绍了java Random.nextInt()方法的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • 利用反射获取Java类中的静态变量名及变量值的简单实例

    利用反射获取Java类中的静态变量名及变量值的简单实例

    下面小编就为大家带来一篇利用反射获取Java类中的静态变量名及变量值的简单实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • Commons beanutils组件简介

    Commons beanutils组件简介

    这篇文章主要介绍了commons beanutils组件的相关内容,以及部分实例和基本用法,需要的朋友可以参考下
    2017-09-09
  • 解决IDEA下载依赖包源码报错Sources not found for:org.springframework.cloud:XXX问题

    解决IDEA下载依赖包源码报错Sources not found for:org.spri

    IDEA查看源码时如遇无法下载源代码报错,可通过Maven命令或配置IDEA自动下载源码来解决,保证依赖包能自动获取源码,提升开发体验
    2025-10-10
  • SpringBoot启动时自动执行指定方法的几种实现方式

    SpringBoot启动时自动执行指定方法的几种实现方式

    在Spring Boot应用程序中,要实现在应用启动时自动执行某些代码,本文主要介绍了SpringBoot启动时自动执行指定方法的几种方式,文中有相关的代码示例供大家参考,需要的朋友可以参考下
    2024-03-03
  • java使用BeanUtils.copyProperties踩坑经历

    java使用BeanUtils.copyProperties踩坑经历

    最近在做个项目,踩了个坑特此记录一下,本文主要介绍了使用BeanUtils.copyProperties踩坑经历,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • SpringMVC JSON数据交互及RESTful支持实现方法

    SpringMVC JSON数据交互及RESTful支持实现方法

    这篇文章主要介绍了SpringMVC JSON数据交互及RESTful支持实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • Java JVM原理与调优_动力节点Java学院整理

    Java JVM原理与调优_动力节点Java学院整理

    JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。下面通过本文给大家介绍jvm原理与调优相关知识,感兴趣的朋友一起学习吧
    2017-04-04
  • springboot中使用过滤器,jsoup过滤XSS脚本详解

    springboot中使用过滤器,jsoup过滤XSS脚本详解

    这篇文章主要介绍了springboot中使用过滤器,jsoup过滤XSS脚本详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12

最新评论