Java CAS 和 synchronized 和 Lock
阿新 • • 發佈:2018-12-25
CAS 機制
- 適用場景:樂觀認為併發不高,不需要阻塞,可以不上鎖。
- 特點:不斷比較更新,直到成功。
- 缺點:高併發cpu壓力大;ABA問題。
ABA問題:
CAS機制生效的前提是,取出記憶體中某時刻的資料,而在下時刻比較並替換。
如果在比較之前,資料發生了變化,例如:A->B->A,即A變為B然後又變化A,那麼這個資料還是發生了變化,但是CAS還是會成功。
Java中CAS機制使用版本號進行對比,避免ABA問題。
synchronized
- 適用場景:悲觀認為併發很高,需要阻塞,需要上鎖。
- 特點:語言層面的優化,鎖粗化、偏向鎖、輕量鎖等等;可讀性高。
ReentrantLock 和 Atomic類
以上兩種併發工具都使用了CAS機制。
在併發不高競爭不激烈時候,效能略低於synchronized;相反,併發高競爭激烈時候,效能高於synchronized。
ReentrantLock相對於synchronized:
- ReentrantLock等待可中斷,synchronized不可以。
- ReentrantLock需要手動釋放鎖,synchronized不需要。
- ReentrantLock可支援公平非公平鎖,synchronized只支援非公平鎖。
- ReentrantLock沒有語言層面的優化,底層實現機制AQS和CAS,synchronized有優化。
- ReentrantLock可重入鎖,synchronized不可重入,可能導致死鎖。
- ReentrantLock支援讀寫鎖,可以提高高併發讀操作。
- synchronized由作業系統支援,涉及核心態和使用者態的上下文切換,併發高時切換開銷非常大。
- ReentrantLock(AQS)依賴volatile int變數標示鎖狀態,結構為雙向連結串列的等待佇列,通過(cas+死迴圈)更改鎖狀態,一旦更新成功,標示競爭到鎖。
AQS釋放鎖:
通過cas改變狀態
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in -progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
獲取鎖:
如果獲取到鎖,設為頭節點,否則一直自旋等待,在高併發時,可以避免大量鎖競爭的上下文切換,降低執行緒切換開銷。
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}