Java多執行緒之ReentrantLock實現原理和原始碼分析(二)
章節概覽、
1、ReentrantLock概述
ReentrantLock字面含義是可重入的互斥鎖,實現了和synchronize關鍵字一樣的獨佔鎖功能。但是ReentrantLock使用的是自旋鎖,通過CAS硬體原語指令實現的輕量級的鎖,不會引起上下文切換。而Synchronize關鍵字是重量級的且是獨佔的悲觀鎖。在使用過程中,會引起上下文切換。同時ReentrantLock增加了一些高階的擴充套件功能,比如它可以實現公平鎖,同時也可以繫結多個Conditon。每個Condition都維護了自己的阻塞佇列。而不像wait/notify只維護一個佇列而引發的喚醒錯誤的情況。
2、ReentrantLock的實現模板
ReentrantLock鎖保證執行緒安全的實現模板如下:
class X {
private final ReentrantLock lock = new ReentrantLock();
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}}
從預設的實現模板可以看出,其使用方式和Synchronize不同,首先其不需要同步的物件。而Synchronize需要一個同步鎖物件。其次ReentrantLock是顯示呼叫。使用者自己可以控制從哪裡開始,到哪裡結束。而Synchronize只能同步程式碼塊等。具體ReentrantLock的使用方式,請參考:
3、ReentrantLock類圖結構
ReentrantLock的類圖結構如下:
從類的繼承圖上可以看出,ReentrantLock實現了Lock介面。同時其具有3個內部類,分別為Sync,NonfairSync,FairSync。而Sync有繼承了AbstractQueuedSynchronize,這個就是我們常說的AQS。而NonFairSync繼承Sync也就是我們所說的非公平鎖。FairSync繼承Sync實現了公平鎖功能。
3.1、Lock介面原始碼描述
/** * @see ReentrantLock * @see Condition * @see ReadWriteLock * * @since 1.5 * @author Doug Lea */ public interface Lock { //獲取鎖 void lock(); //獲取可中斷鎖 void lockInterruptibly() throws InterruptedException; //嘗試獲取鎖,立刻返回 boolean tryLock(); //在指定的時間範圍內嘗試獲取鎖,立即返回 boolean tryLock(long time, TimeUnit unit) throws InterruptedException; //釋放當前鎖 void unlock(); //獲取一個條件物件,用於執行緒等待喚醒 Condition newCondition(); }
3.2、ReentrantLock的構造方法和成員
ReentranLock的構造方法成員引數如下:
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
// 同步器的引用
private final Sync sync;
//非公平鎖的建構函式
public ReentrantLock() {
sync = new NonfairSync();
}
// 公平鎖的建構函式
public ReentrantLock(boolean fair) {
// 三目運算子,如果為true 則為公平鎖,反之為非公平鎖
sync = fair ? new FairSync() : new NonfairSync();
}
}
3.3、Sync抽象類核心原始碼分析
// 靜態的抽象內部類,修飾符為default,僅能在當前類所在的包中使用
// 繼承了AbstractQueuedSynchronizer抽象類
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 抽象方法,獲取當前鎖,具體實現在子類中實現
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
* 非公平鎖嘗試的去獲取鎖,立刻返回
*/
final boolean nonfairTryAcquire(int acquires) {
// 獲取當前執行緒
final Thread current = Thread.currentThread();
// 獲取當前系統的同步狀態變數state
int c = getState();
// 判斷當前的系統狀態變數是否為0
// 其中0,表示當前鎖空閒。大於0表示當前系統鎖已經被佔用
if (c == 0) {
// 利用CAS原語設定當前state的狀態值為1,立刻返回
if (compareAndSetState(0, acquires)) {
// 如果設定成功,表示已經獲取當前鎖,設定當前鎖的擁有者為當前執行緒
setExclusiveOwnerThread(current);
// 返回獲取成功
return true;
}
}
// 如果當前是state不為0,判斷當前執行緒是否已經擁有鎖
// 因為ReentrantLock是可衝入鎖,所以如果當前執行緒已經擁有鎖的情況下,可以再次使用該鎖
else if (current == getExclusiveOwnerThread()) {
// 如果當前執行緒已經擁有鎖,則把當前的 state + 1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 設定當前state的值
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
}
}
3.4、NonfairSync類核心原始碼分析
NonfairSync主要是非公平鎖的實現:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
* 實現Sync的方法
*/
final void lock() {
// 嘗試獲取鎖lock() 方法
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 獲取鎖失敗,加入到等待佇列
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
3.5、FairSync類核心原始碼分析
以下是公平鎖實現的核心原始碼
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
// 公平鎖沒有嘗試的去獲取鎖的功能,直接呼叫acquire()方法
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判斷當前執行緒是否為head的next節點。
// 如果是的話,則嘗試獲取鎖
// 獲取鎖成功進行一系列的設定
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;
}
}
hasQueuedPredecessors原始碼分析:
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
這裡的邏輯判斷稍微有些複雜,我們整理下思路。
return返回的程式碼,可以將邏輯判斷分為2部分,只要其中有一個為true,則返回為true。當返回為真的話, if (!hasQueuedPredecessors() && compareAndSetState(0, acquires))就失敗。我們詳細分析下:
- h != t && ((s = h.next) == null
這個邏輯成立的一種可能是head指向頭結點,tail此時還為null。考慮這種情況:當其他某個執行緒去獲取鎖失敗,需構造一個結點加入同步佇列中(假設此時同步佇列為空),在新增的時候,需要先建立一個無意義傀儡頭結點(在AQS的enq方法中,這是個自旋CAS操作),有可能在將head指向此傀儡結點完畢之後,還未將tail指向此結點。很明顯,此執行緒時間上優於當前執行緒,所以,返回true,表示有等待中的執行緒且比自己來的還早。
- s.thread != Thread.currentThread()
當前的執行緒和Head的next執行緒是否相等。如果不相等返回為true。說明當前執行緒不是head的next節點,因為在公平鎖中,只有head的next節點才有權利去獲取鎖
4、 結語
至此ReentrantLock的類結構和核心原始碼做了簡單分析。通過分析我們可以瞭解到ReentrantLock的整體結構,建構函式,以及當前類的成員變數。以及ReentrantLock的內部類的情況,內部類的繼承情況。ReentrantLock中使用了大量的內部類,很好的做到了封裝性。對細節實現部分進行了封裝。使用者只需要呼叫構造方法,而不用去關係其內部的實現情況。同時也是設計模式中的迪米特法則:最少知道原則的體現。實現了很好的高內聚減少了耦合。