
目錄
- ReentrantLock 簡介
- ReentrantLock 使用示例
- ReentrantLock 與 synchronized 的區別
- ReentrantLock 實現原理
- ReentrantLock 源碼解析

ReentrantLock 簡介
ReentrantLock 是 JDK 提供的一個可重入的獨占鎖,
- 獨占鎖:同一時間只有一個線程可以持有鎖
- 可重入:持有鎖的線程可重復加鎖,意味著第一次成功加鎖時,需要記錄鎖的持有者為當前線程,后續再次加鎖時,可以識別當前線程。
ReentrantLock 提供了公平鎖以及非公平鎖兩種模式,要解釋這兩種模式不異同,得先了解一下 ReentrantLock 的加鎖流程,ReentrantLock 基于 AQS 同步器實現加解鎖,基本的實現流程為:
線程 A、B、C 同時執行加鎖,加鎖是通過CAS操作完成,CAS 是原子操作,可以保證同一時間只有一個線程加鎖成功,假設線程 A 加鎖成功,則線程 B、C 進入 AQS 等待隊列并被掛起,假設 B 在前,C 在后,當線程 A 釋放鎖時,會喚醒排在等待隊列隊首的線程 B,該線程會嘗試通過 CAS 進行獲取鎖。如果線程 B 嘗試加鎖的同時,有線程 D 也同時進行加鎖,如果線程 D 與 線程 B 競爭加鎖,則為非公平鎖,線程 D 加入等待隊列排在線程 C 之后,則為公平鎖。
- 非公平鎖:加鎖時會與等待隊列中的頭節點進行競爭。
- 公平鎖:加鎖時首先判斷等待隊列中是否有線程在排隊,如果沒有則參與競爭鎖,如果有則排隊等待。
所謂公平就是,大家一起到,就競爭上崗,如果已經有人在排隊了,那就先來后到。
使用示例
使用偽代碼表示
class X { private final ReentrantLock lock = new ReentrantLock(); public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock(); } }}
默認實現的是非公平鎖,如果要使用公平鎖,只需要創建 ReentrantLock 對象時傳遞入參 true 即可,使用方法與非公平鎖一樣。
private final ReentrantLock lock = new ReentrantLock(true);
condition 使用示例:
class X { private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); public void poll() { lock.lock(); // block until condition holds try { while(條件判斷表達式) { condition.wait(); } } finally { lock.unlock(); } } public void push() { condition.signal(); }}
ReentrantLock 與 synchronized 的區別
ReentrantLock 提供了 synchronized 類似的功能和內存語義。
相同點:
- ReentrantLock 與 synchronized 都是獨占鎖,可以讓程序正確同步。
- ReentrantLock 與 synchronized 都可重入鎖,可以在循環中使用 synchronized 進行加鎖并不用擔心解鎖問題,但 ReentrantLock 則必須要進行與加鎖相同次數的解鎖操作,不然可能導致沒有真正解鎖成功。
不同點:
- synchronized 是JDK提供的語法,加鎖解鎖的過程是隱式的,用戶不用手動操作,操作簡單,但不夠靈活。
- ReentrantLock 需要手動加鎖解鎖,且解鎖次數必須與加鎖次數一樣,才能保證正確釋放鎖,操作較為復雜,但是因為是手動操作,所以可以應付復雜的并發場景。
- ReentrantLock 可以實現公平鎖
- ReentrantLock 可以響應中斷,使用 lockInterruptibly 方法進行加鎖,可以在加鎖過程中響應中斷,synchronized 不能響應中斷
- ReentrantLock 可以實現快速失敗,使用 tryLock 方法進行加鎖,如果不能加鎖成功,會立即返回 false,而 synchronized 是阻塞式。
- ReentrantLock 可以結合 Condition 實現條件機制。
可以看到,ReentrantLock 與 synchronized 都是實現線程同步加鎖,但 ReentrantLock 比起 synchronized 要靈活很多。

實現原理
ReentrantLock 使用組合的方式,通過繼承 AQS 同步器實現線程同步。通過控制 AQS 同步器的同步狀態 state 達到加鎖解鎖的效果,該狀態默認為 0,代表鎖未被占用,加鎖則是通過 cas 操作將其設置為 1,cas 是原子性操作,可以保證同一時間只有一個線程可以加鎖成功,同一個線程可以重復加鎖,每次加鎖同步狀態自增 1,釋放鎖的過程就是將同步狀態自減,減到 0 時才算完全釋放,這也解釋了為什么釋放鎖的次數必須與加鎖次數一樣的問題,因為只有次數一樣才能將同步狀態減至 0,這樣其它線程才能進行加鎖。
源碼分析
Lock 接口
ReentrantLock 實現了 Lock 接口,這是 JDK 提供的所有 JVM 鎖的基類。
public interface Lock { // 阻塞式加鎖 void lock(); // 阻塞式加鎖,但可以響應中斷,加鎖過程中線程中斷,拋出 InterruptedException 異常 void lockInterruptibly() throws InterruptedException; // 快速失敗加鎖,只嘗試一次, boolean tryLock(); // 阻塞式加鎖,可以響應中斷并且實現超時失敗 boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 釋放鎖 void unlock(); // 實現條件 Condition newCondition();}
通過代碼可以看到,ReentrantLock 的內部實現都是通過 Sync 這個類實現,可以認為遵守組合設計原則,Sync 是 ReentrantLock 的內部類。這里的方法調用,并沒有區分是公平鎖還是非公平鎖,而是無差別地調用,所以區別一定在 Sync 這個類的實現中。
public class ReentrantLock implements Lock, java.io.Serializable { private final Sync sync; public void lock() { sync.lock(); } public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } public boolean tryLock() { return sync.nonfairTryAcquire(1); } public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } public void unlock() { sync.release(1); } public Condition newCondition() { return sync.newCondition(); }}
Sync 類繼承了 AQS 同步器,通過同步器實現線程同步,因為是獨占鎖,所以最重要的就是實現 tryAcquire 與 tryRelease 兩個方法,Sync 類是一個 abstract 類,它擁有兩個實現類 FairSync 與 NonfairSync,通過名字應該就可分辨他們就是公平鎖與非公平鎖。
abstract static class Sync extends AbstractQueuedSynchronizer { abstract void lock(); // 非公平加鎖 final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 如果沒有線程執行鎖 if (compareAndSetState(0, acquires)) { // 通過 CAS 嘗試加鎖 setExclusiveOwnerThread(current); // 加鎖成功,設置鎖的擁有者為當前線程 return true; } } else if (current == getExclusiveOwnerThread()) { // 如果鎖已經被當前線程占有,說明是重復加鎖 int nextc = c + acquires; // 將同步狀態進行自增,acquires 的傳值為 1 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; // 將同步狀態進行自減,acquires 的傳值為 1 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { // 當同步狀態減成 0 時,代表完全釋放鎖,將鎖的擁有者置空 free = true; setExclusiveOwnerThread(null); } setState(c); return free; } // ...省略其它...}
所以公平鎖與非公平鎖的玄機就在 ReentrantLock 的構造方法中,默認的無參構造方法創建非公平鎖,如果傳參 true,則創建公平鎖。而這兩個鎖都是 Sync 的子類,使用了不同的實現策略,可以認為使用了策略模式。
public class ReentrantLock implements Lock, java.io.Serializable { // 默認創建非公平鎖 public ReentrantLock() { sync = new NonfairSync(); } // 如果為 true,則創建公平鎖 public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }}
接下來分別看一下 FairSync 與 NonfairSync 是如何實現公平鎖與非公平鎖的,首先分析非公平鎖
static final class NonfairSync extends Sync { // 阻塞式加鎖 final void lock() { // 首先嘗試競爭加鎖,如果成功則設置當前線程為鎖的擁有者 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); // 使用 AQS 排隊 } // 嘗試加鎖 protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }}
阻塞式加鎖調用 ReentrantLock 的 lock() 方法,該方法調用 sync.lock() 執行加鎖,非公平鎖也就是調用 NonfairSync 類的 lock 方法,該方法首先嘗試競爭加鎖,此時有三種情況:
- 此時鎖沒有人持有,競爭成功,直接設置當前線程為鎖的擁有者并返回
- 此時鎖沒有人持有,競爭失敗,走 AQS 加鎖流程
- 此時鎖被其它線程擁有,走 AQS 加鎖流程
- 此時鎖被自己擁有,競爭失敗,走 AQS 加鎖流程
AQS 加鎖流程就是調用 tryAcquire 方法嘗試加鎖,如果成功則返回加鎖成功,如果失敗則進入等待隊列并掛起,等待鎖的持有者釋放鎖時喚醒等待隊列中的線程,并再次嘗試加鎖,如此反復,直到加鎖成功。
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt();}
AQS 加鎖流程在 AQS 中已經提供了完整實現模板,不需要去了解底層就可以使用,需要做的就是自行實現 tryAcquire 方法,NonfairSync 的 tryAcquire 方法這里再貼一次實現代碼。
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 如果沒有線程執行鎖 if (compareAndSetState(0, acquires)) { // 通過 CAS 嘗試加鎖 setExclusiveOwnerThread(current); // 加鎖成功,設置鎖的擁有者為當前線程 return true; } } else if (current == getExclusiveOwnerThread()) { // 如果鎖已經被當前線程占有,說明是重復加鎖 int nextc = c + acquires; // 將同步狀態進行自增,acquires 的傳值為 1 if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false;}
接下來看一下 FairSync 的實現
static final class FairSync extends Sync { final void lock() { acquire(1); } protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 如果鎖沒有人持有 // 首先判斷隊列是否為空,如果為空則競爭鎖,如果不為空則返回嘗試失敗,線程會被加入等待隊列 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { // 如果鎖已經被當前線程持有,與非公平鎖同樣處理 int nextc = c + acquires; // 將同步狀態進行自增,acquires 的傳值為 1 if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }}
釋放鎖的過程,公平鎖與非公平鎖是一樣的,前面的代碼中已經解釋過了,這里就不再多說了。