ThreadLocalRandom类原理分析

本文转载自微信公众号「一个程序员的成长」,作者一个程序员的成长。转载本文请联系一个程序员的成长公众号。

创新互联建站于2013年创立,是专业互联网技术服务公司,拥有项目成都做网站、成都网站建设网站策划,项目实施与项目整合能力。我们以让每一个梦想脱颖而出为使命,1280元汉中做网站,已为上家服务,为汉中各地企业和个人服务,联系电话:028-86922220

1、Random类及其局限性

 
 
 
  1. public int nextInt(int bound) {
  2.     if (bound <= 0)
  3.         throw new IllegalArgumentException(BadBound);
  4.     // 计算新的种子
  5.     int r = next(31);
  6.     int m = bound - 1;
  7.     // 根据新的种子计算随机数
  8.     if ((bound & m) == 0)  // i.e., bound is a power of 2
  9.         r = (int)((bound * (long)r) >> 31);
  10.     else {
  11.         for (int u = r;
  12.              u - (r = u % bound) + m < 0;
  13.              u = next(31))
  14.             ;
  15.     }
  16.     return r;
  17. }
 
 
 
  1. protected int next(int bits) {
  2.     long oldseed, nextseed;
  3.     // 这是一个原子性的变量
  4.     AtomicLong seed = this.seed;
  5.     do {
  6.         // (1)、获取老的种子
  7.         oldseed = seed.get();
  8.         // (2)、计算出新的种子
  9.         nextseed = (oldseed * multiplier + addend) & mask;
  10.     // (3)、CAS操作更新老的种子
  11.     } while (!seed.compareAndSet(oldseed, nextseed));
  12.     return (int)(nextseed >>> (48 - bits));
  13. }

Random小结:

  • 面试:多线程下Random存在什么样的问题?

每个Random实例里面都有一个原子性的种子变量用来记录当前的种子值,当要生成新的随机数时需要根据当前的种子计算新的种子并更新种子变量。当在多线程环境下,多个线程会竞争同一个原子变量的更新操作,由于原子变量的更新时CAS操作,同时只有一个线程会成功,所以会造成大量线程进行自旋重试,从而降低并发性能。

可能出现的症状:如果并发请求非常多,自旋锁一直重试,那么CPU会一直飙升。

2、ThreadLocalRandom

 
 
 
  1. public static ThreadLocalRandom current() {
  2.     if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
  3.         localInit();
  4.     return instance;
  5. }
 
 
 
  1. static final void localInit() {
  2.     int p = probeGenerator.addAndGet(PROBE_INCREMENT);
  3.     int probe = (p == 0) ? 1 : p; // skip 0
  4.     long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
  5.     Thread t = Thread.currentThread();
  6.     UNSAFE.putLong(t, SEED, seed);
  7.     UNSAFE.putInt(t, PROBE, probe);
  8. }

这个方法用来创建ThreadLocalRandom随机数生成器,如果当前线程中threadLocalRandomProbe的变量值为0,则说明是第一次调用current方法,那么就调用localInit方法初始化种子变量。

这里使用了延迟初始化,在localInit方法中,并没有初始化种子变量,而是在需要生成随机数的时候再生成种子变量,这是一种优化。

 
 
 
  1. static final void localInit() {
  2.     int p = probeGenerator.addAndGet(PROBE_INCREMENT);
  3.     int probe = (p == 0) ? 1 : p; // skip 0
  4.     long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
  5.     Thread t = Thread.currentThread();
  6.     UNSAFE.putLong(t, SEED, seed);
  7.     UNSAFE.putInt(t, PROBE, probe);
  8. }
 
 
 
  1. final long nextSeed() {
  2.     Thread t; long r; // read and update per-thread seed
  3.     // 生成新种子(获取当前线程种子 + 种子增量)
  4.     UNSAFE.putLong(t = Thread.currentThread(), SEED,
  5.                    r = UNSAFE.getLong(t, SEED) + GAMMA);
  6.     return r;
  7. }

mix32是一个固定的算法,这里重点看下nextSeed方法,当第一次调用的时候进行初始化,获取当前线程threadLocalRandomSeed的值(第一次默认值为0) + 种子增量,如果不是第一次那么获取旧种子的值 + 种子增量生成新的种子变量并设置回去。这样的话多线程环境下就避免了竞争,因为threadLocalRandomSeed是Thread的一个变量,属于线程级别。

新闻名称:ThreadLocalRandom类原理分析
文章路径:http://www.shufengxianlan.com/qtweb/news1/60201.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联