原文鏈接:Java CAS 和 synchronized 和 Lock - CSDN博客
CAS 機制
適用場景:樂觀認為并發(fā)不高,不需要阻塞,可以不上鎖。?
特點:不斷比較更新,直到成功。
缺點:高并發(fā)cpu壓力大;ABA問題。
ABA問題:?
CAS機制生效的前提是,取出內(nèi)存中某時刻的數(shù)據(jù),而在下時刻比較并替換。?
如果在比較之前,數(shù)據(jù)發(fā)生了變化,例如:A->B->A,即A變?yōu)锽然后又變化A,那么這個數(shù)據(jù)還是發(fā)生了變化,但是CAS還是會成功。
Java中CAS機制使用版本號進行對比,避免ABA問題。
synchronized
適用場景:悲觀認為并發(fā)很高,需要阻塞,需要上鎖。
特點:語言層面的優(yōu)化,鎖粗化、偏向鎖、輕量鎖等等;可讀性高。
ReentrantLock 和 Atomic類
以上兩種并發(fā)工具都使用了CAS機制。?
在并發(fā)不高競爭不激烈時候,性能略低于synchronized;相反,并發(fā)高競爭激烈時候,性能高于synchronized。
ReentrantLock相對于synchronized:
ReentrantLock等待可中斷,synchronized不可以。
ReentrantLock需要手動釋放鎖,synchronized不需要。
ReentrantLock可支持公平非公平鎖,synchronized只支持非公平鎖。
ReentrantLock沒有語言層面的優(yōu)化,底層實現(xiàn)機制AQS和CAS,synchronized有優(yōu)化。
ReentrantLock可重入鎖,synchronized不可重入,可能導致死鎖。
ReentrantLock支持讀寫鎖,可以提高高并發(fā)讀操作。
synchronized由操作系統(tǒng)支持,涉及內(nèi)核態(tài)和用戶態(tài)的上下文切換,并發(fā)高時切換開銷非常大。
ReentrantLock(AQS)依賴volatile int變量標示鎖狀態(tài),結(jié)構(gòu)為雙向鏈表的等待隊列,通過(cas+死循環(huán))更改鎖狀態(tài),一旦更新成功,標示競爭到鎖。
AQS釋放鎖:?
通過cas改變狀態(tài)
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;
? ? ? ? }
? ? }
獲取鎖:?
如果獲取到鎖,設(shè)為頭節(jié)點,否則一直自旋等待,在高并發(fā)時,可以避免大量鎖競爭的上下文切換,降低線程切換開銷。
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);
? ? ? ? }
? ? }