Java Concurrent Atomic實(shí)現(xiàn)原理&源碼解讀(JDK 10)

JDK 10囊卜,可以說是很新了,比起JDK 8更新了不少實(shí)現(xiàn)邑闺,比如說下面會講到VarHandle

說了這么多篇原理類的,終于要開始看源碼了棕兼。這一篇描述atomic陡舅,主要簡述Concurrent里面的核心使用的類,其他相關(guān)的伴挚,大家看的時候可以順便進(jìn)去看兩眼內(nèi)容不多靶衍,還可以。

是時候看一眼JDK了茎芋,忽然發(fā)現(xiàn)IDEA可能是讀源碼最好用的工具了摊灭,sun包的暫時不看就單說Concurrent里面的 。

image.png

AtomicBoolean败徊、AtomicInteger等是atomic中比較經(jīng)典的一類,這里不描述API掏缎,就單說源碼實(shí)現(xiàn)皱蹦。

AtomicBoolean

首先看到的是:

public class AtomicBoolean implements java.io.Serializable {
    private static final long serialVersionUID = 4654671469794556979L;
    private static final VarHandle VALUE;
    static {
        try {
            MethodHandles.Lookup l = MethodHandles.lookup();
            VALUE = l.findVarHandle(AtomicBoolean.class, "value", int.class);
        } catch (ReflectiveOperationException e) {
            throw new Error(e);
        }
    }

不僅是AtomicBoolean,其他的原子類也都是可序列化眷蜈,并且持有一個通過JNI調(diào)用本地方法的VarHandle沪哺,這個類實(shí)在sun.lang里實(shí)現(xiàn)的,也不復(fù)雜有興趣可以去看看酌儒。首先完成靜態(tài)屬性的初始化辜妓,(這里可以順道提一點(diǎn)類的初始化順序:父類靜態(tài)變量,父類靜態(tài)初始化塊,子類靜態(tài)變量籍滴,子類靜態(tài)初始化塊酪夷,非靜態(tài)屬性與之相同。父類先于子類孽惰,靜態(tài)屬性是在載入類是就已經(jīng)初始化了晚岭。)
再往下看:

private volatile int value;

一個非對象類型的volatile int value值,這個值是整個AtomicBoolean邏輯的核心勋功,后續(xù)的實(shí)現(xiàn)的功能函數(shù)都會圍繞它展開坦报。1:true 0:false,同時volatile避免的必要是的可見性及防止重排序狂鞋。

/**
* Creates a new {@code AtomicBoolean} with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicBoolean(boolean initialValue) {
    value = initialValue ? 1 : 0;
}
/**
* Creates a new {@code AtomicBoolean} with initial value {@code false}.
*/
public AtomicBoolean() {
}

這個是構(gòu)造函數(shù)片择,可以清晰看到value的使用。

/**
* Returns the current value,
* with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @return the current value
*/
public final boolean get() {
    return value != 0;
}

get函數(shù)直接返回就ok骚揍,因?yàn)槭莢olatile字管,volatile的直接寫操作是原子的,保證了return的值絕對是各線程更新后最新的疏咐,不存在在工作內(nèi)存為刷入主存纤掸,而導(dǎo)致的value值不一致的情況。問題不大浑塞,繼續(xù)向下:

/**
* Atomically sets the value to {@code newValue}
* if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#compareAndSet}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(boolean expectedValue, boolean newValue) {
    return VALUE.compareAndSet(this,
                               (expectedValue ? 1 : 0),
                               (newValue ? 1 : 0));
}

這里開始使用JNI調(diào)用本地函數(shù)了借跪,進(jìn)到源碼里看一眼:這里我就不放注釋了,著實(shí)太長酌壕。(這個方法在VarHandle里)

public final native
@MethodHandle.PolymorphicSignature
@HotSpotIntrinsicCandidate
boolean compareAndSet(Object... args);

可以看到修飾這個函數(shù)的關(guān)鍵詞掏愁,native 關(guān)鍵詞,native主要有兩個作用
1卵牍、JDK依賴于C++函數(shù)直接完成某些系統(tǒng)調(diào)用及函數(shù).
2果港、Java C++聯(lián)合開發(fā)時,可以順道調(diào)用C++工具函數(shù)糊昙,當(dāng)然了驅(qū)動外部程序現(xiàn)在有了ProcessBuilder但是兩者還是存在很大區(qū)別辛掠,JNI是直接調(diào)用的函數(shù),而ProcessBuilder是完成外部完整程序的運(yùn)行释牺,比如說一條shell萝衩,一個Cpp文件,給我們直觀的感受是開了個進(jìn)程没咙。
這里native 的使用場景猩谊,完全符合第一種場景。

Java 8及之前底層能保證比較并操作原子性的方式有這么幾種:(因?yàn)樵诘讓佑懻摷栏眨訟tomicInteger屬于上層實(shí)現(xiàn)了牌捷,這里不算是一種墙牌,但在上層應(yīng)用中還是算一種原子的)

1、使用原子性的FieldUpdaters暗甥,利用了反射機(jī)制喜滨,操作開銷也會更大;
2淋袖、使用sun.misc.Unsafe提供的JVM內(nèi)置函數(shù)API鸿市,雖然這種方式比較快,但它會損害安全性和可移植性即碗。
在Java 9時出現(xiàn)了VarHandle來部分替代java.util.concurrent.atomicsun.misc.Unsafe焰情。

VarHandle 可以與任何字段、數(shù)組元素或靜態(tài)變量關(guān)聯(lián)剥懒,支持在不同訪問模型下對這些類型變量的訪問内舟,包括簡單的 read/write 訪問,volatile 類型的 read/write 訪問初橘,和 CAS(compare-and-swap)等验游。雖然unsafe仍舊保留使用,但Java 9之后用的大部分就都是VarHandle了保檐,官方稱為變量句柄耕蝉,然后VarHandle依賴于VarForm使用,然后VarForm包含一下兩個變量及相關(guān)操作以完成VarHandle的支持夜只。

final @Stable MethodType[] methodType_table;
final @Stable MemberName[] memberName_table;

主要支持鏈接所有簽名的多態(tài)方法垒在,注意compare的第一個注解。
然后第二個注解的含義:
如果使用@SetNativeMethodPrefix注解本地方法扔亥,run的時候就會得到一個警告场躯。
比如:

For instance for Thread::isInterrupted:
Compiler intrinsic is defined for method
[java.lang.Thread.isInterrupted(Z)Z], but the method is not annotated with
@HotSpotIntrinsicCandidate. Method will not be inlined.

然后boolean類型原子性的保證到這里就看完了。

AtomicInteger

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;
    /*
     * This class intended to be implemented using VarHandles, but there
     * are unresolved cyclic startup dependencies.
     */
    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
    private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
    private volatile int value;

跟boolean一樣旅挤,支持序列化踢关,同樣維護(hù)著一個private volatile int value不同的是AtomicInteger是依賴于UnSafe調(diào)用外部方法實(shí)現(xiàn)的粘茄。

下面是Unsafe中compareAndSet的實(shí)現(xiàn):

/**
* Atomically updates Java variable to {@code x} if it is currently
* holding {@code expected}.
*
* <p>This operation has memory semantics of a {@code volatile} read
* and write.  Corresponds to C11 atomic_compare_exchange_strong.
*
* @return {@code true} if successful
*/
@HotSpotIntrinsicCandidate
public final native boolean compareAndSetInt(Object o, long offset,
                                             int expected,
                                             int x);

同樣調(diào)用本地方法签舞,不多贅述。
其他方法的使用柒瓣,不如說+1瘪菌、-1等看官方API就可以了,底層的函數(shù)比較簡單通俗易懂嘹朗。

Atomic*Array

基于VarHandle實(shí)現(xiàn),其中維護(hù)了一個數(shù)組對象诵肛,但是相對于其他對象來說屹培,使用了MethodHandles.arrayElementVarHandle(int[].class)來完成初始默穴。

public class AtomicIntegerArray implements java.io.Serializable {
    private static final long serialVersionUID = 2862133569453604235L;
    private static final VarHandle AA
        = MethodHandles.arrayElementVarHandle(int[].class);
    private final int[] array;

其中所調(diào)用的本地方法也與其他不同,volatile相關(guān)特性依賴于native函數(shù)實(shí)現(xiàn)褪秀。

public final native
@MethodHandle.PolymorphicSignature
@HotSpotIntrinsicCandidate
Object getVolatile(Object... args);

AtomicBoolean:VarHandle
AtomicInteger:UnSafe
AtomicLong:UnSafe
AtomicReference:Varhandle蓄诽,但與AtomicBoolean不同的是AtomicReference多了一個泛型處理。
Atomic里面的實(shí)現(xiàn)總來說就是這樣的媒吗。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仑氛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子闸英,更是在濱河造成了極大的恐慌锯岖,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甫何,死亡現(xiàn)場離奇詭異出吹,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)辙喂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門捶牢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人巍耗,你說我怎么就攤上這事秋麸。” “怎么了炬太?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵灸蟆,是天一觀的道長。 經(jīng)常有香客問我娄琉,道長次乓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任孽水,我火速辦了婚禮票腰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘女气。我一直安慰自己杏慰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布炼鞠。 她就那樣靜靜地躺著缘滥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谒主。 梳的紋絲不亂的頭發(fā)上朝扼,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機(jī)與錄音霎肯,去河邊找鬼擎颖。 笑死榛斯,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的搂捧。 我是一名探鬼主播驮俗,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼允跑!你這毒婦竟也來了王凑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤聋丝,失蹤者是張志新(化名)和其女友劉穎索烹,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體潮针,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡术荤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了每篷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓣戚。...
    茶點(diǎn)故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖焦读,靈堂內(nèi)的尸體忽然破棺而出子库,到底是詐尸還是另有隱情,我是刑警寧澤矗晃,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布仑嗅,位于F島的核電站,受9級特大地震影響张症,放射性物質(zhì)發(fā)生泄漏仓技。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一俗他、第九天 我趴在偏房一處隱蔽的房頂上張望脖捻。 院中可真熱鬧,春花似錦兆衅、人聲如沸地沮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摩疑。三九已至,卻和暖如春畏铆,著一層夾襖步出監(jiān)牢的瞬間雷袋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工辞居, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留片排,地道東北人寨腔。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像率寡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子倚搬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容