- Java-AQS同步器 源碼解讀<一>獨占鎖加鎖
- Java-AQS同步器 源碼解讀<二>獨占鎖解鎖
- Java-AQS同步器 源碼解讀<三>共享鎖
- Java-AQS同步器 源碼解讀<四>-條件隊列上
- Java-AQS同步器 源碼解讀<五>-條件隊列下
今天繼續(xù)在昨天的源碼分析嗷嗷5侔!螟凭!
昨天 我們看了加鎖的代碼牙瓢,今天我們來看下解鎖的代碼刃鳄,解鎖的入口又怎么看呢?
獨占鎖解鎖
解鎖相對加鎖而言 簡單了很多
-release方法
我們還是拿ReentrantLock重入鎖解鎖舉例去看下怎么做的,然后一步一步的進(jìn)入
public void unlock() {
sync.release(1);
}
上面是ReentrantLock的解鎖方法辅柴,上一篇文章上我描述了sync 是ReentrantLock內(nèi)部抽象方法類 繼承了AQS 倒戏,那這個release 方法 我們很容易就定位到了在AQS的一個方法
/**
* AbstractQueuedSynchronizer 類中方法
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
-tryRelease方法
看到這個tryRelease 看過上一篇文章的 應(yīng)該心里知道 怎么回事 和加鎖的時候tryAcquire是一樣的怠噪,tryRelease 需要使用到此方法的類 去重寫這個方法,不讓回報錯杜跷,因為這個方法AQS沒有實現(xiàn)傍念,只是拋出異常,具體為什么會這么寫葱椭,上一篇文章我也提到過怎么回事捂寿,不清楚的 可以去上一篇文章中看一看
我們再去ReentrantLock類中看一看
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
abstract void lock();
//釋放資源
protected final boolean tryRelease(int releases) {
int c = getState() - releases;// 算下 當(dāng)前同步器中state的差值
//確保釋放和加鎖線程是同一個 上一篇中我也提到過
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {//如果C的值是0 說明沒有線程擁有鎖了
free = true;
/*設(shè)置擁有鎖的線程為null 因為現(xiàn)在鎖是沒線程暫用的 如果不修改 下次別的線程去獲取鎖的會有這個判斷
*/
setExclusiveOwnerThread(null);
}
setState(c);//修改 同步器的State 狀態(tài)
return free;
}
}
很快 我們找到了tryRelease在Sync中的實現(xiàn),題外說下Sync 2個子類一個非公平鎖一個公平鎖孵运,區(qū)別就是在加鎖秦陋,解鎖其實都一樣的,不信看代碼結(jié)構(gòu)就能看到治笨,2個子類的解鎖都沒有重寫 都是用了Sync類中的實現(xiàn)驳概。
那我們回到上面的release方法中
/*
* 釋放當(dāng)前資源
* 喚醒等待線程去獲取資源
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {//說明釋放資源成功
Node h = head;//當(dāng)前隊列里面的head節(jié)點
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//通知阻塞隊列里面的head的next節(jié)點去獲取鎖
return true;
}
return false;
}
-unparkSuccessor方法
/**
* 喚醒等待線程去獲取資源
*/
private void unparkSuccessor(Node node) {
/*
* 這邊判斷了下如果當(dāng)前節(jié)點狀態(tài)小于0赤嚼,更新這邊的節(jié)點狀態(tài)的0,是為了防止多次釋放的時候 會多次喚醒顺又,
* 因為上面的方法有個判斷waitStatus不等0才會執(zhí)行到這個方法里面
*/
int ws = node.waitStatus;//這邊的弄得節(jié)點就是 要釋放的節(jié)點 也就是當(dāng)前隊列的頭節(jié)點
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
// 如果當(dāng)前節(jié)點的next 節(jié)點 不存在或者waitStatus 大于0 說明next節(jié)點的線程已取消
if (s == null || s.waitStatus > 0) {
s = null;
//這個循環(huán)就是 從尾部節(jié)點開始往前找更卒,找到離node節(jié)點也就是當(dāng)前釋放節(jié)點最近的一個非取消的節(jié)點
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
/*一開始覺得 這行判斷null有點多余 因為上面去for 循環(huán)去找s 的時候 已經(jīng)判斷了不等于null
才可以進(jìn)下面的循環(huán)賦值的 后來一想 不對,你們猜為什么?
因為 可能在循環(huán)的過程中t在賦值給s后稚照,繼續(xù)循環(huán) 雖然條件不滿足蹂空,
但是這個時候已經(jīng)比修改成null 了 我是這么想的哈~不知道對不對~
*/
if (s != null)
LockSupport.unpark(s.thread);// 這邊就是喚醒當(dāng)前線程去獲取資源,
}
線程被喚醒后 就繼續(xù)執(zhí)行到parkAndCheckInterrupt 方法里面這邊 因為就是在這邊線程被阻塞的 后面就去繼續(xù)嘗試獲取資源了
總結(jié)一下
獨占鎖的解鎖比較簡單 分為2個步驟
第一步就是去tryRelease釋放資源
第二步 如果釋放資源成功果录,就進(jìn)入unparkSuccessor 方法里面上枕,找到下個合適的節(jié)點 去喚醒一個等待的線程