前言
上一篇中已經分析了關鍵的AQS抽象隊列同步器,下面我們來看一下使用AQS來實現的可重入獨占鎖ReentrantLock。
ReentrantLock是可重入的獨占鎖,同時只有一個線程可以獲取該鎖,下面我們來看下其類圖:

ReentrantLock結構圖
Sync抽象類
Sync是ReentrantLock的靜態(tài)內部類,繼承自AbstractQueuedSynchronizer。它使用AQS的state字段來表示當前鎖的持有數量,從而實現可重入的特性。
- #lock()
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
允許子類實現快速獲得非公平鎖的邏輯。
- #nonfairTryAcquire(int acquires)
final boolean nonfairTryAcquire(int acquires) {
//1.當前線程
final Thread current = Thread.currentThread();
//2.獲取同步狀態(tài)
int c = getState();
//3.state==0表示當前鎖處于空閑狀態(tài)
if (c == 0) {
//3.1 通過CAS獲取鎖,獲取成功后設置為當前線程所有
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//4.判斷鎖持有的線程是否為當前線程,可重入
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;
}
- #tryRelease(int releases)
protected final boolean tryRelease(int releases) {
//1. 剩余可重入數量
int c = getState() - releases;
//2. 持有鎖的線程不是當前線程,直接拋出異常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//3. c == 0 表示已經釋放完全了,其他線程可以獲取同步狀態(tài)了
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;
}
/**
* 自定義序列化邏輯
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
Sync實現類
- NonfairSync,非公平鎖實現類
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
- FairSync,公平鎖實現類
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;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
- 區(qū)別
非公平鎖和公平鎖獲取同步狀態(tài)的過程唯一的區(qū)別就在于,公平鎖在獲取同步狀態(tài)時多了一個限制條件#hasQueuedPredecessors()方法,是否有前序節(jié)點,即自己不是首個等待獲取同步狀態(tài)的節(jié)點。代碼如下:
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
//頭節(jié)點 != 尾節(jié)點
//同步隊列第一個節(jié)點不為null
//當前線程是同步隊列第一個節(jié)點
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
這里主要是判斷當前線程是否位于 CLH 同步隊列中的第一個。如果是則返回 true ,否則返回 false 。
ReentrantLock
- 構造方法
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
默認是非公平鎖,但是可以通過構造方法設置。
- lock、trylock、unlock
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);
}
總結
ReentrantLock 與 synchronized 的區(qū)別
- 與synchronized相比,ReentrantLock提供了更多,更加全面的功能,具備更強的擴展性。例如:時間鎖等候,可中斷鎖等候,鎖投票。
- ReentrantLock還提供了條件Condition,對線程的等待、喚醒操作更加詳細和靈活,所以在多個條件變量和高度競爭鎖的地方,ReentrantLock 更加適合。
- ReentrantLock提供了可輪詢的鎖請求。它會嘗試著去獲取鎖,如果成功則繼續(xù),否則可以等到下次運行時處理,而synchronized則一旦進入鎖請求要么成功要么阻塞,所以相比synchronized而言,ReentrantLock會不容易產生死鎖些。
- ReentrantLock 支持更加靈活的同步代碼塊,但是使用 synchronized 時,只能在同一個 synchronized塊結構中獲取和釋放。注意,ReentrantLock 的鎖釋放一定要在 finally 中處理,否則可能會產生嚴重的后果。
- ReentrantLock 支持中斷處理,且性能較 synchronized 會好些。