重入鎖ui
(1)重進入:spa
1.定義:重進入是指任意線程在獲取到鎖以後,再次獲取該鎖而不會被該鎖所阻塞。關聯一個線程持有者+計數器,重入意味着鎖操做的顆粒度爲「線程」。線程
2.須要解決兩個問題:code
線程再次獲取鎖:鎖須要識別獲取鎖的現場是否爲當前佔據鎖的線程,若是是,則再次成功獲取;對象
鎖的最終釋放:線程重複n次獲取鎖,隨後在第n次釋放該鎖後,其餘線程可以獲取該鎖。要求對鎖對於獲取進行次數的自增,計數器對當前鎖被重複獲取的次數進行統計,當鎖被釋放的時候,計數器自減,當計數器值爲0時,表示鎖成功釋放。blog
3.重入鎖實現重入性:每一個鎖關聯一個線程持有者和計數器,當計數器爲0時表示該鎖沒有被任何線程持有,那麼任何線程均可能得到該鎖而調用相應的方法;當某一線程請求成功後,JVM會記下鎖的持有線程,而且將計數器置爲1;此時其它線程請求該鎖,則必須等待;而該持有鎖的線程若是再次請求這個鎖,就能夠再次拿到這個鎖,同時計數器會遞增;當線程退出同步代碼塊時,計數器會遞減,若是計數器爲0,則釋放該鎖遞歸
(2)ReentrantLock是的非公平類中經過組合自定義同步器來實現鎖的獲取與釋放。隊列
1 /** 2 * Sync中的nonfairTryAcquire()方法實現 3 * 這個跟公平類中的實現主要區別在於不會判斷當前線程是不是等待時間最長的線程 4 **/ 5 final boolean nonfairTryAcquire(int acquires) { 6 final Thread current = Thread.currentThread(); 7 int c = getState(); 8 if (c == 0) { 9 // 跟FairSync中的主要區別,不會判斷hasQueuedPredecessors() 10 if (compareAndSetState(0, acquires)) { 11 setExclusiveOwnerThread(current); 12 return true; 13 } 14 } 15 else if (current == getExclusiveOwnerThread()) { 16 int nextc = c + acquires; 17 if (nextc < 0) // overflow 18 throw new Error("Maximum lock count exceeded"); 19 setState(nextc); 20 return true; 21 } 22 return false; 23 }
nonfairTryAcquire()方法中,增長了再次獲取同步狀態的處理邏輯,經過判斷當前線程是否爲獲取鎖的線程來決定獲取操做是否成功,若是是獲取鎖的線程再次請求,則將同步狀態值進行增長並返回true,表示獲取同步狀態成功。資源
成功獲取鎖的現場再次獲取鎖,只是增長了同步狀態值,要求ReentrantLock在釋放同步狀態時減小同步狀態值。get
1 /** 2 * Sync中tryRelease() 3 **/ 4 protected final boolean tryRelease(int releases) { 5 // 修改當前鎖的狀態 6 // 若是一個線程遞歸獲取了該鎖(也就是state != 1), 那麼c可能不等0 7 // 若是沒有線程遞歸獲取該鎖,則c == 0 8 int c = getState() - releases; 9 10 // 若是鎖的佔有線程不等於當前正在執行釋放操做的線程,則拋出異常 11 if (Thread.currentThread() != getExclusiveOwnerThread()) 12 throw new IllegalMonitorStateException(); 13 boolean free = false; 14 // c == 0,表示當前線程釋放鎖成功,同時表示遞歸獲取了該鎖的線程已經執行完畢 15 // 則設置當前鎖狀態爲free,同時設置鎖的當前線程爲null,可讓其餘線程來獲取 16 // 同時也說明,若是c != 0,則表示線程遞歸佔用了鎖資源, 17 // 因此鎖的當前佔用線程依然是當前釋放鎖的線程(實際沒有釋放) 18 if (c == 0) { 19 free = true; 20 setExclusiveOwnerThread(null); 21 } 22 // 從新設置鎖的佔有數 23 setState(c); 24 return free; 25 }
若是該鎖被獲取n次,則前(n-1)次tryRelease(int releases)方法必須返回false,而只有同步狀態徹底釋放了,才返回true,該方法將同步狀態是否爲0做爲最終釋放的條件,當同步狀態爲0時,將佔有線程設置爲null,並返回true,表示釋放成功。
對於公平鎖而言
1 /** 2 * FairSync中tryAcquire()的實現 3 * 返回 4 * true: 獲取鎖成功 5 * false: 獲取鎖不成功 6 **/ 7 protected final boolean tryAcquire(int acquires) { 8 // 獲取當前線程 9 final Thread current = Thread.currentThread(); 10 // 獲取鎖資源的狀態 11 // 0: 說明當前鎖可當即獲取,在此種狀態下(又是公平鎖) 12 // >0而且當前線程與持有鎖資源的線程是同一個線程則state + 1並返回true 13 // >0而且佔有鎖資源的不是當前線程,則返回false表示獲取不成功 14 int c = getState(); 15 if (c == 0) { 16 // 在鎖能夠當即獲取的狀況下 17 // 首先判斷線程是不是剛剛釋放鎖資源的頭節點的下一個節點(線程的等待前後順序) 18 // 若是是等待時間最長的纔會立刻獲取到鎖資源,不然不會(這也是公平與不公平的主要區別所在) 19 if (!hasQueuedPredecessors() && 20 compareAndSetState(0, acquires)) { 21 setExclusiveOwnerThread(current); 22 return true; 23 } 24 } 25 else if (current == getExclusiveOwnerThread()) { //線程能夠遞歸獲取鎖 26 int nextc = c + acquires; 27 // 超過int上限值拋出錯誤 28 if (nextc < 0) 29 throw new Error("Maximum lock count exceeded"); 30 setState(nextc); 31 return true; 32 } 33 return false; 34 }
與非公平惟一的區別是判斷條件中多了hasQueuedPredecessors()方法,即加入了同步隊列中當前節點是否有前驅節點的判斷,若是該方法返回了true,則表示有線程比當前線程更早地請求獲取鎖,因此須要等待前驅線程獲取並釋放鎖後才能繼續獲取該鎖。
可是非公平鎖是默認實現:非公平性鎖可能使線程「飢餓」,可是極少的線程切換,能夠保證其更大的吞吐量。而公平性鎖,保證了鎖的獲取按照FIFO原則,代價是進行大量的線程切換。
(3)synchronized可重入性
同一線程在調用本身類中其餘synchronized方法/塊或調用父類的synchronized方法/塊都不會阻礙該線程的執行,就是說同一線程對同一個對象鎖是可重入的,並且同一個線程能夠獲取同一把鎖屢次,也就是能夠屢次重入。