簡體   English   中英

AtomicReference 中的 compareAndSet 和 weakCompareAndSet 有什么區別?

[英]what's the difference between compareAndSet and weakCompareAndSet in AtomicReference?

源代碼是一樣的。

public final boolean compareAndSet(V expect, V update) {
    return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

public final boolean weakCompareAndSet(V expect, V update) {
    return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

有什么意義?

在 x86 上,LOCK CMPXCHG 指令用於實現 CAS。 它是原子的,提供(接近)最大排序保證並且不會出現虛假故障。 因此,在 x86 平台上,使用較少保證的 CAS 沒有任何好處。

但是在其他平台(例如 PowerPC 或 ARM CAS)上,CAS 是作為多個指令序列實現的,這些指令將LL/SC行為和內存屏障作為單獨的構建塊提供。 這為您的 CAS 在排序和故障保證方面的強大程度創造了一些回旋余地。 相反,這意味着全強度 CAS 的指令序列可能比某些並發算法所需的指令序列成本更高。

許多並發算法涉及在 CAS 失敗時重試或重新計算操作然后重試的循環。 由於 LL/SC 可能會虛假地失敗,因此基於它的強大 CAS 實現必須在內部循環。 如果代碼已經包含一個外循環,它可以通過用允許虛假失敗的弱 CAS 替換強 CAS 來避免內循環。

因此,weakCAS 的存在是為了在弱有序架構上提供更高效的代碼。

javadoc 對弱排序的確切含義含糊不清,因為它目前無法用 java 內存模型來表達。 當它與 C++11 內存模型更緊密地對齊時,可能會在未來進行修訂。

JSR-133 Cookbook的多處理器章節中的表格概述了平台的不同之處。

weakCompareAndSet javadoc 這樣解釋:

如果當前值 == 預期值,則原子地將值設置為給定的更新值。

可能會錯誤地失敗並且不提供排序保證,因此很少是 compareAndSet 的合適替代方案。

簡而言之,javadoc 說weak版本是(或曾經)提供“較弱”保證的版本。

現在,正如您所觀察到的,這兩種方法的當前實現是相同的。 根據 Grepcode 站點上的源代碼,從 Java 6 到 Java 8(至少)都是如此。

所以我推測這兩種方法的實現要么是:

  • 最初不同,但由於對Unsafe的實現進行大修而變得相同:

    • 為方便起見(例如,為了節省實施工作
    • 因為“弱”版本的假定性能,或
    • 因為“弱”版本有問題; 例如,正確使用太難了。
  • 最初相同,並且指定了差異(但未實施),因為設計人員認為可能存在性能優勢。

最后一種解釋不太可能。 如果最初實現的兩個方法相同,重新實現它們可能會破壞預先存在的代碼。 這是一個壞主意,即使對於Unsafe


@assylias / @ Stefan Gobel 評論了另一種解釋。 基本上,我們在源代碼中看到的“相同代碼”實際上可能會被 JIT 編譯器重寫,從而為兩種方法提供不同的機器代碼。

這當然是有道理的。 JIT 編譯器確實為某些(非本地)方法調用生成特殊情況代碼:所謂的“內部函數”。


在 Java 9 中, weakCompareAndSet方法被標記為已棄用。 源碼中的解釋是:

此方法具有簡單的記憶效應,但方法名稱暗示易失性記憶效應(參見 {@link #compareAndExchange} 和 {@link #compareAndSet} 等方法)。 為避免混淆普通或易失​​性記憶效應,建議改用 {@link #weakCompareAndSetPlain} 方法。

另一方面,我們現在看到compareAndSet現在的實現方式與weakCompareAndSet / weakCompareAndSetPlain不同:

public final boolean compareAndSet(V expectedValue, V newValue) {
    return VALUE.compareAndSet(this, expectedValue, newValue);
}

public final boolean weakCompareAndSet(V expectedValue, V newValue) {
    return VALUE.weakCompareAndSetPlain(this, expectedValue, newValue);
}

其中VALUE被聲明為java.lang.invoke.VarHandle 上面使用的VarHandle方法是native並標記為內在候選者。

同樣來自java文檔,看起來其他答案錯過了這個:

原子類還支持適用性有限的方法 weakCompareAndSet。 在某些平台上,弱版本在正常情況下可能比 compareAndSet 更有效,但不同之處在於,任何給定的 weakCompareAndSet 方法調用都可能虛假地返回 false(即,沒有明顯原因)。 錯誤返回僅意味着可以根據需要重試操作,這依賴於保證在變量持有 expectedValue 並且沒有其他線程也嘗試設置變量時重復調用最終會成功。 (例如,這種虛假故障可能是由於與預期值和當前值是否相等無關的內存爭用效應。)此外,weakCompareAndSet 不提供同步控制通常需要的排序保證。 然而,當此類更新與程序的其他發生前排序無關時,該方法可能對更新計數器和統計數據很有用。 當線程看到由weakCompareAndSet 引起的原子變量更新時,它不一定會看到在weakCompareAndSet 之前發生的任何其他變量的更新。 例如,在更新性能統計數據時,這可能是可以接受的,但在其他情況下則很少。

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html#weakCompareAndSet

在功能上,兩者是相同的。 主要區別在於 weakAtomicCompareAndSet 可能會虛假失敗( 請參閱 oracle 文檔)並且不提供排序保證

推薦使用 atomicCompareAndSet 而不是弱版本

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM