Java并发编程-ReentrantLock源码分析
一、前言
在分析了?AbstractQueuedSynchronier?源碼后,接著分析ReentrantLock源碼,其實(shí)在?AbstractQueuedSynchronizer?的分析中,已經(jīng)提到過(guò)ReentrantLock,ReentrantLock表示下面具體分析ReentrantLock源碼。
二、ReentrantLock數(shù)據(jù)結(jié)構(gòu)
ReentrantLock的底層是借助AbstractQueuedSynchronizer實(shí)現(xiàn),所以其數(shù)據(jù)結(jié)構(gòu)依附于AbstractQueuedSynchronizer的數(shù)據(jù)結(jié)構(gòu),關(guān)于AQS的數(shù)據(jù)結(jié)構(gòu),在前一篇已經(jīng)介紹過(guò),不再累贅。
三、ReentrantLock源碼分析
3.1 類(lèi)的繼承關(guān)系
public class ReentrantLock implements Lock, java.io.Serializable 說(shuō)明:ReentrantLock實(shí)現(xiàn)了Lock接口,Lock接口中定義了lock與unlock相關(guān)操作,并且還存在newCondition方法,表示生成一個(gè)條件。3.2 類(lèi)的內(nèi)部類(lèi)
ReentrantLock總共有三個(gè)內(nèi)部類(lèi),并且三個(gè)內(nèi)部類(lèi)時(shí)緊密相關(guān)的,下面先看三個(gè)類(lèi)的關(guān)系。
說(shuō)明:ReentrantLock類(lèi)內(nèi)部總共存在Sync、NonfairSync、FairSync三個(gè)類(lèi),NonfairSync與FairSync類(lèi)繼承自Sync類(lèi),Sync類(lèi)繼承自AbstractQueuedSynchronizer抽象類(lèi)。下面逐個(gè)進(jìn)行分析。
1. Sync類(lèi)
Sync類(lèi)的源碼如下
abstract static class Sync extends AbstractQueuedSynchronizer {// 序列號(hào)private static final long serialVersionUID = -5179523762034025860L;// 獲取鎖abstract void lock();// 非公平方式獲取final boolean nonfairTryAcquire(int acquires) {// 當(dāng)前線程final Thread current = Thread.currentThread();// 獲取狀態(tài)int c = getState();if (c == 0) { // 表示沒(méi)有線程正在競(jìng)爭(zhēng)該鎖if (compareAndSetState(0, acquires)) { // 比較并設(shè)置狀態(tài)成功,狀態(tài)0表示鎖沒(méi)有被占用// 設(shè)置當(dāng)前線程獨(dú)占 setExclusiveOwnerThread(current); return true; // 成功 }}else if (current == getExclusiveOwnerThread()) { // 當(dāng)前線程擁有該鎖int nextc = c + acquires; // 增加重入次數(shù)if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");// 設(shè)置狀態(tài) setState(nextc); // 成功return true; }// 失敗return false;}// 試圖在共享模式下獲取對(duì)象狀態(tài),此方法應(yīng)該查詢是否允許它在共享模式下獲取對(duì)象狀態(tài),如果允許,則獲取它protected final boolean tryRelease(int releases) {int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread()) // 當(dāng)前線程不為獨(dú)占線程throw new IllegalMonitorStateException(); // 拋出異常// 釋放標(biāo)識(shí)boolean free = false; if (c == 0) {free = true;// 已經(jīng)釋放,清空獨(dú)占setExclusiveOwnerThread(null); }// 設(shè)置標(biāo)識(shí) setState(c); return free; }// 判斷資源是否被當(dāng)前線程占有protected final boolean isHeldExclusively() {// While we must in general read state before owner,// we don't need to do so to check if current thread is ownerreturn getExclusiveOwnerThread() == Thread.currentThread();}// 新生一個(gè)條件final ConditionObject newCondition() {return new ConditionObject();}// Methods relayed from outer class// 返回資源的占用線程final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread();}// 返回狀態(tài)final int getHoldCount() { return isHeldExclusively() ? getState() : 0;}// 資源是否被占用final boolean isLocked() { return getState() != 0;}/*** Reconstitutes the instance from a stream (that is, deserializes it).*/// 自定義反序列化邏輯private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();setState(0); // reset to unlocked state }}說(shuō)明:Sync類(lèi)存在如下方法和作用如下。
2. NonfairSync類(lèi)
NonfairSync類(lèi)繼承了Sync類(lèi),表示采用非公平策略獲取鎖,其實(shí)現(xiàn)了Sync類(lèi)中抽象的lock方法,源碼如下。
// 非公平鎖static final class NonfairSync extends Sync {// 版本號(hào)private static final long serialVersionUID = 7316153563782823691L;// 獲得鎖final void lock() {if (compareAndSetState(0, 1)) // 比較并設(shè)置狀態(tài)成功,狀態(tài)0表示鎖沒(méi)有被占用// 把當(dāng)前線程設(shè)置獨(dú)占了鎖 setExclusiveOwnerThread(Thread.currentThread());else // 鎖已經(jīng)被占用,或者set失敗// 以獨(dú)占模式獲取對(duì)象,忽略中斷acquire(1); }protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}}說(shuō)明:從lock方法的源碼可知,每一次都嘗試獲取鎖,而并不會(huì)按照公平等待的原則進(jìn)行等待,讓等待時(shí)間最久的線程獲得鎖。
3. FairSyn類(lèi)
FairSync類(lèi)也繼承了Sync類(lèi),表示采用公平策略獲取鎖,其實(shí)現(xiàn)了Sync類(lèi)中的抽象lock方法,源碼如下。
// 公平鎖static final class FairSync extends Sync {// 版本序列化private static final long serialVersionUID = -3000897897090466540L;final void lock() {// 以獨(dú)占模式獲取對(duì)象,忽略中斷acquire(1);}/*** Fair version of tryAcquire. Don't grant access unless* recursive call or no waiters or is first.*/// 嘗試公平獲取鎖protected final boolean tryAcquire(int acquires) {// 獲取當(dāng)前線程final Thread current = Thread.currentThread();// 獲取狀態(tài)int c = getState();if (c == 0) { // 狀態(tài)為0if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) { // 不存在已經(jīng)等待更久的線程并且比較并且設(shè)置狀態(tài)成功// 設(shè)置當(dāng)前線程獨(dú)占 setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) { // 狀態(tài)不為0,即資源已經(jīng)被線程占據(jù)// 下一個(gè)狀態(tài)int nextc = c + acquires;if (nextc < 0) // 超過(guò)了int的表示范圍throw new Error("Maximum lock count exceeded");// 設(shè)置狀態(tài) setState(nextc);return true;}return false;}}說(shuō)明:跟蹤lock方法的源碼可知,當(dāng)資源空閑時(shí),它總是會(huì)先判斷sync隊(duì)列(AbstractQueuedSynchronizer中的數(shù)據(jù)結(jié)構(gòu))是否有等待時(shí)間更長(zhǎng)的線程,如果存在,則將該線程加入到等待隊(duì)列的尾部,實(shí)現(xiàn)了公平獲取原則。其中,FairSync類(lèi)的lock的方法調(diào)用如下,只給出了主要的方法。
說(shuō)明:可以看出只要資源被其他線程占用,該線程就會(huì)添加到sync queue中的尾部,而不會(huì)先嘗試獲取資源。這也是和Nonfair最大的區(qū)別,Nonfair每一次都會(huì)嘗試去獲取資源,如果此時(shí)該資源恰好被釋放,則會(huì)被當(dāng)前線程獲取,這就造成了不公平的現(xiàn)象,當(dāng)獲取不成功,再加入隊(duì)列尾部。
3.3 類(lèi)的屬性
public class ReentrantLock implements Lock, java.io.Serializable {// 序列號(hào)private static final long serialVersionUID = 7373984872572414699L; // 同步隊(duì)列private final Sync sync; }說(shuō)明:ReentrantLock類(lèi)的sync非常重要,對(duì)ReentrantLock類(lèi)的操作大部分都直接轉(zhuǎn)化為對(duì)Sync和AbstractQueuedSynchronizer類(lèi)的操作。
3.4 類(lèi)的構(gòu)造函數(shù)
1. ReentrantLock()型構(gòu)造函數(shù)
public ReentrantLock() {// 默認(rèn)非公平策略sync = new NonfairSync();}說(shuō)明:可以看到默認(rèn)是采用的非公平策略獲取鎖。
2. ReentrantLock(boolean)型構(gòu)造函數(shù)
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}說(shuō)明:可以傳遞參數(shù)確定采用公平策略或者是非公平策略,參數(shù)為true表示公平策略,否則,采用非公平策略。
3.5 類(lèi)的核心函數(shù)分析
通過(guò)分析ReentrantLock的源碼,可知對(duì)其操作都轉(zhuǎn)化為對(duì)Sync對(duì)象的操作,由于Sync繼承了AQS,所以基本上都可以轉(zhuǎn)化為對(duì)AQS的操作。如將ReentrantLock的lock函數(shù)轉(zhuǎn)化為對(duì)Sync的lock函數(shù)的調(diào)用,而具體會(huì)根據(jù)采用的策略(如公平策略或者非公平策略)的不同而調(diào)用到Sync的不同子類(lèi)。
所以可知,在ReentrantLock的背后,是AQS對(duì)其服務(wù)提供了支持,由于之前我們分析AQS的核心源碼,遂不再累贅。下面還是通過(guò)例子來(lái)更進(jìn)一步分析源碼。
四、示例分析
4.1 公平鎖
package com.hust.grid.leesf.abstractqueuedsynchronizer;import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;class MyThread extends Thread {private Lock lock;public MyThread(String name, Lock lock) {super(name);this.lock = lock;}public void run () {lock.lock();try {System.out.println(Thread.currentThread() + " running");try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}} finally {lock.unlock();}} }public class AbstractQueuedSynchonizerDemo {public static void main(String[] args) throws InterruptedException {Lock lock = new ReentrantLock(true);MyThread t1 = new MyThread("t1", lock); MyThread t2 = new MyThread("t2", lock);MyThread t3 = new MyThread("t3", lock);t1.start();t2.start(); t3.start();} } 運(yùn)行結(jié)果(某一次): Thread[t1,5,main] running Thread[t2,5,main] running Thread[t3,5,main] running說(shuō)明:該示例使用的是公平策略,由結(jié)果可知,可能會(huì)存在如下一種時(shí)序。
說(shuō)明:首先,t1線程的lock操作 -> t2線程的lock操作 -> t3線程的lock操作 -> t1線程的unlock操作 -> t2線程的unlock操作 -> t3線程的unlock操作。根據(jù)這個(gè)時(shí)序圖來(lái)進(jìn)一步分析源碼的工作流程。
① t1線程執(zhí)行l(wèi)ock.lock,下圖給出了方法調(diào)用中的主要方法。
說(shuō)明:由調(diào)用流程可知,t1線程成功獲取了資源,可以繼續(xù)執(zhí)行。
② t2線程執(zhí)行l(wèi)ock.lock,下圖給出了方法調(diào)用中的主要方法。
說(shuō)明:由上圖可知,最后的結(jié)果是t2線程會(huì)被禁止,因?yàn)檎{(diào)用了LockSupport.park。
③ t3線程執(zhí)行l(wèi)ock.lock,下圖給出了方法調(diào)用中的主要方法。
說(shuō)明:由上圖可知,最后的結(jié)果是t3線程會(huì)被禁止,因?yàn)檎{(diào)用了LockSupport.park。
④ t1線程調(diào)用了lock.unlock,下圖給出了方法調(diào)用中的主要方法。
說(shuō)明:如上圖所示,最后,head的狀態(tài)會(huì)變?yōu)?,t2線程會(huì)被unpark,即t2線程可以繼續(xù)運(yùn)行。此時(shí)t3線程還是被禁止。
⑤ t2獲得cpu資源,繼續(xù)運(yùn)行,由于t2之前被park了,現(xiàn)在需要恢復(fù)之前的狀態(tài),下圖給出了方法調(diào)用中的主要方法。
說(shuō)明:在setHead函數(shù)中會(huì)將head設(shè)置為之前head的下一個(gè)結(jié)點(diǎn),并且將pre域與thread域都設(shè)置為null,在acquireQueued返回之前,sync queue就只有兩個(gè)結(jié)點(diǎn)了。
⑥ t2執(zhí)行l(wèi)ock.unlock,下圖給出了方法調(diào)用中的主要方法。
說(shuō)明:由上圖可知,最終unpark t3線程,讓t3線程可以繼續(xù)運(yùn)行。
⑦ t3線程獲取cpu資源,恢復(fù)之前的狀態(tài),繼續(xù)運(yùn)行。
說(shuō)明:最終達(dá)到的狀態(tài)是sync queue中只剩下了一個(gè)結(jié)點(diǎn),并且該節(jié)點(diǎn)除了狀態(tài)為0外,其余均為null。
⑧ t3執(zhí)行l(wèi)ock.unlock,下圖給出了方法調(diào)用中的主要方法。
說(shuō)明:最后的狀態(tài)和之前的狀態(tài)是一樣的,隊(duì)列中的一個(gè)結(jié)點(diǎn)會(huì)被GC,最后隊(duì)列會(huì)為空。
使用公平策略和Condition的情況可以參考上一篇關(guān)于AQS的源碼示例分析部分,不再累贅。
五、總結(jié)
再掌握了AQS后,再來(lái)分析ReentrantLock的源碼,就會(huì)非常簡(jiǎn)單,因?yàn)镽eentrantLock的絕大部分操作都是基于AQS類(lèi)的。所以,進(jìn)行分析時(shí)要找準(zhǔn)方向,就會(huì)事半功倍。謝謝各位園友觀看~
總結(jié)
以上是生活随笔為你收集整理的Java并发编程-ReentrantLock源码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【AMESim】AMESim和Simul
- 下一篇: [Java基础][Java]toStri