原文鏈接:Java CAS 和 synchronized 和 Lock - CSDN博客
CAS 機(jī)制
適用場景:樂觀認(rèn)為并發(fā)不高侈询,不需要阻塞舌涨,可以不上鎖。?
特點(diǎn):不斷比較更新扔字,直到成功囊嘉。
缺點(diǎn):高并發(fā)cpu壓力大;ABA問題革为。
ABA問題:?
CAS機(jī)制生效的前提是扭粱,取出內(nèi)存中某時(shí)刻的數(shù)據(jù),而在下時(shí)刻比較并替換震檩。?
如果在比較之前琢蛤,數(shù)據(jù)發(fā)生了變化蜓堕,例如:A->B->A,即A變?yōu)锽然后又變化A博其,那么這個(gè)數(shù)據(jù)還是發(fā)生了變化套才,但是CAS還是會(huì)成功。
Java中CAS機(jī)制使用版本號(hào)進(jìn)行對(duì)比慕淡,避免ABA問題霜旧。
synchronized
適用場景:悲觀認(rèn)為并發(fā)很高,需要阻塞儡率,需要上鎖。
特點(diǎn):語言層面的優(yōu)化以清,鎖粗化儿普、偏向鎖、輕量鎖等等掷倔;可讀性高眉孩。
ReentrantLock 和 Atomic類
以上兩種并發(fā)工具都使用了CAS機(jī)制。?
在并發(fā)不高競爭不激烈時(shí)候勒葱,性能略低于synchronized浪汪;相反,并發(fā)高競爭激烈時(shí)候凛虽,性能高于synchronized死遭。
ReentrantLock相對(duì)于synchronized:
ReentrantLock等待可中斷,synchronized不可以凯旋。
ReentrantLock需要手動(dòng)釋放鎖呀潭,synchronized不需要。
ReentrantLock可支持公平非公平鎖至非,synchronized只支持非公平鎖钠署。
ReentrantLock沒有語言層面的優(yōu)化,底層實(shí)現(xiàn)機(jī)制AQS和CAS荒椭,synchronized有優(yōu)化谐鼎。
ReentrantLock可重入鎖,synchronized不可重入趣惠,可能導(dǎo)致死鎖狸棍。
ReentrantLock支持讀寫鎖,可以提高高并發(fā)讀操作信卡。
synchronized由操作系統(tǒng)支持隔缀,涉及內(nèi)核態(tài)和用戶態(tài)的上下文切換,并發(fā)高時(shí)切換開銷非常大傍菇。
ReentrantLock(AQS)依賴volatile int變量標(biāo)示鎖狀態(tài)猾瘸,結(jié)構(gòu)為雙向鏈表的等待隊(duì)列,通過(cas+死循環(huán))更改鎖狀態(tài),一旦更新成功牵触,標(biāo)示競爭到鎖淮悼。
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é)點(diǎn)揽思,否則一直自旋等待袜腥,在高并發(fā)時(shí),可以避免大量鎖競爭的上下文切換钉汗,降低線程切換開銷羹令。
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);
? ? ? ? }
? ? }