帶你看看Java-AQS同步器 源碼解讀<二>獨占鎖解鎖

  1. Java-AQS同步器 源碼解讀<一>獨占鎖加鎖
  2. Java-AQS同步器 源碼解讀<二>獨占鎖解鎖
  3. Java-AQS同步器 源碼解讀<三>共享鎖
  4. Java-AQS同步器 源碼解讀<四>-條件隊列上
  5. 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é)點 去喚醒一個等待的線程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市弱恒,隨后出現(xiàn)的幾起案子辨萍,更是在濱河造成了極大的恐慌,老刑警劉巖返弹,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锈玉,死亡現(xiàn)場離奇詭異,居然都是意外死亡义起,警方通過查閱死者的電腦和手機(jī)拉背,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來并扇,“玉大人去团,你說我怎么就攤上這事∏钣迹” “怎么了土陪?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長肴熏。 經(jīng)常有香客問我鬼雀,道長,這世上最難降的妖魔是什么蛙吏? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任源哩,我火速辦了婚禮,結(jié)果婚禮上鸦做,老公的妹妹穿的比我還像新娘励烦。我一直安慰自己,他們只是感情好泼诱,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布坛掠。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪屉栓。 梳的紋絲不亂的頭發(fā)上舷蒲,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機(jī)與錄音友多,去河邊找鬼牲平。 笑死,一個胖子當(dāng)著我的面吹牛域滥,可吹牛的內(nèi)容都是我干的纵柿。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼骗绕,長吁一口氣:“原來是場噩夢啊……” “哼藐窄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起酬土,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎格带,沒想到半個月后撤缴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡叽唱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年屈呕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棺亭。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡虎眨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出镶摘,到底是詐尸還是另有隱情嗽桩,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布凄敢,位于F島的核電站碌冶,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏涝缝。R本人自食惡果不足惜扑庞,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拒逮。 院中可真熱鬧罐氨,春花似錦、人聲如沸滩援。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至约啊,卻和暖如春邑遏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背恰矩。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工记盒, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人外傅。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓纪吮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親萎胰。 傳聞我的和親對象是個殘疾皇子碾盟,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容