Jdk1.8 JUC源码增量解析(1)-atomic-Striped64

转载自  Jdk1.8 JUC源码增量解析(1)-atomic-Striped64

功能简介:
  • Striped64是jdk1.8提供的用于支持如Long累加器,Double累加器这样机制的基础类。
  • Striped64的设计核心思路就是通过内部的分散计算来避免竞争(比如多线程CAS操作时的竞争)。
  • Striped64内部包含一个基础值和一个单元哈希表。没有竞争的情况下,要累加的数会累加到这个基础值上;如果有竞争的话,会将要累加的数累加到单元哈希表中的某个单元里面。所以整个Striped64的值包括基础值和单元哈希表中所有单元的值的总和。
源码分析:
  • 先看一下内部结构:
  1. /** 
  2.  * 存放Cell的hash表,大小为2的幂。 
  3.  */  
  4. transient volatile Cell[] cells;  
  5. /** 
  6.  * 基础值,没有竞争时会使用(更新)这个值,同时做为初始化竞争失败的回退方案。 
  7.  * 原子更新。 
  8.  */  
  9. transient volatile long base;  
  10. /** 
  11.  * 自旋锁,通过CAS操作加锁,用于保护创建或者扩展Cell表。 
  12.  */  
  13. transient volatile int cellsBusy;  

 

       看下Cell的内部结构:

  1. @sun.misc.Contended   
  2. static final class Cell {  
  3.     volatile long value;  
  4.     Cell(long x) { value = x; }  
  5.     final boolean cas(long cmp, long val) {  
  6.         return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);  
  7.     }  
  8.     // Unsafe mechanics  
  9.     private static final sun.misc.Unsafe UNSAFE;  
  10.     private static final long valueOffset;  
  11.     static {  
  12.         try {  
  13.             UNSAFE = sun.misc.Unsafe.getUnsafe();  
  14.             Class<?> ak = Cell.class;  
  15.             valueOffset = UNSAFE.objectFieldOffset  
  16.                 (ak.getDeclaredField("value"));  
  17.         } catch (Exception e) {  
  18.             throw new Error(e);  
  19.         }  
  20.     }  
  21. }  
     Cell内部保存了一个volatile修饰的long型域,同时提供了原子操作,看起来像一个原子量。
       注意到Cell类被一个Contended注解修饰,Contended的作用是对Cell做缓存行填充,避免伪共享。
  • Striped64主要提供了longAccumulate和doubleAccumulate方法来支持子类,先看下longAccumulate:
  1. static final int NCPU = Runtime.getRuntime().availableProcessors();  
  2. final void longAccumulate(long x, LongBinaryOperator fn,  
  3.                           boolean wasUncontended) {  
  4.     int h;  
  5.     //获取当前线程的probe值作为hash值。  
  6.     if ((h = getProbe()) == 0) {  
  7.         //如果probe值为0,强制初始化当前线程的probe值,这次初始化的probe值不会为0。  
  8.         ThreadLocalRandom.current();   
  9.         //再次获取probe值作为hash值。  
  10.         h = getProbe();  
  11.         //这次相当于再次计算了hash,所以设置未竞争标记为true。  
  12.         wasUncontended = true;  
  13.     }  
  14.     boolean collide = false;  
  15.     for (;;) {  
  16.         Cell[] as; Cell a; int n; long v;  
  17.         if ((as = cells) != null && (n = as.length) > 0) {  
  18.             //通过h从cell表中选定一个cell位置。  
  19.             if ((a = as[(n - 1) & h]) == null) {  
  20.                 //如果当前位置没有cell,尝试新建一个。  
  21.                 if (cellsBusy == 0) {  
  22.                     //创建一个Cell。         
  23.                     Cell r = new Cell(x);   
  24.                     //尝试或者cellsBusy锁。  
  25.                     if (cellsBusy == 0 && casCellsBusy()) {  
  26.                         boolean created = false;  
  27.                         try {                 
  28.                             Cell[] rs; int m, j;  
  29.                             //在获取锁的情况下再次检测一下。  
  30.                             if ((rs = cells) != null &&  
  31.                                 (m = rs.length) > 0 &&  
  32.                                 rs[j = (m - 1) & h] == null) {  
  33.                                 //设置新建的cell到指定位置。  
  34.                                 rs[j] = r;  
  35.                                 //创建标记设置为true。  
  36.                                 created = true;  
  37.                             }  
  38.                         } finally {  
  39.                             //释放cellsBusy锁。  
  40.                             cellsBusy = 0;  
  41.                         }  
  42.                         if (created)  
  43.                             //如果创建成功,直接跳出循环,退出方法。  
  44.                             break;  
  45.                         //说明上面指定的cell的位置上有cell了,继续尝试。  
  46.                         continue;    
  47.                     }  
  48.                 }  
  49.                 //走到这里说明获取cellsBusy锁失败。  
  50.                 collide = false;  
  51.             }  
  52.             //以下条件说明上面通过h选定的cell表的位置上有Cell,就是a。  
  53.             else if (!wasUncontended)       // CAS already known to fail  
  54.                 //如果之前的CAS失败,说明已经发生竞争,  
  55.                 //这里会设置未竞争标志位true,然后再次算一个probe值,然后重试。  
  56.                 wasUncontended = true;      // Continue after rehash  
  57.             //这里尝试将x值加到a的value上。  
  58.             else if (a.cas(v = a.value, ((fn == null) ? v + x :  
  59.                                          fn.applyAsLong(v, x))))  
  60.                 //如果尝试成功,跳出循环,方法退出。  
  61.                 break;  
  62.             else if (n >= NCPU || cells != as)  
  63.                 //如果cell表的size已经最大,或者cell表已经发生变化(as是一个过时的)。  
  64.                 collide = false;             
  65.             else if (!collide)  
  66.                 //设置冲突标志,表示发生了冲突,重试。  
  67.                 collide = true;  
  68.             //尝试获取cellsBusy锁。  
  69.             else if (cellsBusy == 0 && casCellsBusy()) {  
  70.                 try {  
  71.                     //检测as是否过时。  
  72.                     if (cells == as) {        
  73.                         //给cell表扩容。  
  74.                         Cell[] rs = new Cell[n << 1];  
  75.                         for (int i = 0; i < n; ++i)  
  76.                             rs[i] = as[i];  
  77.                         cells = rs;  
  78.                     }  
  79.                 } finally {  
  80.                     //释放cellsBusy锁。  
  81.                     cellsBusy = 0;  
  82.                 }  
  83.                 collide = false;  
  84.                 //扩容cell表后,再次重试。  
  85.                 continue;                    
  86.             }  
  87.             //算出下一个hash值。  
  88.             h = advanceProbe(h);  
  89.         }  
  90.         //如果cell表还未创建,先尝试获取cellsBusy锁。  
  91.         else if (cellsBusy == 0 && cells == as && casCellsBusy()) {  
  92.             boolean init = false;  
  93.             try {                            
  94.                 if (cells == as) {  
  95.                     //初始化cell表,初始容量为2。  
  96.                     Cell[] rs = new Cell[2];  
  97.                     rs[h & 1] = new Cell(x);  
  98.                     cells = rs;  
  99.                     init = true;  
  100.                 }  
  101.             } finally {  
  102.                 //释放cellsBusy锁。  
  103.                 cellsBusy = 0;  
  104.             }  
  105.             if (init)  
  106.                 //初始化cell表成功后,退出方法。  
  107.                 break;  
  108.         }  
  109.         //如果创建cell表由于竞争导致失败,尝试将x累加到base上。  
  110.         else if (casBase(v = base, ((fn == null) ? v + x :  
  111.                                     fn.applyAsLong(v, x))))  
  112.             break;                            
  113.     }  
  114. }  
说明一下这个方法,方法的作用是将给定的值x累加到当前值(Striped64本身)上,x值为正就是加、为负就是减。
方法流程细节:
       首先,方法内部首先会算一个hash值,用来确定cell数组的下标。hash值初始源于当前Thread中的threadLocalRandomProbe域,如果hash值初始后为0,会初始化一下当前线程的threadLocalRandomProbe值,然后再次赋给hash值。注意方法传入第三个参数wasUncontended表示调用方法之前是否未发生竞争,加入前面走了初始化threadLocalRandomProbe的过程,就会将wasUncontended设置为true。
       接下来,方法进入主循环。
       1.先判断Cell表是否创建。
       1.1.如果Cell表未创建,尝试获取cellsBusy锁。
       1.1.1.如果获取cellsBusy锁成功,会创建一个size为2的Cell表作为初始cell表,然后新建一个保存给定x的Cell实例,然后根据hash值设置到Cell表对应的位置上;
       1.1.2.如果获取cellsBusy锁失败,会尝试将x累加到base上,失败重试。
       1.2.如果Cell表已经创建,通过hash值算出一个Cell表中的位置,然后获取这个位置上的Cell,称为a。
       1.2.1.如果a为null,尝试获取cellsBusy锁。
       1.2.1.1.如果获取cellsBusy成功,创建一个新的Cell,然后赋值给a,方法退出。(过程中需要多次检测冲突)
       1.2.1.2.如果获取cellsBusy失败,会将collide设置为false(实际上是表示发生了冲突),然后重试。
       1.2.2.如果a不为null。
       1.2.2.1.如果wasUncontended为false,说明之前发生过CAS竞争失败,设置wasUncontended为true,重新计算hash值,重试;如果wasUncontended为true,继续尝试下面过程。
       1.2.2.2.尝试通过CAS方式将x累加到a的value上,如果尝试成功,方法退出;如果尝试失败,继续尝试下面过程。
       1.2.2.3.如果当前Cell表的大小以及达到最大值(当前处理器核数),或者Cell表发生了变化(竞争导致过时),那么会设置collide为false,重新计算hash值,然后重试;否则,继续尝试下面过程。
       1.2.2.4.如果collide为false,说明之前发生过冲突,将collide设置为true,重新计算hash值,然后重试;否则,继续尝试下面过程。
       1.2.2.5.尝试获取cellsBusy,如果成功,扩展Cell表,并将collide设置为false,然后重试;否则,重新计算hash值,然后重试;

       看下longAccumulate中使用到的一些方法:

  1. final boolean casBase(long cmp, long val) {  
  2.     return UNSAFE.compareAndSwapLong(this, BASE, cmp, val);  
  3. }  
  4.   
  5. final boolean casCellsBusy() {  
  6.     return UNSAFE.compareAndSwapInt(this, CELLSBUSY, 01);  
  7. }  
  8.   
  9. static final int getProbe() {  
  10.     return UNSAFE.getInt(Thread.currentThread(), PROBE);  
  11. }  
  12.   
  13. //计算下一个随机值作为hash值,使用xorshift算法。  
  14. static final int advanceProbe(int probe) {  
  15.     probe ^= probe << 13;  
  16.     probe ^= probe >>> 17;  
  17.     probe ^= probe << 5;  
  18.     //设置到当前线程的threadLocalRandomProbe域。  
  19.     UNSAFE.putInt(Thread.currentThread(), PROBE, probe);  
  20.     return probe;  
  21. }  

  

  • 再看下doubleAccumulate:
  1. final void doubleAccumulate(double x, DoubleBinaryOperator fn,  
  2.                             boolean wasUncontended) {  
  3.     int h;  
  4.     if ((h = getProbe()) == 0) {  
  5.         ThreadLocalRandom.current(); // force initialization  
  6.         h = getProbe();  
  7.         wasUncontended = true;  
  8.     }  
  9.     boolean collide = false;                // True if last slot nonempty  
  10.     for (;;) {  
  11.         Cell[] as; Cell a; int n; long v;  
  12.         if ((as = cells) != null && (n = as.length) > 0) {  
  13.             if ((a = as[(n - 1) & h]) == null) {  
  14.                 if (cellsBusy == 0) {       // Try to attach new Cell  
  15.                     Cell r = new Cell(Double.doubleToRawLongBits(x));  
  16.                     if (cellsBusy == 0 && casCellsBusy()) {  
  17.                         boolean created = false;  
  18.                         try {               // Recheck under lock  
  19.                             Cell[] rs; int m, j;  
  20.                             if ((rs = cells) != null &&  
  21.                                 (m = rs.length) > 0 &&  
  22.                                 rs[j = (m - 1) & h] == null) {  
  23.                                 rs[j] = r;  
  24.                                 created = true;  
  25.                             }  
  26.                         } finally {  
  27.                             cellsBusy = 0;  
  28.                         }  
  29.                         if (created)  
  30.                             break;  
  31.                         continue;           // Slot is now non-empty  
  32.                     }  
  33.                 }  
  34.                 collide = false;  
  35.             }  
  36.             else if (!wasUncontended)       // CAS already known to fail  
  37.                 wasUncontended = true;      // Continue after rehash  
  38.             else if (a.cas(v = a.value,  
  39.                            ((fn == null) ?  
  40.                             Double.doubleToRawLongBits  
  41.                             (Double.longBitsToDouble(v) + x) :  
  42.                             Double.doubleToRawLongBits  
  43.                             (fn.applyAsDouble  
  44.                              (Double.longBitsToDouble(v), x)))))  
  45.                 break;  
  46.             else if (n >= NCPU || cells != as)  
  47.                 collide = false;            // At max size or stale  
  48.             else if (!collide)  
  49.                 collide = true;  
  50.             else if (cellsBusy == 0 && casCellsBusy()) {  
  51.                 try {  
  52.                     if (cells == as) {      // Expand table unless stale  
  53.                         Cell[] rs = new Cell[n << 1];  
  54.                         for (int i = 0; i < n; ++i)  
  55.                             rs[i] = as[i];  
  56.                         cells = rs;  
  57.                     }  
  58.                 } finally {  
  59.                     cellsBusy = 0;  
  60.                 }  
  61.                 collide = false;  
  62.                 continue;                   // Retry with expanded table  
  63.             }  
  64.             h = advanceProbe(h);  
  65.         }  
  66.         else if (cellsBusy == 0 && cells == as && casCellsBusy()) {  
  67.             boolean init = false;  
  68.             try {                           // Initialize table  
  69.                 if (cells == as) {  
  70.                     Cell[] rs = new Cell[2];  
  71.                     rs[h & 1] = new Cell(Double.doubleToRawLongBits(x));  
  72.                     cells = rs;  
  73.                     init = true;  
  74.                 }  
  75.             } finally {  
  76.                 cellsBusy = 0;  
  77.             }  
  78.             if (init)  
  79.                 break;  
  80.         }  
  81.         else if (casBase(v = base,  
  82.                          ((fn == null) ?  
  83.                           Double.doubleToRawLongBits  
  84.                           (Double.longBitsToDouble(v) + x) :  
  85.                           Double.doubleToRawLongBits  
  86.                           (fn.applyAsDouble  
  87.                            (Double.longBitsToDouble(v), x)))))  
  88.             break;                          // Fall back on using base  
  89.     }  
  90. }  
doubleAccumulate方法是针对double值做累加的,逻辑和longAccumulate一致。但由于Cell内部用long保存数据,所以在累加的时候会利用Double的doubleToRawLongBits和longBitsToDouble方法做double和longBits形式的double之间的转换。
Striped64的代码解析完毕!
参考资料:http://mail.openjdk.java.net/pipermail/hotspot-dev/2012-November/007309.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/329152.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java内部类选择题_java内部类详解(附相关面试题)

说起内部类这个词&#xff0c;想必很多人都不陌生&#xff0c;但是又会觉得不熟悉。原因是平时编写代码时可能用到的场景不多&#xff0c;用得最多的是在有事件监听的情况下&#xff0c;并且即使用到也很少去总结内部类的用法。今天我们就来一探究竟。一.内部类基础在Java中&am…

开源,新的平台之战

近日&#xff0c;OpenDaylight项目的执行总监Neela Jacques在文章《开源的转变&#xff1a;一种新的平台战争》 中提到&#xff1a;开源已经成为软件公司业务战略的关键&#xff0c;是一种新的平台之战。 多年来&#xff0c;开源软件似乎处于技术产业的边缘。而如今&#xff0c…

java下载图片到手机相册_Unity保存图片到Android手机且更新相册

Android 保存图片到设备前言:在许多的应用或游戏中,大多都有保存图片或者截图等等的功能,这篇文档我们的目的是通过 Unity 保存图片,并且调用 Andorid 中的更新相册的原生方法.流程步骤:编写更新相册的 Android 原生接口 -> Unity 编写保存图片逻辑以及调用更新相册 Android…

如何在 Java 中正确使用 wait, notify 和 notifyAll – 以生产者消费者模型为例

转载自 如何在 Java 中正确使用 wait, notify 和 notifyAll – 以生产者消费者模型为例 wait, notify 和 notifyAll&#xff0c;这些在多线程中被经常用到的保留关键字&#xff0c;在实际开发的时候很多时候却并没有被大家重视。本文对这些关键字的使用进行了描述。 在 Java 中…

.NET Core 使用Dapper 操作MySQL

.NET Core 使用Dapper 操作MySQL 数据库&#xff0c; .NET Core 使用Dapper。 目前官方没有出.NET Core MySQL 驱动&#xff0c;但是已经有第三方进行改动封装出.NET Core MySQL Connector 预览版。 Dapper 也已经出了 .NET Core 预览版。 Dapper dot net 是一个轻量型的ORM&a…

Angular 2与TypeScript概览

迄今为止&#xff0c;在创建Web应用方面&#xff0c;AngularJS是当前最为流行的JavaScript框架。如今&#xff0c;Angular 2和TypeScript通过一种非常类似于Java 8的语法&#xff0c;使真正面向对象的Web开发成为了主流。 据Google的工程主管Brad Green介绍&#xff0c;有130万…

正确使用 Volatile 变量

转载自 Java 理论与实践 - 正确使用 Volatile 变量 - volatile 变量使用指南Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”&#xff1b;与 synchronized 块相比&#xff0c;volatile 变量所需的编码较少&#xff0c;并且运行时开销也较少&#xf…

java龟兔赛跑设计思路_JAVA程序设计(09)-----面对对象设计初级应用 龟兔赛跑

1.乌龟和兔子共有属性和方法 做成父类 避免重复代码package com.lovo;/*** 类&#xff1a; 动物* author Abe* 属性&#xff1a; 名字 步距 总距离 睡觉的日子*/public class Animal {protected String name;protected int step;protected int distance;protected int sleepDay…

16年国庆假期期间兼职所悟

2016年9月25日&#xff0c;学校放假了&#xff01;&#xff01;&#xff01; 学校放假11天&#xff0c;10月7号才开学&#xff0c;除了晚上上个夜班之外别的时间都在闲着&#xff0c;这么大的自己感觉闲着真不是滋味&#xff0c;于是开始疯狂的在58上找工作&#xff0c;心里想…

python flask项目过程_Python 开发过程遇到的问题

另一方面&#xff0c;也是因为时间原因&#xff0c;没有事先系统了解 python 的具体内容&#xff0c;所以开发过程中基本都是拿 java 的东西往 python 里面套。比如&#xff1a;某个功能用 java 的 ArrayList 可以解决&#xff0c;那 python 中有没有类似的东西呢&#xff1f;j…

Java 中的双重检查(Double-Check)

转载自 Java 中的双重检查&#xff08;Double-Check&#xff09; 在 Effecitve Java 一书的第 48 条中提到了双重检查模式&#xff0c;并指出这种模式在 Java 中通常并不适用。该模式的结构如下所示&#xff1a; public Resource getResource() { if (resource null) { …

使用 Autofac 进行依赖注入

先说下为什么翻译这篇文章&#xff0c;既定的方向是架构&#xff0c;然后为了学习架构就去学习一些架构模式、设计思想。 突然有一天发现依赖注入这种技能。为了使得架构可测试、易维护、可扩展&#xff0c;需要架构设计为松耦合类型&#xff0c;简单的说也就是解耦。为了解耦前…

组合的示例代码 java_java实现Composite组合模式的实例代码

//20210121写在前面&#xff1a;刚期末考试完&#xff0c;考了面向对象&#xff0c;里边儿有23个设计模式&#xff0c;我寻思着考完挨个儿实现一下&#xff0c;本文实现组合模式组合模式核心思想类似文件夹的概念&#xff0c;构件树形结构&#xff0c;树形有叶子结点和文件夹结…

Java中的ThreadPoolExecutor类

转载自 Java中的ThreadPoolExecutor类在前面的文章中&#xff0c;我们使用线程的时候就去创建一个线程&#xff0c;这样实现起来非常简便&#xff0c;但是就会有一个问题&#xff1a; 如果并发的线程数量很多&#xff0c;并且每个线程都是执行一个时间很短的任务就结束了&…

webpack 前端构建

一、建立简单的项目目录 1、创建 manager 根目录(作为项目根目录)2、执行 npm init&#xff0c;在根目录manager下自动生成 package.json文件3、npm install webpack --save-dev&#xff0c;在项目中安装 webpack npm包4、在根目录下 创建 webpack.config.js&#xff0c;所有的…

简析 .NET Core 构成体系

简析 .NET Core 构成体系Roslyn 编译器RyuJIT 编译器CoreCLR & CoreRTCoreFX(.NET Core Libraries).NET Core 代码开发、部署、运行过程总结 前文介绍了.NET Core 在整个.NET 平台所处的地位&#xff0c;以及与.NET Framework的关系(原文链接)&#xff0c;本文将详细介绍.N…

判断一个男人穷还是富,只看这几点!

转载至&#xff1a; 来源&#xff1a;甜蜜爸妈手记&#xff08;wxtm01&#xff09; 作者&#xff1a;甜甜妈 创业君 导读 千主意万主意&#xff0c;如果不行动&#xff0c;永远就只是个想法而已。好想法要配得上行动才行。 看看他的爱好一个有事业心男人&#xff0c;绝对不…

php制作留言板的题_PHP实现留言板功能实例代码

本文实例为大家分享了php留言板的实现思路&#xff0c;供大家参考&#xff0c;具体内容如下&#xff1a;1.创建一个存放留言信息的文件名2.获取表单中的数据给一个变量3.判断文件的时候存在4.对文件执行写的操作&#xff0c;在这之前&#xff0c;注意打开文件的时候&#xff0c…

Java线程池,从使用到原理

转载自 Java线程池&#xff0c;从使用到原理线程池的技术背景 在面向对象编程中&#xff0c;创建和销毁对象是很费时间的&#xff0c;因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此&#xff0c;虚拟机将试图跟踪每一个对象&#xff0c;以便能够在对象销毁…

聊聊HTTPS和SSL/TLS协议

要说清楚 HTTPS 协议的实现原理&#xff0c;至少需要如下几个背景知识。1. 大致了解几个基本术语&#xff08;HTTPS、SSL、TLS&#xff09;的含义2. 大致了解 HTTP 和 TCP 的关系&#xff08;尤其是“短连接”VS“长连接”&#xff09;3. 大致了解加密算法的概念&#xff08;尤…