鎖的劣勢
使用鎖有以下的缺點:
-
如果線程持有鎖而延遲,會導致其他的線程的等待。
-
高優先級的線程阻塞,而低優先級的線程持有鎖造成 優先級反轉(priority inversion)。
-
如果持有鎖的線程被永久地阻塞,所有等待這個鎖的線程就無法執行下去,造成 死鎖(dead lock)。
爲了避免這些問題,我們更傾向於非阻塞算法來保證同步。
比較交換(CAS)
CAS的含義是:“我認爲V的值應該是A,如果是,那麼將V的值更新爲B,否則不修改並告訴V的值實際爲多少”。CAS是一項樂觀鎖的技術,它希望能成功地執行更新操作,並且如果有另外一個線程在最近一次檢查後更新了該變量,那麼CAS能檢測到這個錯誤。
原子變量
原子變量是JVM對CAS的支持。
原子變量比鎖的粒度更細,量級更輕,並且對於在多處理器系統上實現高性能的併發代碼來說是非常關鍵的。原子變量將發生競爭的範圍縮小到單個變量上,這是你獲得的粒度最新的情況。
原子變量是一種“更好的volatile”。
原子變量主要包括java.util.concurrent.atomic 包下的類。
- AtomicBoolean 可以用原子方式更新的 boolean 值。
- AtomicInteger 可以用原子方式更新的 int 值。
- AtomicIntegerArray 可以用原子方式更新其元素的 int 數組。
- AtomicIntegerFieldUpdater 基於反射的實用工具,可以對指定類的指定 volatile int 字段進行原子更新。
- AtomicLong 可以用原子方式更新的 long 值。
- AtomicLongArray 可以用原子方式更新其元素的 long 數組。
- AtomicLongFieldUpdater 基於反射的實用工具,可以對指定類的指定 volatile long 字段進行原子更新。
- AtomicMarkableReference AtomicMarkableReference 維護帶有標記位的對象引用,可以原子方式對其進行更新。
- AtomicReference 可以用原子方式更新的對象引用。
- AtomicReferenceArray 可以用原子方式更新其元素的對象引用數組。
- AtomicReferenceFieldUpdater<T,V> 基於反射的實用工具,可以對指定類的指定 volatile 字段進行原子更新。
- AtomicStampedReference AtomicStampedReference 維護帶有整數“標誌”的對象引用,可以用原子方式對其進行更新。
ABA問題
在某些算法中,如果V的值首先由A變成B,再由B變成A,那麼結果看起來沒變,實際上數據已經發生過變化了。
ABA問題是一種異常現象:如果在算法中的節點可以被循環使用,那麼在使用“比較並交換”指令時就可能出現這種問題(主要在沒有垃圾回收機制的環境中)。在CAS操作中將判斷“V的值是否仍然是A?”,並且如果是的話就繼續執行更新操作。在大多數情況下,這種判斷是完全足夠的。然而,有時候還需要知道“自從上次看到V的值爲A以來,這個值是否發生了變化?”
在某些算法中,如果V的值首先由A變成B,再由B變成A,那麼結果看起來沒變,實際上數據已經發生過變化了。
有一個相對簡單的解決方案:不是更新某個引用的值,而是更新兩個值,包括一個引用和一個版本號。即使這個值由A變成 B,然後又變成A,版本號也將是不同的。