文章目錄
一、ReentrantLock介紹
ReentrantLock重入鎖,能夠實現對同一個資源的重複加鎖,即當前線程重複申請資源的時候,在已經擁有鎖的前提下,不用被阻塞,便可以直接擁有鎖。對於ReentrantLock的理解主要是理解如何實現重入性,公平鎖和非公平鎖對Sync內部類的實現。我們在這裏主要講解公平鎖和非公平鎖,重入性的實現在講解的時候指出。
在使用的時候,官方建議一直跟隨try代碼塊,例如
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
這樣可以避免因爲出現異常等原因無法釋放鎖,導致其他線程一直無法獲取鎖。
二、ReentrantLock繼承關係
該圖顯示了ReentrantLock的繼承關係以及內部實現類。
ReentrantLock實現了Lock接口,表明該鎖是JDK層面的鎖,而不是JVM層面的鎖(synchronized),也可以實現對資源進行加鎖,解鎖等操作。
其中FairSync是公平鎖的實現類,NonfairSync是非公平鎖的實現類。Sync繼承自AbstractQueuedSynchronizer抽象類。
三、Sync
繼承自AbstractQueuedSynchronizer,簡稱AQS,他提供了一個機遇FIFO的隊列,可以用於實現依賴於先進先出(FIFO)等待隊列的阻塞鎖鎖和相關同步器等。
3.1 主要實現的方法
加鎖操作
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*
* 執行非公平的tryLock,tryAcquire繼承自子類,
* 但是兩個都需要非公平的trylock方法
*
*/
final boolean nonfairTryAcquire(int acquires) { // 傳入請求資源的個數
final Thread current = Thread.currentThread(); // 獲得當前線程
int c = getState(); // 獲得鎖的狀態
if (c == 0) { // 當前沒有被鎖定
if (compareAndSetState(0, acquires)) { // 嘗試獲取鎖
setExclusiveOwnerThread(current); // 設置當前擁有獨佔訪問權限的線程
return true; // 加鎖成功,返回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); // 將擁有獨佔權限的線程設置爲null,表示沒有線程擁有獨佔訪問權限
}
setState(c); // 更新鎖狀態
return free;
}
裏面的state成員變量在AbstractQueuedSynchronizer抽象類中,表示的是同步的狀態,數值爲多少就表示有多少的鎖。
四、FairSync(公平鎖)
4.1 加鎖
加鎖時直接調用的是lock()方法,lock()方法通過acquire()方法間接的調用tryAcquire()方法進行加鎖。
/**
* 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) { // 當前鎖爲空
if (!hasQueuedPredecessors() && // 判斷是否有比當前線程等待更久的線程
compareAndSetState(0, acquires)) { // 如果沒有就使用CAS算法,修改鎖狀態
setExclusiveOwnerThread(current); // 設置擁有獨佔訪問權限的線程是當前線程
return true; // 加鎖成功,返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; // 加鎖失敗
}
在沒有鎖的情況下使用CAS算法進行修改,而在有鎖的情況下直接修改便可,是因爲在有鎖的情況下其他線程無法訪問並修改狀態,而在無鎖狀態下,其他線程也可以訪問並修改鎖的狀態,所以必須使用CAS算法。
4.2 加鎖失敗,進入等待
當在加鎖的時候,會調用java.util.concurrent.locks.AbstractQueuedSynchronizer中的acquire()方法
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 先嚐試加鎖
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 如果加鎖失敗便將該線程加入到等待隊列
selfInterrupt(); // 兩者均失敗,便終止該線程
}
4.3 釋放鎖
鎖的釋放使用的是Sync類裏面的tryRelease()方法,與上述的釋放鎖的方式一致。
五、非公平鎖
5.1 加鎖
final void lock() {
if (compareAndSetState(0, 1)) // 使用CAS算法判斷當前是否已經被鎖住(如果成功交換就代表沒有被鎖住)
setExclusiveOwnerThread(Thread.currentThread()); // 就設置擁有獨佔權限的線程是當前線程
else
acquire(1); // 將會重新嘗試加鎖,如果依舊加鎖失敗,便加入等待隊列,然後中斷當前線程
}
非公平鎖在第一步加鎖失敗,重新嘗試加鎖的時候,會調用Sync中的nonfairTryAcquire()方法,該方法上面已經講述過,便不在贅述。
5.2 釋放鎖
鎖的釋放使用的是Sync類裏面的tryRelease()方法
六、其他方法
其他方法均是獲得一些基本信息,直接閱讀就可以看懂
如果文章中有任何錯誤的地方,歡迎指出,謝謝!