写点什么

多线程并发主题 -ThreadLocalRandom 类

用户头像
Geek_896619
关注
发布于: 2020 年 11 月 11 日
  • Random类简介

  • Random类的不足之处

  • ThreadLocalRandom简介

  • ThreadLocalRandom原理剖析

  • Random与ThreadLocalRandom实际执行情况对比

  • ThreadLocalRandom类总结



Instances of {@code java.util.Random} are threadsafe. However, the concurrent use of the same {@code java.util.Random} instance across threads may encounter contention and consequent poor performance. Consider instead using {@linkjava.util.concurrent.ThreadLocalRandom} in multithreaded designs.



摘自Random类注释:

java.util.Random实例是线程安全的,然而,如果是在高并发的场景下使用,则其性能会降低。可以在多线程的场景下,使用java.util.concurrent.ThreadLocalRandom来替代java.util.Random。



环境:

JDK 1.7



Random类简介

Random类是jdk util包中的一个随机数生成类。其基本使用如下:

Random random = new Random();
System.out.println(random.nextInt(5));
//3

通过以上代码就可以生成0至5之间的随机整数(包含0,不包含5)。

Random生成随机数的过程如下:

1.首先实例化Random类,这时会初始化一个long型的种子值。

2.这个seed种子值可以以两种方式初始化,一种时根据系统时间,也就是通过无参方式初始化。

3.另外一种方式是根据固定种子值初始化,也就是有参方式。

4.seed种子的作用就是为了生成随机数(这里我们主要探究Random类与ThreadLocalRandom类的比较,所以对seed的生成过程源码不做探究,只说明作用),如果种子值相同,那么生成的随机数也是“相同”的。如下:

Random random = new Random(1000);
Random random1 = new Random(1000);
System.out.println(random.nextInt(5));
System.out.println(random1.nextInt(5));
//2
//2

5.有了种子后,就可以通过java.util.Random#nextInt()方法生成随机数了。在多线程环境下,多条线程会拿到相同的种子吗?答案是不会,在Random类中有一个方法,避免多线程环境下,线程共用一个种子值,java.util.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));
}

能看到,方法中使用原子类AtomicLong,并用自旋操作保证一个至多会有一个线程更新种子值,这就可以保证Random是线程安全的。



Random类的不足之处

以上就是Random类生成随机数的过程,我们看到Random类来保证线程安全的关键是通过原子类的自旋操作保证的,cas是乐观锁的实现,适用于多线程少量更新的情况,一旦有大量更新的场景,自旋操作就会大量耗费cpu的资源,带来性能的下降,那么有没有方法解决Random在多线程环境下的性能问题呢,ThreadLocalRandom为我们提供了解决方案。



ThreadLocalRandom简介

JUC下的ThreadLocalRandom类是线程安全的类,它的作用和random一样,生成随机数。

根据类名,我们很容易想到ThreadLocal类,而ThreadLocalRandom保证线程安全的机制也是运行ThreadLocal的方式实现的,使用ThreadLocalRandom生成随机数的代码如下:

ThreadLocalRandom localRandom = ThreadLocalRandom.current();
localRandom.nextInt();

ThreadLocalRandom实例化是通过静态方法:ThreadLocalRandom.current(),



在多线程环境下,为什么ThreadLocalRandom的性能就明显优于Random类呢?不同于Random类,ThreadLocalRandom的种子是保存在本地线程中的,不存在种子值的资源共享问题,ThreadLocalRandom类创建本地线程的是类中的current方法,ThreadLocalRandom.current()方法的代码如下:

private static final ThreadLocal<ThreadLocalRandom> localRandom =
new ThreadLocal<ThreadLocalRandom>() {
protected ThreadLocalRandom initialValue() {
return new ThreadLocalRandom();
}
};
public static ThreadLocalRandom current() {
return localRandom.get();
}

显而易见,每一个线程去调用ThreadLocalRandom.current()都会生成一个本地线程的实例ThreadLocal<ThreadLocalRandom> localRandom;这里本地线程的变量就是ThreadLocalRandom;

ThreadLocalRandom原理剖析



Random类



ThreadLocalRandom类



ThreadLocalRandom源码分析

  1. 实例化ThreadLocalRandom

/**
* Returns the current thread's {@code ThreadLocalRandom}.
*
* @return the current thread's {@code ThreadLocalRandom}
*/
public static ThreadLocalRandom current() {
return localRandom.get();
}



2.



Random与ThreadLocalRandom实际执行情况对比



ThreadLocalRandom类总结



发布于: 2020 年 11 月 11 日阅读数: 76
用户头像

Geek_896619

关注

还未添加个人签名 2018.11.08 加入

还未添加个人简介

评论

发布
暂无评论
多线程并发主题-ThreadLocalRandom类