ReentrantLock (重入鎖) 源碼淺析
ReentrantLock重入鎖,顧名思義,就是支持重入的鎖,它表示能夠支持一個線程對資源的重復加鎖;我們之前學習過Synchronized鎖,它也是支持重入的一種鎖,參考我的另一篇Synchronized 鎖的實現原理與應用,Synchronized支持隱式的重入鎖,比如遞歸方法,在方法運行時,執行線程在獲取到了鎖之後仍能連續多次地獲取鎖;ReentrantLock雖然不能隱式重入,但是獲取到鎖的線程多次調用lock方法,不會阻塞進入同步隊列;除此之外在獲取鎖時支持公平或者非公平的選擇。
二、主要成員和結構圖
①、ReentrantLock關系圖
②、Sync是ReentrantLock的內部類,繼承AQS
③、FairSync公平的鎖實現,也是ReentrantLock的內部類,繼承Sync
④、NonfairSync非公平的鎖實現,也是ReentrantLock的內部類,繼承Sync
三、主要的方法
分析一些常用方法,不會介紹AQS,AQS的一些方法參考我的這一篇文章
①、構造方法,我們可以看出默認的無參是非公平鎖,有參構造true表示公平,false表示非公平。
// 無參 public ReentrantLock() { sync = new NonfairSync(); } // 有參 public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
②、lock()獲取鎖,其實就是把state從0變成n(重入鎖可以累加)。
實際調用的是sync的lock方法,分公平和非公平。
public void lock() {
sync.lock();
}
公平實現:FairSync,我們發現其實調用的是acquire,其實這個是AQS的acquire,然後aqs的acquire的方法裏面又會調用tryAcquire方法,因為這個方法需要同步組件自己去實現,所以ReentrantLock裏面重寫了AQS的tryAcquire方法,所以我們獲取到鎖就會返回true,沒有就會返回false;然後沒有獲取到鎖的線程就交給AQS去處理。
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) {
// hasQueuedPredecessors 判斷隊列還有沒有其它node,要保證公平
// 沒有在用cas設置狀態
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;
}
}
非公平實現:NonfairSync,我們可以發現基本和公平一樣,就沒有hasQueuedPredecessors方法,沒有遵循FIFO隊列的模式,而是不管隊列有沒有node,自己都可以去獲取鎖,不需要排隊
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
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;
}
②、lockInterruptibly支持中斷的獲取鎖,其實是調用了AQS的lockInterruptibly方法,在AQS方法裏面又回去調用tryAcquire方法,這個方法在上面已經解釋過了。
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
AQS的lockInterruptibly方法
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
③、tryLock(long timeout, TimeUnit unit),支持中斷,並且在這個基礎上增加了超時設置,其實也是調用了AQS的tryAcquireNanos方法,我們發現其實他也是調用的tryAcquire方法。
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
AQS的tryAcquireNanos方法
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
④、unlock釋放鎖,其實就是把state從n(可能發生了鎖的重入,需要多次釋放)變成0,這個不區分公平與非公平,首先其實也是調用AQS的release方法,然後AQS在調用子類Sync的tryRelease方法。
public void unlock() {
sync.release(1);
}
調用Sync的tryRelease方法
protected final boolean tryRelease(int releases) {
// 獲取鎖的狀態
int c = getState() - releases;
// 獲得鎖的線程才能釋放鎖
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 直到鎖的狀態是0,說明鎖釋放成功,因為有重入鎖
// 說明我們在一個線程裏面調用幾次lock,就要調用幾次unlock,才能最終釋放鎖
if (c == 0) {
free = true;
// 釋放線程的擁有者
setExclusiveOwnerThread(null);
}
// 設置鎖的狀態
setState(c);
return free;
}
⑤、newCondition方法,創建一個newCondition。
public Condition newCondition() {
return sync.newCondition();
}
⑥、getHoldCount方法,獲取當前線程獲得鎖的個數。
public int getHoldCount() {
return sync.getHoldCount();
}
final int getHoldCount() {
// 當前線程是否獲取到鎖
return isHeldExclusively() ? getState() : 0;
}
⑦、isHeldByCurrentThread方法,當前線程是否獲取到鎖。
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();
}
⑧、isLocked方法,是否有線程獲取到了鎖。
final boolean isLocked() {
return getState() != 0;
}
⑨、getOwner方法,獲取取得鎖的線程。
⑩、getQueueLength方法,獲取同步隊列的數量。
public final int getQueueLength() {
// 從aqs的尾節點開始往前遍歷,除去空節點(但是其實只有第一個節點是空節點),也就是thread != null
int n = 0;
for (Node p = tail; p != null; p = p.prev) {
if (p.thread != null)
++n;
}
return n;
}
四、總結
學習ReentrantLock,我們主要需要了解它,公平和非公平的實現,以及重入鎖的獲取與釋放的流程,還有最重要的就是要了解AQS,這是實現重入鎖的基礎,因為ReentrantLock只是實現了AQS獲取鎖和釋放鎖制定的模板方法的語義,所以要理解ReentrantLock獲取鎖成功和失敗具體都做了什麽邏輯,和AQS的實現是離不開的。
可以參考我的這一篇AQS的文章。
參考 《Java 並發編程的藝術》
ReentrantLock (重入鎖) 源碼淺析