Jdk1.8 JUC源码增量解析(1)-atomic-Striped64
转载自 Jdk1.8 JUC源码增量解析(1)-atomic-Striped64
- Striped64是jdk1.8提供的用于支持如Long累加器,Double累加器这样机制的基础类。
- Striped64的设计核心思路就是通过内部的分散计算来避免竞争(比如多线程CAS操作时的竞争)。
- Striped64内部包含一个基础值和一个单元哈希表。没有竞争的情况下,要累加的数会累加到这个基础值上;如果有竞争的话,会将要累加的数累加到单元哈希表中的某个单元里面。所以整个Striped64的值包括基础值和单元哈希表中所有单元的值的总和。
- 先看一下内部结构:
- /**
- * 存放Cell的hash表,大小为2的幂。
- */
- transient volatile Cell[] cells;
- /**
- * 基础值,没有竞争时会使用(更新)这个值,同时做为初始化竞争失败的回退方案。
- * 原子更新。
- */
- transient volatile long base;
- /**
- * 自旋锁,通过CAS操作加锁,用于保护创建或者扩展Cell表。
- */
- transient volatile int cellsBusy;
看下Cell的内部结构:
- @sun.misc.Contended
- static final class Cell {
- volatile long value;
- Cell(long x) { value = x; }
- final boolean cas(long cmp, long val) {
- return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
- }
- // Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long valueOffset;
- static {
- try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> ak = Cell.class;
- valueOffset = UNSAFE.objectFieldOffset
- (ak.getDeclaredField("value"));
- } catch (Exception e) {
- throw new Error(e);
- }
- }
- }
- Striped64主要提供了longAccumulate和doubleAccumulate方法来支持子类,先看下longAccumulate:
- static final int NCPU = Runtime.getRuntime().availableProcessors();
- final void longAccumulate(long x, LongBinaryOperator fn,
- boolean wasUncontended) {
- int h;
- //获取当前线程的probe值作为hash值。
- if ((h = getProbe()) == 0) {
- //如果probe值为0,强制初始化当前线程的probe值,这次初始化的probe值不会为0。
- ThreadLocalRandom.current();
- //再次获取probe值作为hash值。
- h = getProbe();
- //这次相当于再次计算了hash,所以设置未竞争标记为true。
- wasUncontended = true;
- }
- boolean collide = false;
- for (;;) {
- Cell[] as; Cell a; int n; long v;
- if ((as = cells) != null && (n = as.length) > 0) {
- //通过h从cell表中选定一个cell位置。
- if ((a = as[(n - 1) & h]) == null) {
- //如果当前位置没有cell,尝试新建一个。
- if (cellsBusy == 0) {
- //创建一个Cell。
- Cell r = new Cell(x);
- //尝试或者cellsBusy锁。
- if (cellsBusy == 0 && casCellsBusy()) {
- boolean created = false;
- try {
- Cell[] rs; int m, j;
- //在获取锁的情况下再次检测一下。
- if ((rs = cells) != null &&
- (m = rs.length) > 0 &&
- rs[j = (m - 1) & h] == null) {
- //设置新建的cell到指定位置。
- rs[j] = r;
- //创建标记设置为true。
- created = true;
- }
- } finally {
- //释放cellsBusy锁。
- cellsBusy = 0;
- }
- if (created)
- //如果创建成功,直接跳出循环,退出方法。
- break;
- //说明上面指定的cell的位置上有cell了,继续尝试。
- continue;
- }
- }
- //走到这里说明获取cellsBusy锁失败。
- collide = false;
- }
- //以下条件说明上面通过h选定的cell表的位置上有Cell,就是a。
- else if (!wasUncontended) // CAS already known to fail
- //如果之前的CAS失败,说明已经发生竞争,
- //这里会设置未竞争标志位true,然后再次算一个probe值,然后重试。
- wasUncontended = true; // Continue after rehash
- //这里尝试将x值加到a的value上。
- else if (a.cas(v = a.value, ((fn == null) ? v + x :
- fn.applyAsLong(v, x))))
- //如果尝试成功,跳出循环,方法退出。
- break;
- else if (n >= NCPU || cells != as)
- //如果cell表的size已经最大,或者cell表已经发生变化(as是一个过时的)。
- collide = false;
- else if (!collide)
- //设置冲突标志,表示发生了冲突,重试。
- collide = true;
- //尝试获取cellsBusy锁。
- else if (cellsBusy == 0 && casCellsBusy()) {
- try {
- //检测as是否过时。
- if (cells == as) {
- //给cell表扩容。
- Cell[] rs = new Cell[n << 1];
- for (int i = 0; i < n; ++i)
- rs[i] = as[i];
- cells = rs;
- }
- } finally {
- //释放cellsBusy锁。
- cellsBusy = 0;
- }
- collide = false;
- //扩容cell表后,再次重试。
- continue;
- }
- //算出下一个hash值。
- h = advanceProbe(h);
- }
- //如果cell表还未创建,先尝试获取cellsBusy锁。
- else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
- boolean init = false;
- try {
- if (cells == as) {
- //初始化cell表,初始容量为2。
- Cell[] rs = new Cell[2];
- rs[h & 1] = new Cell(x);
- cells = rs;
- init = true;
- }
- } finally {
- //释放cellsBusy锁。
- cellsBusy = 0;
- }
- if (init)
- //初始化cell表成功后,退出方法。
- break;
- }
- //如果创建cell表由于竞争导致失败,尝试将x累加到base上。
- else if (casBase(v = base, ((fn == null) ? v + x :
- fn.applyAsLong(v, x))))
- break;
- }
- }
看下longAccumulate中使用到的一些方法:
- final boolean casBase(long cmp, long val) {
- return UNSAFE.compareAndSwapLong(this, BASE, cmp, val);
- }
- final boolean casCellsBusy() {
- return UNSAFE.compareAndSwapInt(this, CELLSBUSY, 0, 1);
- }
- static final int getProbe() {
- return UNSAFE.getInt(Thread.currentThread(), PROBE);
- }
- //计算下一个随机值作为hash值,使用xorshift算法。
- static final int advanceProbe(int probe) {
- probe ^= probe << 13;
- probe ^= probe >>> 17;
- probe ^= probe << 5;
- //设置到当前线程的threadLocalRandomProbe域。
- UNSAFE.putInt(Thread.currentThread(), PROBE, probe);
- return probe;
- }
- 再看下doubleAccumulate:
- final void doubleAccumulate(double x, DoubleBinaryOperator fn,
- boolean wasUncontended) {
- int h;
- if ((h = getProbe()) == 0) {
- ThreadLocalRandom.current(); // force initialization
- h = getProbe();
- wasUncontended = true;
- }
- boolean collide = false; // True if last slot nonempty
- for (;;) {
- Cell[] as; Cell a; int n; long v;
- if ((as = cells) != null && (n = as.length) > 0) {
- if ((a = as[(n - 1) & h]) == null) {
- if (cellsBusy == 0) { // Try to attach new Cell
- Cell r = new Cell(Double.doubleToRawLongBits(x));
- if (cellsBusy == 0 && casCellsBusy()) {
- boolean created = false;
- try { // Recheck under lock
- Cell[] rs; int m, j;
- if ((rs = cells) != null &&
- (m = rs.length) > 0 &&
- rs[j = (m - 1) & h] == null) {
- rs[j] = r;
- created = true;
- }
- } finally {
- cellsBusy = 0;
- }
- if (created)
- break;
- continue; // Slot is now non-empty
- }
- }
- collide = false;
- }
- else if (!wasUncontended) // CAS already known to fail
- wasUncontended = true; // Continue after rehash
- else if (a.cas(v = a.value,
- ((fn == null) ?
- Double.doubleToRawLongBits
- (Double.longBitsToDouble(v) + x) :
- Double.doubleToRawLongBits
- (fn.applyAsDouble
- (Double.longBitsToDouble(v), x)))))
- break;
- else if (n >= NCPU || cells != as)
- collide = false; // At max size or stale
- else if (!collide)
- collide = true;
- else if (cellsBusy == 0 && casCellsBusy()) {
- try {
- if (cells == as) { // Expand table unless stale
- Cell[] rs = new Cell[n << 1];
- for (int i = 0; i < n; ++i)
- rs[i] = as[i];
- cells = rs;
- }
- } finally {
- cellsBusy = 0;
- }
- collide = false;
- continue; // Retry with expanded table
- }
- h = advanceProbe(h);
- }
- else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
- boolean init = false;
- try { // Initialize table
- if (cells == as) {
- Cell[] rs = new Cell[2];
- rs[h & 1] = new Cell(Double.doubleToRawLongBits(x));
- cells = rs;
- init = true;
- }
- } finally {
- cellsBusy = 0;
- }
- if (init)
- break;
- }
- else if (casBase(v = base,
- ((fn == null) ?
- Double.doubleToRawLongBits
- (Double.longBitsToDouble(v) + x) :
- Double.doubleToRawLongBits
- (fn.applyAsDouble
- (Double.longBitsToDouble(v), x)))))
- break; // Fall back on using base
- }
- }
Jdk1.8 JUC源码增量解析(1)-atomic-Striped64相关推荐
- Jdk1.8 JUC源码增量解析(2)-atomic-LongAdder和LongAccumulator
转载自 Jdk1.8 JUC源码增量解析(2)-atomic-LongAdder和LongAccumulator 功能简介: LongAdder是jdk1.8提供的累加器,基于Striped64实现. ...
- Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue
功能简介: ArrayBlockingQueue是一种基于数组实现的有界的阻塞队列.队列中的元素遵循先入先出(FIFO)的规则.新元素插入到队列的尾部,从队列头部取出元素. 和普通队列有所不同,该队列 ...
- Jdk1.6 JUC源码解析(1)-atomic-AtomicXXX
转自:http://brokendreams.iteye.com/blog/2250109 功能简介: 原子量和普通变量相比,主要体现在读写的线程安全上.对原子量的是原子的(比如多线程下的共享变量i+ ...
- Jdk1.6 JUC源码解析(13)-LinkedBlockingQueue
功能简介: LinkedBlockingQueue是一种基于单向链表实现的有界的(可选的,不指定默认int最大值)阻塞队列.队列中的元素遵循先入先出 (FIFO)的规则.新元素插入到队列的尾部,从队列 ...
- JUC源码分析-线程池篇(五):ForkJoinPool - 2
通过上一篇(JUC源码分析-线程池篇(四):ForkJoinPool - 1)的讲解,相信同学们对 ForkJoinPool 已经有了一个大概的认识,本篇我们将通过分析源码的方式来深入了解 ForkJ ...
- 万字长文|Hashtable源码深度解析以及与HashMap的区别
基于JDK1.8对Java中的Hashtable集合的源码进行了深度解析,包括各种方法.扩容机制.哈希算法.遍历方法等方法的底层实现,最后给出了Hashtable和HashMap的详细对比以及使用建议 ...
- jdk1.8.0_45源码解读——ArrayList的实现
转载自 jdk1.8.0_45源码解读--ArrayList的实现 一.ArrayList概述 ArrayList是List接口的可变数组的实现.实现了所有可选列表操作,并允许包括 null 在内的 ...
- Java LockSupport以及park、unpark方法源码深度解析
介绍了JUC中的LockSupport阻塞工具以及park.unpark方法的底层原理,从Java层面深入至JVM层面. 文章目录 1 LockSupport的概述 2 LockSupport的特征和 ...
- PSINS源码test_SINS_DR解析
PSINS源码test_SINS_DR解析 前言 源码解析 整体介绍 对源码参数的更改 test_SINS_DR脚本 glvf函数 drinit函数 odsimu函数 RMRN函数 imuerrset ...
最新文章
- php写一个shell脚本文件格式,一篇文章学会——shell脚本编写
- python函数参数定义不合法_下列哪种函式参数定义不合法?
- *++p和*p++的区别
- AI+医疗——初识庐山真面目
- mysql 驱动名称_mysql驱动名更新
- moosefs即将发布新版
- Laravel 数据库配置
- 苹果Mac修改图标的一种简单方法
- Java快捷键(学到会慢慢更新)
- lcd12864历程C语言程序,基于51单片机的LCD12864程序设计
- Android 图片虚化,虚化图片,模糊图片
- 读书笔记 - 说话之道 (蔡康永) - 1
- Battleship
- 思维导图|kotlin入门基础语法
- trainning-----1
- 微软Project Server 2016正式版下载:支持项目组合管理-搜狐
- Bzoj3653 谈笑风生
- Word背景默认为绿色,如何更改默认为白色
- 软体艺术系列--抽象工厂 (原文最终修订于2006年10月18日 凌晨04:25:06)
- MATLAB与STK互联46:在场景中加入某个国家作为Area Target对象(GIS命令使用)
热门文章
- [SpringSecurity]web权限方案_用户认证_自定义用户登录页面
- Java继承-子类不可以继承父类的构造方法
- [JavaWeb-MySQL]事务的基本介绍
- C++string容器-子串获取
- matlab中图像轮廓变细,Matlab中,用bwmorph函数提取二进制图像的轮廓
- 数据结构与算法--举例分析法- 栈的压入弹出序列
- ora-00923数据类型不一致_小白学 Python(2):基础数据类型(上)
- XSS(跨站脚本攻击)攻击与防御
- L. Continuous Intervals(单调栈 + 线段树 + 思维)
- P4248 [AHOI2013]差异