synchronizedphp
Reentrantlockhtml
ReentrantReadWriteLockjava
爲何須要鎖?緩存
爲何JAVA有了synchronize還須要Reentrantlock和ReentrantReadWriteLock?優化
synchronize和lock分別怎麼實現同步快(原子性,一致性,禁重排序)?ui
synchronize和lock分別怎麼實現鎖的優化,可重入鎖,偏向鎖?atom
lock如何實現公平鎖(synchronize是非公平鎖)?spa
目的:.net
鎖的目的是防止資源的競爭,主要從 原子性(一致性),可見性,防止處理重排序 三個方面來處理, volatile知足了後面兩個特性,JAVA從兩方面來實現鎖線程
synchronized與ReentrantLock ,使用上看區別
1, synchronize在獲取鎖阻塞的時候是不能打斷的
2, synchronize無超時機制,阻塞了的話只能一直阻塞形成死鎖
3,synchronize只能notify,wait,若是須要兩個或以上條件就不能用了,如: JAVA阻塞隊列的實現,須要用是否爲空和是否已滿兩個條件來阻塞線程
看lock相關的API就知道, 主要就是解決這幾個問題
方法名稱 | 描述 |
lock | 獲取鎖,若是鎖沒法獲取,那麼當前的線程就變爲不可被調度,直到鎖被獲取到 |
lockInterruptibly | 獲取鎖,除非當前線程被中斷。若是獲取到了鎖,那麼當即返回,若是獲取不到,那麼當前線程變得不可被調度,一直休眠直到下面兩件事情發生:一、當前線程獲取到了鎖
二、其餘的線程中斷了當前的線程 |
tryLock | 若是調用的時候可以獲取鎖,那麼就獲取鎖而且返回true,若是當前的鎖沒法獲取到,那麼這個方法會馬上返回false |
tryLcok(long time,TimeUnit unit) | 在指定時間內嘗試獲取鎖若是能夠獲取鎖,那麼獲取鎖而且返回true,若是當前的鎖沒法獲取,那麼當前的線程變得不可被調度,直到下面三件事之一發生:一、當前線程獲取到了鎖
二、當前線程被其餘線程中斷 三、指定的等待時間到了 |
unlock | 釋放當前線程佔用的鎖 |
newCondition | 返回一個與當前的鎖關聯的條件變量。在使用這個條件變量以前,當前線程必須佔用鎖。調用Condition的await方法,會在等待以前原子地釋放鎖,並在等待被喚醒後原子的獲取鎖 |
讀寫鎖用於讀多寫少的狀況,即當一條線程獲取寫鎖後,後面的讀鎖都被阻塞,等待獲取寫鎖的線程完成釋放。
場景,如本地緩存失效,當須要去DB拿數據進行寫入的操做,須要阻塞其它讀的操做.
固然,讀寫鎖也是能夠基於notifyAll和wait實現
須要注意的是
synchronized鎖
實現依賴
原子性,可見性和重排序都是依靠指令。方法同步和代碼塊同步依靠Monitor指令,代碼塊同步是使用monitorenter和monitorexit指令實現
鎖信息保存在JAVA對象頭裏,準確說是Mark Word
synchronize的阻塞,依靠幾個隊列,屬於不公平鎖(線程先CAS競爭鎖,再進隊列)
ContentionList(LIFO)-->EntryList(LIFO)-->OnDeck-->Owner-->Wait Set (http://www.cnblogs.com/lykm02/p/4516777.html )
鎖的轉換方面
無鎖-->偏向鎖-->輕量鎖-->重量鎖 (http://blog.csdn.net/xad707348125/article/details/47189107)
偏向鎖和可重入鎖的實現
可重入鎖,即當本線程進入同一鎖時能夠進行屢次上鎖,固然也須要屢次釋放
偏向鎖,即當獲取線程再次進入同步塊時不須要再次競爭(CAS),當某個Core CAS成功時必然會引發總線風暴,這就是所謂的本地延遲,本質上偏向鎖就是爲了消除CAS,下降Cache一致性流量
原理:
1, 鎖保存當前鎖的線程,判斷同一個線程時容許
用一個計數器去記錄當前重入的次數,當進入時計數器+1, 當釋放鎖時計算器-1, 當爲0時表示可競爭
synchronized
實現方式, 會在 Mark Word 存儲獲取鎖線程的ID,而後棧幀中也存儲線程ID,之後該線程再次進入同步塊(同步方法)時不須要花費CAS了。
lock
實現方式, 用JAVA代碼實現處理,跟蹤下 lock()(NonfairSyncCAS獲取鎖失敗)->acquire(AQS嘗試獲取鎖)-->tryAcquire(nonfairTryAcquire)-->nonfairTryAcquire(Sync 以下處理):
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { //再次判斷是不是未鎖狀態,state爲0爲未有線程獲取鎖 if (compareAndSetState(0, acquires)) { //CAS再次競爭獲取鎖,此處是公平鎖與非公平鎖的區別 setExclusiveOwnerThread(current); return true; } } //這裏是是實現偏向鎖的關鍵,比較若是是當前鎖就不進入CLH隊列後面的競爭了 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
synchronize自己是非公平鎖,無公平性實現.
lock非公平鎖代碼(詳細如上)
if (compareAndSetState(0, acquires)) { //如鎖被釋放,是非公平鎖的話,用CAS再次競爭獲取鎖
公平鎖此段代碼以下:
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {//如鎖被釋放,必須當隊列爲空時纔去CAS競爭鎖 setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
多個線程一塊兒用CAS去嘗試獲取一個共同的可見性(volatile)的變量,獲取成功即爲獲取鎖
如 全部線程都運行
/** * 自旋鎖方式去實現阻塞 * * 缺點:沒法實現公平性,若是大量使用會增長CPU的Cache一致性流量開銷 */ public static void CASLock() { // 不斷去獲取CAS的鎖,如成功表示獲取鎖成功 while (state.compareAndSet(0, 1)) { } } public static void CASUnlock() { if (!state.compareAndSet(1, 0)) { // 釋放鎖異常 throw new RuntimeException(); } }
import java.util.concurrent.atomic.AtomicInteger; public class TicketLock { private AtomicInteger serviceNum = new AtomicInteger(); // 服務號 private AtomicInteger ticketNum = new AtomicInteger(); // 排隊號 public int lock() { // 首先原子性地得到一個排隊號 int myTicketNum = ticketNum.getAndIncrement(); // 只要當前服務號不是本身的就不斷輪詢 while (serviceNum.get() != myTicketNum) { } return myTicketNum; } public void unlock(int myTicket) { // 只有當前線程擁有者才能釋放鎖 int next = myTicket + 1; serviceNum.compareAndSet(myTicket, next); } }
CLH是在前驅節點的屬性上自旋,
組成一個隊列後,每一個節點都有保存當前節點獲取鎖的狀態,和前一個節點的指向,獲取鎖的步驟
1, 新加個節點,並把節點經過自旋指向tail節點
2, 成功後,不停判斷指向節點的鎖狀態,當前節點鎖釋放時獲取鎖
3, 釋放鎖,改變自身的鎖持有狀態就行
java 就是運用的CLH鎖,但有所改進,主要包括
1, 指向了head
2, 在等待機制上由原來的自旋改爲阻塞喚醒
詳細介紹: http://blog.csdn.net/chenssy/article/details/50432195
而MCS是在本地屬性變量上自旋。
資料參考: http://coderbee.net/index.php/concurrent/20131115/577/comment-page-1
歡迎關注個人公衆號, 一塊兒來構建咱們的知識體系