1、Random类及其局限性

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

Random小结:

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

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

可能出现的症状:

如果并发请求非常多,自旋锁一直重试,那么CPU会一直飙升。

2、ThreadLocalRandom

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

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

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

public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
// 生成种子
int r = mix32(nextSeed());
int m = bound - 1;
if ((bound & m) == 0) // power of two
r &= m;
else { // reject over-represented candidates
for (int u = r >>> 1;
u + m - (r = u % bound) < 0;
u = mix32(nextSeed()) >>> 1)
;
}
return r;
}
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;
}

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

ThreadLocalRandom类原理分析的更多相关文章

  1. String类原理分析及部分方法

    //String类原理分析及部分方法 //http://www.cnblogs.com/vamei/archive/2013/04/08/3000914.html //http://www.cnblo ...

  2. 78.objects对象所属类原理分析

    def index3(request): # 查找文章题目中包含中国的文章分类 category = Category.objects.filter(article__title__contains= ...

  3. Java并发编程笔记之ThreadLocalRandom源码分析

    JDK 并发包中 ThreadLocalRandom 类原理剖析,经常使用的随机数生成器 Random 类的原理是什么?及其局限性是什么?ThreadLocalRandom 是如何利用 ThreadL ...

  4. 老李推荐:第6章3节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令翻译类

    老李推荐:第6章3节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令翻译类   每个来自网络的字串命令都需要进行解析执行,只是有些是在解析的过程中直接执行 ...

  5. Junit 注解 类加载器 .动态代理 jdbc 连接池 DButils 事务 Arraylist Linklist hashset 异常 哈希表的数据结构,存储过程 Map Object String Stringbufere File类 文件过滤器_原理分析 flush方法和close方法 序列号冲突问题

    Junit 注解 3).其它注意事项: 1).@Test运行的方法,不能有形参: 2).@Test运行的方法,不能有返回值: 3).@Test运行的方法,不能是静态方法: 4).在一个类中,可以同时定 ...

  6. 关于boost中enable_shared_from_this类的原理分析

    首先要说明的一个问题是:如何安全地将this指针返回给调用者.一般来说,我们不能直接将this指针返回.想象这样的情况,该函数将this指针返回到外部某个变量保存,然后这个对象自身已经析构了,但外部变 ...

  7. ConcurrentHashMap原理分析(1.7与1.8)-put和 get 需要执行两次Hash

    ConcurrentHashMap 与HashMap和Hashtable 最大的不同在于:put和 get 两次Hash到达指定的HashEntry,第一次hash到达Segment,第二次到达Seg ...

  8. MapReduce剖析笔记之八: Map输出数据的处理类MapOutputBuffer分析

    在上一节我们分析了Child子进程启动,处理Map.Reduce任务的主要过程,但对于一些细节没有分析,这一节主要对MapOutputBuffer这个关键类进行分析. MapOutputBuffer顾 ...

  9. Handler系列之原理分析

    上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...

随机推荐

  1. ubuntu14.04 安装MySQL 5.7

    ubuntu14.04 默认在线安装MySQL 5.5 1) wget http://dev.mysql.com/get/mysql-apt-config_0.7.3-1_all.deb 2) dpk ...

  2. SQL-DELETE触发器练习

    &练习一 如下所示三张表( student,grade,student_updata_before ): student表 grade表 Student_update_before表 # 触发 ...

  3. 手把手教你 Docker Compose安装DOClever

    一.什么是Docker Compose以及Docker Compose的安装和使用 查看我的另外一篇博客:Docker Compose的安装和使用 二.DOClever是什么 DOClever是一个可 ...

  4. scrum项目冲刺_day07总结

    摘要:今日完成任务. 1.短信服务正在进行 2.路线规划正在进行 总任务: 一.appUI页面(已完成) 二.首页功能: 1.图像识别功能(已完成) 2.语音识别功能(已完成) 3.垃圾搜索功能 4. ...

  5. 解析Markdown文件生成React组件文档

    前言 最近做的项目使用了微前端框架single-spa. 对于这类微前端框架而言,通常有个utility应用,也就是公共应用,里面是各个子应用之间可以共用的一些公共组件或者方法. 对于一个团队而言,项 ...

  6. 对象继承深入、call_apply、圣杯模式、构造函数和闭包,企业模块化

    一个实现加减乘除的插件:   原型其实是在构造函数之上的,构造函数变成实例化函数的时候才会有原型, 原型实际上是构造函数的一个属性 原型无非就是2个字:继承 原型中继承父类所有方法是很不合理的,因为没 ...

  7. ubuntu系统执行生成密匙命令后,home目录下面没有生成.ssh目录

    ubuntu系统配置git ssh时,执行:ssh-keygen -trsa -C "youremail@example.com",home目录下面没有生成.ssh目录. .ssh ...

  8. three.js 材质翻转

    刚学.这个鸟玩意儿卡了半天,记录一下. var skyBox = new THREE.Mesh(skyGeometry, skyMaterial); //创建一个完整的天空盒,填入几何模型和材质的参数 ...

  9. 『GoLang』控制结构

    条件语句 if 是用于测试某个条件(布尔型或逻辑型)的语句,如果该条件成立,则会执行if后由大括号括起来的代码块,否则就忽略该代码块继续执行后续的代码. if condition { // do so ...

  10. bzoj4589-Hard Nim【FWT】

    正题 题目链接:https://darkbzoj.tk/problem/4589 题目大意 求有多少个长度为\(n\)的数列满足它们都是不大于\(m\)的质数且异或和为\(0\). 解题思路 两个初始 ...