一、前言
在分析了AbstractQueuedSynchronier源碼后,接着分析ReentrantLock源碼,其實在AbstractQueuedSynchronizer的分析中,已經提到過ReentrantLock,ReentrantLock表示下面具體分析ReentrantLock源碼。
二、ReentrantLock數據結構
ReentrantLock的底層是借助AbstractQueuedSynchronizer實現,所以其數據結構依附於AbstractQueuedSynchronizer的數據結構,關於AQS的數據結構,在前一篇已經介紹過,不再累贅。
三、ReentrantLock源碼分析
3.1 類的繼承關系
public class ReentrantLock implements Lock, java.io.Serializable
說明:ReentrantLock實現了Lock接口,Lock接口中定義了lock與unlock相關操作,並且還存在newCondition方法,表示生成一個條件。
3.2 類的內部類
ReentrantLock總共有三個內部類,並且三個內部類是緊密相關的,下面先看三個類的關系。
說明:ReentrantLock類內部總共存在Sync、NonfairSync、FairSync三個類,NonfairSync與FairSync類繼承自Sync類,Sync類繼承自AbstractQueuedSynchronizer抽象類。下面逐個進行分析。
1. Sync類
Sync類的源碼如下

abstract static class Sync extends AbstractQueuedSynchronizer { // 序列號 private static final long serialVersionUID = -5179523762034025860L; // 獲取鎖 abstract void lock(); // 非公平方式獲取 final boolean nonfairTryAcquire(int acquires) { // 當前線程 final Thread current = Thread.currentThread(); // 獲取狀態 int c = getState(); if (c == 0) { // 表示沒有線程正在競爭該鎖 if (compareAndSetState(0, acquires)) { // 比較並設置狀態成功,狀態0表示鎖沒有被占用 // 設置當前線程獨占 setExclusiveOwnerThread(current); return true; // 成功 } } 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; } // 試圖在共享模式下獲取對象狀態,此方法應該查詢是否允許它在共享模式下獲取對象狀態,如果允許,則獲取它 protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) // 當前線程不為獨占線程 throw new IllegalMonitorStateException(); // 拋出異常 // 釋放標識 boolean free = false; if (c == 0) { free = true; // 已經釋放,清空獨占 setExclusiveOwnerThread(null); } // 設置標識 setState(c); return free; } // 判斷資源是否被當前線程占有 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 owner return getExclusiveOwnerThread() == Thread.currentThread(); } // 新生一個條件 final ConditionObject newCondition() { return new ConditionObject(); } // Methods relayed from outer class // 返回資源的占用線程 final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread(); } // 返回狀態 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 } }
說明:Sync類存在如下方法和作用如下。
2. NonfairSync類
NonfairSync類繼承了Sync類,表示采用非公平策略獲取鎖,其實現了Sync類中抽象的lock方法,源碼如下。

// 非公平鎖 static final class NonfairSync extends Sync { // 版本號 private static final long serialVersionUID = 7316153563782823691L; // 獲得鎖 final void lock() { if (compareAndSetState(0, 1)) // 比較並設置狀態成功,狀態0表示鎖沒有被占用 // 把當前線程設置獨占了鎖 setExclusiveOwnerThread(Thread.currentThread()); else // 鎖已經被占用,或者set失敗 // 以獨占模式獲取對象,忽略中斷 acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
說明:從lock方法的源碼可知,每一次都嘗試獲取鎖,而並不會按照公平等待的原則進行等待,讓等待時間最久的線程獲得鎖。
3. FairSyn類
FairSync類也繼承了Sync類,表示采用公平策略獲取鎖,其實現了Sync類中的抽象lock方法,源碼如下。

// 公平鎖 static final class FairSync extends Sync { // 版本序列化 private static final long serialVersionUID = -3000897897090466540L; final void lock() { // 以獨占模式獲取對象,忽略中斷 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) { // 獲取當前線程 final Thread current = Thread.currentThread(); // 獲取狀態 int c = getState(); if (c == 0) { // 狀態為0 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { // 不存在已經等待更久的線程並且比較並且設置狀態成功 // 設置當前線程獨占 setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { // 狀態不為0,即資源已經被線程占據 // 下一個狀態 int nextc = c + acquires; if (nextc < 0) // 超過了int的表示范圍 throw new Error("Maximum lock count exceeded"); // 設置狀態 setState(nextc); return true; } return false; } }
說明:跟蹤lock方法的源碼可知,當資源空閑時,它總是會先判斷sync隊列(AbstractQueuedSynchronizer中的數據結構)是否有等待時間更長的線程,如果存在,則將該線程加入到等待隊列的尾部,實現了公平獲取原則。其中,FairSync類的lock的方法調用如下,只給出了主要的方法。
說明:可以看出只要資源被其他線程占用,該線程就會添加到sync queue中的尾部,而不會先嘗試獲取資源。這也是和Nonfair最大的區別,Nonfair每一次都會嘗試去獲取資源,如果此時該資源恰好被釋放,則會被當前線程獲取,這就造成了不公平的現象,當獲取不成功,再加入隊列尾部。
3.3 類的屬性
public class ReentrantLock implements Lock, java.io.Serializable { // 序列號 private static final long serialVersionUID = 7373984872572414699L; // 同步隊列 private final Sync sync; }
說明:ReentrantLock類的sync非常重要,對ReentrantLock類的操作大部分都直接轉化為對Sync和AbstractQueuedSynchronizer類的操作。
3.4 類的構造函數
1. ReentrantLock()型構造函數

public ReentrantLock() { // 默認非公平策略 sync = new NonfairSync(); }
說明:可以看到默認是采用的非公平策略獲取鎖。
2. ReentrantLock(boolean)型構造函數

public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
說明:可以傳遞參數確定采用公平策略或者是非公平策略,參數為true表示公平策略,否則,采用非公平策略。
3.5 核心函數分析
通過分析ReentrantLock的源碼,可知對其操作都轉化為對Sync對象的操作,由於Sync繼承了AQS,所以基本上都可以轉化為對AQS的操作。如將ReentrantLock的lock函數轉化為對Sync的lock函數的調用,而具體會根據采用的策略(如公平策略或者非公平策略)的不同而調用到Sync的不同子類。
所以可知,在ReentrantLock的背后,是AQS對其服務提供了支持,由於之前我們分析AQS的核心源碼,遂不再累贅。下面還是通過例子來更進一步分析源碼。
四、示例分析
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(); } }
運行結果(某一次):
Thread[t1,5,main] running
Thread[t2,5,main] running
Thread[t3,5,main] running
說明:該示例使用的是公平策略,由結果可知,可能會存在如下一種時序。
說明:首先,t1線程的lock操作 -> t2線程的lock操作 -> t3線程的lock操作 -> t1線程的unlock操作 -> t2線程的unlock操作 -> t3線程的unlock操作。根據這個時序圖來進一步分析源碼的工作流程。
① t1線程執行lock.lock,下圖給出了方法調用中的主要方法。
說明:由調用流程可知,t1線程成功獲取了資源,可以繼續執行。
② t2線程執行lock.lock,下圖給出了方法調用中的主要方法。
說明:由上圖可知,最后的結果是t2線程會被禁止,因為調用了LockSupport.park。
③ t3線程執行lock.lock,下圖給出了方法調用中的主要方法。
說明:由上圖可知,最后的結果是t3線程會被禁止,因為調用了LockSupport.park。
④ t1線程調用了lock.unlock,下圖給出了方法調用中的主要方法。
說明:如上圖所示,最后,head的狀態會變為0,t2線程會被unpark,即t2線程可以繼續運行。此時t3線程還是被禁止。
⑤ t2獲得cpu資源,繼續運行,由於t2之前被park了,現在需要恢復之前的狀態,下圖給出了方法調用中的主要方法。
說明:在setHead函數中會將head設置為之前head的下一個結點,並且將pre域與thread域都設置為null,在acquireQueued返回之前,sync queue就只有兩個結點了。
⑥ t2執行lock.unlock,下圖給出了方法調用中的主要方法。
說明:由上圖可知,最終unpark t3線程,讓t3線程可以繼續運行。
⑦ t3線程獲取cpu資源,恢復之前的狀態,繼續運行。
說明:最終達到的狀態是sync queue中只剩下了一個結點,並且該節點除了狀態為0外,其余均為null。
⑧ t3執行lock.unlock,下圖給出了方法調用中的主要方法。
說明:最后的狀態和之前的狀態是一樣的,隊列中有一個空節點,頭結點為尾節點均指向它。
使用公平策略和Condition的情況可以參考上一篇關於AQS的源碼示例分析部分,不再累贅。
五、總結
再掌握了AQS后,再來分析ReentrantLock的源碼,就會非常簡單,因為ReentrantLock的絕大部分操作都是基於AQS類的。所以,進行分析時要找准方向,就會事半功倍。謝謝各位園友觀看~