[轉]JVM鎖機制volatile/synchronized/lock
(1)聊聊並發(一)——深入分析Volatile的實現原理
--硬件級別鎖實現,Lock前綴指令會引起處理器緩存(CPU高級緩存L1/L2/L3)回寫到內存。一個處理器的緩存回寫到內存會導致其他處理器的緩存無效。
2.JVM鎖機制--synchronized
--自旋鎖、偏向鎖
--synchronized的底層實現主要依靠Lock-Free的隊列,基本思路是自旋后阻塞,競爭切換后繼續競爭鎖,稍微犧牲了公平性,但獲得了高吞吐量。
(2)聊聊並發(二)——Java SE1.6中的Synchronized
--偏向鎖—>輕量鎖—>重量鎖 比較
偏向鎖
Hotspot的作者經過以往的研究發現大多數情況下鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價更低而引入了偏向鎖。當一個線程訪問同步塊並獲取鎖時,會在對象頭和棧幀中的鎖記錄里存儲鎖偏向的線程ID,以后該線程在進入和退出同步塊時不需要花費CAS操作來加鎖和解鎖,而只需簡單的測試一下對象頭的Mark Word里是否存儲着指向當前線程的偏向鎖,如果測試成功,表示線程已經獲得了鎖,如果測試失敗,則需要再測試下Mark Word中偏向鎖的標識是否設置成1(表示當前是偏向鎖),如果沒有設置,則使用CAS競爭鎖,如果設置了,則嘗試使用CAS將對象頭的偏向鎖指向當前線程。
偏向鎖的撤銷:偏向鎖使用了一種等到競爭出現才釋放鎖的機制,所以當其他線程嘗試競爭偏向鎖時,持有偏向鎖的線程才會釋放鎖。偏向鎖的撤銷,需要等待全局安全點(在這個時間點上沒有字節碼正在執行),它會首先暫停擁有偏向鎖的線程,然后檢查持有偏向鎖的線程是否活着,如果線程不處於活動狀態,則將對象頭設置成無鎖狀態,如果線程仍然活着,擁有偏向鎖的棧會被執行,遍歷偏向對象的鎖記錄,棧中的鎖記錄和對象頭的Mark Word要么重新偏向於其他線程,要么恢復到無鎖或者標記對象不適合作為偏向鎖,最后喚醒暫停的線程。下圖中的線程1演示了偏向鎖初始化的流程,線程2演示了偏向鎖撤銷的流程。
輕量鎖
即自旋鎖
重量鎖
即阻塞鎖
鎖的優缺點對比
偏向鎖 |
加鎖和解鎖不需要額外的消耗,和執行非同步方法比僅存在納秒級的差距。 |
如果線程間存在鎖競爭,會帶來額外的鎖撤銷的消耗。 |
適用於只有一個線程訪問同步塊場景。 |
輕量級鎖 |
競爭的線程不會阻塞,提高了程序的響應速度。 |
如果始終得不到鎖競爭的線程使用自旋會消耗CPU。 |
追求響應時間。 同步塊執行速度非常快。 |
重量級鎖 |
線程競爭不使用自旋,不會消耗CPU。 |
線程阻塞,響應時間緩慢。 |
追求吞吐量。 同步塊執行速度較長。 |
(3)JVM鎖實現探究1:synchronized初探
(4)JVM鎖實現探究2:synchronized深探
3.JVM鎖機制AQS(AbstractQueuedSynchronizer)
--源碼分析Java.util.concurrent.AbstractQueuedSynchronizer類
Lock VS Synchronized
AbstractQueuedSynchronizer通過構造一個基於阻塞的CLH隊列容納所有的阻塞線程,而對該隊列的操作均通過Lock-Free(CAS)操作,但對已經獲得鎖的線程而言,ReentrantLock實現了偏向鎖的功能。
synchronized的底層也是一個基於CAS操作的等待隊列,但JVM實現的更精細,把等待隊列分為ContentionList和EntryList,目的是為了降低線程的出列速度;當然也實現了偏向鎖,從數據結構來說二者設計沒有本質區別。但synchronized還實現了自旋鎖,並針對不同的系統和硬件體系進行了優化,而Lock則完全依靠系統阻塞掛起等待線程。
當然Lock比synchronized更適合在應用層擴展,可以繼承AbstractQueuedSynchronizer定義各種實現,比如實現讀寫鎖(ReadWriteLock),公平或不公平鎖;同時,Lock對應的Condition也比wait/notify要方便的多、靈活的多。
4.ReentrantLock
(1) JAVA並發編程學習筆記之ReentrantLock --ReentrantLock是一個可重入的互斥鎖,ReentrantLock由最近成功獲取鎖,還沒有釋放的線程所擁有
(2)ReentrantLock與synchronized的區別
--ReentrantLock的lock機制有2種,忽略中斷鎖和響應中斷鎖
--synchronized實現的鎖機制是可重入的,主要區別是中斷控制和競爭鎖公平策略
原文地址:http://blog.csdn.net/asdf717/article/details/47252763