文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

ThreadLocalRandom类原理分析

2024-12-02 19:56

关注

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

Random小结:

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

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

2、ThreadLocalRandom

  1. public static ThreadLocalRandom current() { 
  2.     if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0) 
  3.         localInit(); 
  4.     return instance; 
  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); 

这个方法用来创建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); 
  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; 

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

 

来源:一个程序员的成长内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯