生活随笔
收集整理的這篇文章主要介紹了
Jdk1.8 JUC源码增量解析(1)-atomic-Striped64
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
轉載自??Jdk1.8 JUC源碼增量解析(1)-atomic-Striped64
功能簡介:
- Striped64是jdk1.8提供的用于支持如Long累加器,Double累加器這樣機制的基礎類。
- Striped64的設計核心思路就是通過內部的分散計算來避免競爭(比如多線程CAS操作時的競爭)。
- Striped64內部包含一個基礎值和一個單元哈希表。沒有競爭的情況下,要累加的數會累加到這個基礎值上;如果有競爭的話,會將要累加的數累加到單元哈希表中的某個單元里面。所以整個Striped64的值包括基礎值和單元哈希表中所有單元的值的總和。
源碼分析:????transient?volatile?Cell[]?cells;???????transient?volatile?long?base;??????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);??????}????????????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);??????????}??????}??}??? ? ?Cell內部保存了一個volatile修飾的long型域,同時提供了原子操作,看起來像一個原子量。? ? ? ?注意到Cell類被一個Contended注解修飾,Contended的作用是對Cell做緩存行填充,避免偽共享。- Striped64主要提供了longAccumulate和doubleAccumulate方法來支持子類,先看下longAccumulate:
static?final?int?NCPU?=?Runtime.getRuntime().availableProcessors();??final?void?longAccumulate(long?x,?LongBinaryOperator?fn,????????????????????????????boolean?wasUncontended)?{??????int?h;????????????if?((h?=?getProbe())?==?0)?{????????????????????ThreadLocalRandom.current();?????????????????????h?=?getProbe();????????????????????wasUncontended?=?true;??????}??????boolean?collide?=?false;??????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)?{????????????????????????????????????????????Cell?r?=?new?Cell(x);?????????????????????????????????????????????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)?{????????????????????????????????????????????????????????????????????rs[j]?=?r;????????????????????????????????????????????????????????????????????created?=?true;??????????????????????????????}??????????????????????????}?finally?{????????????????????????????????????????????????????????????cellsBusy?=?0;??????????????????????????}??????????????????????????if?(created)????????????????????????????????????????????????????????????break;????????????????????????????????????????????????????continue;????????????????????????}??????????????????}????????????????????????????????????collide?=?false;??????????????}????????????????????????????else?if?(!wasUncontended)?????????????????????????????????????????????????????????????wasUncontended?=?true;??????????????????????????????????else?if?(a.cas(v?=?a.value,?((fn?==?null)???v?+?x?:???????????????????????????????????????????fn.applyAsLong(v,?x))))????????????????????????????????????break;??????????????else?if?(n?>=?NCPU?||?cells?!=?as)????????????????????????????????????collide?=?false;?????????????????????????else?if?(!collide)????????????????????????????????????collide?=?true;????????????????????????????else?if?(cellsBusy?==?0?&&?casCellsBusy())?{??????????????????try?{????????????????????????????????????????????if?(cells?==?as)?{??????????????????????????????????????????????????????????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;????????????????????????????????}????????????????????????????h?=?advanceProbe(h);??????????}????????????????????else?if?(cellsBusy?==?0?&&?cells?==?as?&&?casCellsBusy())?{??????????????boolean?init?=?false;??????????????try?{????????????????????????????????????????????if?(cells?==?as)?{????????????????????????????????????????????Cell[]?rs?=?new?Cell[2];??????????????????????rs[h?&?1]?=?new?Cell(x);??????????????????????cells?=?rs;??????????????????????init?=?true;??????????????????}??????????????}?finally?{????????????????????????????????????cellsBusy?=?0;??????????????}??????????????if?(init)????????????????????????????????????break;??????????}????????????????????else?if?(casBase(v?=?base,?((fn?==?null)???v?+?x?:??????????????????????????????????????fn.applyAsLong(v,?x))))??????????????break;????????????????????????????????}??}??說明一下這個方法,方法的作用是將給定的值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中使用到的一些方法:
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);??}??????static?final?int?advanceProbe(int?probe)?{??????probe?^=?probe?<<?13;??????probe?^=?probe?>>>?17;??????probe?^=?probe?<<?5;????????????UNSAFE.putInt(Thread.currentThread(),?PROBE,?probe);??????return?probe;??}????
final?void?doubleAccumulate(double?x,?DoubleBinaryOperator?fn,??????????????????????????????boolean?wasUncontended)?{??????int?h;??????if?((h?=?getProbe())?==?0)?{??????????ThreadLocalRandom.current();???????????h?=?getProbe();??????????wasUncontended?=?true;??????}??????boolean?collide?=?false;??????????????????????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)?{?????????????????????????????Cell?r?=?new?Cell(Double.doubleToRawLongBits(x));??????????????????????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)?{??????????????????????????????????rs[j]?=?r;??????????????????????????????????created?=?true;??????????????????????????????}??????????????????????????}?finally?{??????????????????????????????cellsBusy?=?0;??????????????????????????}??????????????????????????if?(created)??????????????????????????????break;??????????????????????????continue;?????????????????????????????????}??????????????????}??????????????????collide?=?false;??????????????}??????????????else?if?(!wasUncontended)?????????????????????????wasUncontended?=?true;????????????????????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;??????????????????????????else?if?(!collide)??????????????????collide?=?true;??????????????else?if?(cellsBusy?==?0?&&?casCellsBusy())?{??????????????????try?{??????????????????????if?(cells?==?as)?{????????????????????????????????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;?????????????????????????????????}??????????????h?=?advanceProbe(h);??????????}??????????else?if?(cellsBusy?==?0?&&?cells?==?as?&&?casCellsBusy())?{??????????????boolean?init?=?false;??????????????try?{?????????????????????????????????????????????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;????????????????????????????????}??}??doubleAccumulate方法是針對double值做累加的,邏輯和longAccumulate一致。但由于Cell內部用long保存數據,所以在累加的時候會利用Double的doubleToRawLongBits和longBitsToDouble方法做double和longBits形式的double之間的轉換。Striped64的代碼解析完畢!參考資料:http://mail.openjdk.java.net/pipermail/hotspot-dev/2012-November/007309.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的Jdk1.8 JUC源码增量解析(1)-atomic-Striped64的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。