Java 同步鎖ReentrantLock與抽象同步隊列AQS

AbstractQueuedSynchronizer 抽象同步隊列,它是個模板類提供了許多以鎖相關的操作,常說的AQS指的就是它。AQS繼承了AbstractOwnableSynchronizer類,AOS用於保存線程對象,保存什麼線程對象呢?保存鎖被獨佔的線程對象

抽象同步隊列AQS除了實現序列化標記接口,並沒有實現任何的同步接口,該類提供了許多同步狀態獲取和釋放的方法給自定義同步器使用,如ReentrantLock的內部類Sync。抽象同步隊列支持獨佔式或共享式的的獲取同步狀態,方便實現不同類型的自定義同步器。一般方法名帶有Shared的為共享式,比如,嘗試以共享式的獲取鎖的方法int tryAcquireShared(int),而獨佔式獲取鎖方法為boolean tryAcquire(int)

AQS是抽象同步隊列,其重點就是同步隊列如何操作同步隊列

同步隊列

雙向同步隊列,採用尾插法新增節點,從頭部的下一個節點獲取操作節點,節點自旋獲取同步鎖,實現FIFO(先進先出)原則。

image

理解節點中的屬性值作用

  • prev:前驅節點;即當前節點的前一個節點,之所以叫前驅節點,是因為前一個節點在使用完鎖之後會解除後一個節點的阻塞狀態;

  • next:後繼節點;即當前節點的後一個節點,之所以叫後繼節點,是因為「後繼有人」了,表示有「下一代」節點承接這個獨有的鎖🔒;

  • nextWaiter:表示指向下一個Node.CONDITION狀態的節點(本文不講述Condition隊列,在此可以忽略它);

  • thread:節點對象中保存的線程對象,節點都是配角,線程才是主角;

  • waitStatus:當前節點在隊列中的等待狀態;waitStatus = CANCELLED = 1,表示線程已經取消(該狀態下的節點為作廢節點,將從隊列中斷開);

    • waitStatus = SIGNAL = -1,表示線程處於請求釋放的狀態,後繼線程需要阻塞等待(該狀態下的節點線程處於阻塞等待狀態或獲取鎖未釋放狀態);

    • waitStatus = CONDITION = -2,表示線程正在等待

    • waitStatus = PROPAGATE = -3,在共享情況下,表示下一個被請求的shared應該無條件傳播;

    • waitStatus = 0,表示節點初始化時的默認值(int類型成員變量的默認值)。

注意1:節點對象中的prev、next和nextWaiter都是一個完整的Node節點對象,也就是說每個節點都保存了前後節點的對象,如果沒有則為null。

注意2:head節點是個虛節點(prev=null、thread=null),但head本身是一個實際存在的節點對象,起到標記隊列的開頭;尾節點tail節點的next=null,等待新節點插入。

頭節點head為什麼是虛節點

重點必須明確知道頭節點head為什麼是虛節點!!!這很重要。

原因是,當前節點在獲取到鎖🔒之後,它這個線程對象就會被保存到AOS(AbstractOwnableSynchronizer)中的exclusiveOwnerThread 對象,(一開頭就提到過了,在這裡再強調一次),所以在隊列中,頭節點是無需存儲Thread對象的了。那為什麼設計成這樣呢?因為存在臨界情況就是只有一個線程獲取鎖資源時,無需初始化生成同步隊列,直接獲取同步鎖即可。只有存在鎖未釋放同時又進來了新的線程時,才會去初始化同步隊列,並為未釋放鎖的線程佔個位置,這個位置就是頭節點head,表明前面還有個線程在使用資源。

// 初始化隊列的方法private Node enq(final Node node) {
    // 死循環
    for (;;) {
        Node t = tail;
        // 沒頭沒尾時
        if (t == null) { // Must initialize
            // 生成頭節點(尾節點)
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            // 有頭有尾後,才把需要等待的線程節點加入隊列中
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

在獨佔線程釋放鎖時,判斷head是否為null,即可知道同步隊列是否存在,如果同步隊列不存在,那麼無需執行嘗試喚醒後繼節點那些操作了。

在同個時間節點中,單個線程只需要操作AOS的Thread對象和AQS的state狀態即可實現同步鎖和鎖的可重入性。

線程加入同步隊列的過程

在鎖被佔用時,獲取鎖失敗後,當前線程被封裝成Node節點並加入到隊列尾部。

tryAcquire(arg)返回false時,

執行acquireQueued(addWaiter(Node.EXCLUSIVE), arg))操作,

addWaiter(Node.EXCLUSIVE) 為新增節點到同步隊列,隊列未初始化時會執行enq完成初始化後再新增節點到隊列。

因此,新增節點分為首次生成同步隊列同時新增節點和在原有同步隊列中插入新增節點。

首次生成同步隊列新增節點:通過enq(final Node node)方法,先初始化頭節點(虛節點),再通過原子操作compareAndSetTail方法從隊列尾部插入新節點。

在原有同步隊列中新增節點:通過原子操作compareAndSetTail方法從隊列尾部插入新節點。

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

private Node addWaiter(Node mode) {
    // 使用當前線程生成新節點
    Node node = new Node(Thread.currentThread(), mode);
    // 獲取同步器的尾節點
    Node pred = tail;
    if (pred != null) {
        // 第一步:新節點的prev節點指向尾部節點(pred=tail)
        node.prev = pred;
        // 第二部:CAS比較尾節點,相等就讓tail=node
        if (compareAndSetTail(pred, node)) {
            // 第三步:pred=舊的tail,即舊的尾節點的next節點指向新節點
            pred.next = node;
            return node;
        }
    }
    // 當tail=null 時執行,邏輯相類似的;enq初始化的head節點為虛節點
    enq(node);
    return node;
}

// 將節點插入隊列,必要時初始化(即tail=null時,也即是同步隊列沒有節點時初始化)。private Node enq(final Node node) {
    // 死循環
    for (;;) {
        // 第一次進來tail=null,第二次進來tail=head=new Node()
        Node t = tail;
        if (t == null) {
            // 創建一個空節點作為head節點
            if (compareAndSetHead(new Node()))
                tail = head;
        } else { // 下面就是正常的尾插法新增節點
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">配合源碼和動圖理解:新增隊列節點過程(三部曲)</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">![](file://D:\Program Files\Git文檔\xmindFile\01:Java\02:Java高級\多線程\images\AQS新增隊列節點過程(三部曲).gif?msec=1668428554895)</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">使用尾插法新增同步隊列節點</p>
<ul data-tool="mdnice編輯器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;">
<li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;"><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">第一步:新增節點的prev節點指向尾節點tail;</p>
</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;"><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">第二步:尾節點tail 和新節點做CAS操作,即compareAndSetTail(pred,node) ,即同步器的tail節點指向新節點;</p>
</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;"><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">第三步:舊的尾節點的next節點指向新節點(此時的新節點=尾節點tail)</p>
</section></li></ul>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">最終結果圖</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">![](file://D:\Program Files\Git文檔\xmindFile\01:Java\02:Java高級\多線程\images\AQS數據結構-第三步.png?msec=1668428569216)</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">新增節點加入隊列之後,在同步隊列中線程怎麼等待?線程怎麼獲取鎖呢?</p>
<h2 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span class="prefix" style="display: none;"></span><span class="content" style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">節點線程獲取鎖</span><span class="suffix"></span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">看代碼前必須明確知道哪個節點是要獲取鎖的。頭節點為虛節點,標記隊列的開頭,真正要獲取鎖的是頭節點的後繼節點。</p>
<h3 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px;"><span class="prefix" style="display: none;"></span><span class="content">獲取鎖過程</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">鎖在釋放時調用的關鍵流程:</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">ReentrantLock#lock() -&gt; Sync#lock() -&gt; AQS#acquire(1) -&gt; NonfairSync#tryAcquire(1) 【或FairSync#tryAcquire(1)】-&gt; AQS#addWaiter(node) -&gt; AQS#acquireQueued(node,1) -&gt; AQS#selfInterrupt()</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">關鍵代碼👇</p>
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(//files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br>&nbsp;*&nbsp;以獨佔不可中斷模式獲取已在隊列中的線程。<br>&nbsp;*/</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">acquireQueued</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;Node&nbsp;node,&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span>&nbsp;arg)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;異常標誌狀態</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span>&nbsp;failed&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;是否發生過中斷的標誌</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span>&nbsp;interrupted&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;自旋鎖&gt;&gt;&gt;死循環:每個node都獨立執行着這個死循環,直至線程被阻塞</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">for</span>&nbsp;(;;)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;獲取前驅節點(當前節點的前一個節點)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;Node&nbsp;p&nbsp;=&nbsp;node.predecessor();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;當前節點的前驅節點是否等於頭節點(虛節點),等於就會執行嘗試獲取鎖&nbsp;tryAcquire(arg)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(p&nbsp;==&nbsp;head&nbsp;&amp;&amp;&nbsp;tryAcquire(arg))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setHead(node);&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;設置當前節點為頭節點,在獲取鎖成功時,thread對象已經保存到AQS中的exclusiveOwnerThread了</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p.next&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;前驅節點的next指向null,斷開前驅節點(舊的頭節點)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;failed&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;只要當前節點node正常獲取到鎖,就不會執行finally的cancelAcquire(node)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;interrupted;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;前驅節點的waitStatus=-1時,當前node會被阻塞,防止無限循環浪費資源</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(shouldParkAfterFailedAcquire(p,&nbsp;node)&nbsp;&amp;&amp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parkAndCheckInterrupt())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interrupted&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">finally</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/*&nbsp;正常情況:只有return時,才會執行finally代碼,而只要return,failed都等於false,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;所以,failed&nbsp;是為了避免節點發生異常時,node沒有被釋放。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;比如:node.predecessor()&nbsp;可能產生空指針異常。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(failed)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cancelAcquire(node);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br><br><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">setHead</span><span class="hljs-params" style="line-height: 26px;">(Node&nbsp;node)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;head&nbsp;=&nbsp;node;<br>&nbsp;&nbsp;&nbsp;&nbsp;node.thread&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;node.prev&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>;<br>}<br></code></pre>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">為什麼在嘗試獲取鎖前要判斷前驅節點是否為頭節點?</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">因為除了<strong style="font-weight: bold; color: black;">阻塞被釋放</strong>會讓死循環繼續執行的情況外,還有<strong style="font-weight: bold; color: black;">中斷</strong>指令也會使線程從阻塞狀態中<strong style="font-weight: bold; color: black;">被釋放</strong>,所以存在任意節點提前重新執行「死循環」嘗試獲取鎖的情況,如果不判斷獲取鎖節點的前驅節點是否為頭節點,那就會出現提前嘗試獲取鎖,從而破壞了同步隊列的先進先出(FIFO)原則,說白了,就是被插隊了。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">當前節點不是頭節點時,執行以下代碼</p>
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(//files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br>&nbsp;*&nbsp;檢查和更新獲取鎖失敗的節點的狀態。如果線程需要等待,則返回true,使其執行阻塞操作。<br>&nbsp;*/</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">shouldParkAfterFailedAcquire</span><span class="hljs-params" style="line-height: 26px;">(Node&nbsp;pred,&nbsp;Node&nbsp;node)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;獲取前驅節點的等待狀態</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span>&nbsp;ws&nbsp;=&nbsp;pred.waitStatus;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(ws&nbsp;==&nbsp;Node.SIGNAL)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;因為前驅節點處於請求釋放的狀態,所以當前節點需要阻塞等待,會返回true,從而執行後續方法進入阻塞狀態</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(ws&nbsp;&gt;&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;前驅節點被標上取消標誌了,需要跳過前驅節點並不斷重試</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">do</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;循環向前查找取消節點,把取消節點從隊列中移除</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node.prev&nbsp;=&nbsp;pred&nbsp;=&nbsp;pred.prev;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">while</span>&nbsp;(pred.waitStatus&nbsp;&gt;&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pred.next&nbsp;=&nbsp;node;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">else</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;waitStatus必須為0或等於PROPAGATE=-3<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;表示需要設置前驅節點等待狀態為SIGNAL,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;將會在外層循環再次嘗試獲取鎖,如果再次獲取鎖失敗,那麼就會阻塞當前線程<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;compareAndSetWaitStatus(pred,&nbsp;ws,&nbsp;Node.SIGNAL);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;<br>}<br></code></pre>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">當<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">shouldParkAfterFailedAcquire(p, node)</code> 返回true時,將會執行阻塞操作<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">parkAndCheckInterrupt())</code>,其通過線程阻塞工具類方法<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">LockSupport.park(this)</code> 來阻塞當前線程。</p>
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(//files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">parkAndCheckInterrupt</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;阻塞當前線程線程調度</span><br>&nbsp;&nbsp;&nbsp;&nbsp;LockSupport.park(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;清除當前線程的中斷狀態,並返回上一次的中斷狀態</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;Thread.interrupted();<br>}<br></code></pre>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">注意:<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">LockSupport.park(this)</code> 阻塞後,需要喚醒阻塞才會執行後續操作,可通過解除阻塞<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">LockSupport.unpark(thread)</code> 或 中斷<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">thread.interrupt()</code> 來喚醒阻塞。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">只有<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">shouldParkAfterFailedAcquire</code> 和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">parkAndCheckInterrupt</code>都返回true時,才會執行interrupted = true,即只有是中斷導致阻塞結束時,才返回true,此時 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">selfInterrupt()</code>重新執行一次中斷操作。</p>
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(//files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">acquireQueued</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;Node&nbsp;node,&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span>&nbsp;arg)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;......<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(shouldParkAfterFailedAcquire(p,&nbsp;node)&nbsp;&amp;&amp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parkAndCheckInterrupt())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;只有是中斷導致阻塞結束時,才返回true</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interrupted&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;......<br>}<br><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">acquire</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span>&nbsp;arg)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(!tryAcquire(arg)&nbsp;&amp;&amp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;acquireQueued(addWaiter(Node.EXCLUSIVE),&nbsp;arg))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;中斷當前線程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selfInterrupt();<br>}<br><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">selfInterrupt</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;中斷當前線程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;Thread.currentThread().interrupt();<br>}<br></code></pre>
<h3 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px;"><span class="prefix" style="display: none;"></span><span class="content">為什麼需要再一次執行中斷呢?</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">因為存在中斷<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">thread.interrupt()</code> 喚醒自旋鎖阻塞的情況,而<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Thread.interrupted()</code> 獲取中斷狀態並清除當前線程的中斷狀態,所以需要重新執行一次中斷操作<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">selfInterrupt()</code>,將中斷標誌置為true。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">這種獲取鎖的方式是<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">非中斷鎖</code>,就是無法通過中斷的方式<strong style="font-weight: bold; color: black;">結束</strong>鎖的獲取,區別於<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">中斷鎖</code>,所以該方式在獲取鎖的過程中,不會處理中斷,只是記錄中斷狀態,Thread.interrupted() 獲取中斷狀態後清除中斷狀態,所以需要重新設置中斷標誌為true。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">如果你想要<strong style="font-weight: bold; color: black;">處理中斷的情況</strong>,那我們可以在acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 返回true 的時候去處理。比如:拋出中斷異常。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">如果你需要在<strong style="font-weight: bold; color: black;">線程發生中斷時結束獲取鎖</strong>,那麼可以考慮使用<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">lockInterruptibly()</code>來獲取鎖。</p>
<h3 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px;"><span class="prefix" style="display: none;"></span><span class="content">兩種方式獲取鎖的區別</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">lock()</code>方式獲取鎖:自旋鎖只會在正常獲取到鎖或發生異常時結束自旋鎖(死循環)。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">void lockInterruptibly()</code> 方式獲取鎖:會在發生中斷的情況下,拋出中斷異常<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">throw new InterruptedException();</code> 從而跳出自旋鎖(死循環),而調用lockInterruptibly() 的方法需要捕獲中斷異常,做一些異常處理。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">獲取鎖的<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">lock()</code>和<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">lockInterruptibly()</code>的主要區別是:<strong style="font-weight: bold; color: black;">中斷是否會結束鎖的獲取</strong>。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">看看源碼怎麼實現中斷結束鎖的獲取</p>
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(//files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">doAcquireInterruptibly</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span>&nbsp;arg)</span><br>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throws</span>&nbsp;InterruptedException&nbsp;</span>{<br>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;Node&nbsp;node&nbsp;=&nbsp;addWaiter(Node.EXCLUSIVE);<br>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span>&nbsp;failed&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">for</span>&nbsp;(;;)&nbsp;{<br>&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;Node&nbsp;p&nbsp;=&nbsp;node.predecessor();<br>&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(p&nbsp;==&nbsp;head&nbsp;&amp;&amp;&nbsp;tryAcquire(arg))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;setHead(node);<br>&nbsp;&nbsp;&nbsp;&nbsp;p.next&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;help&nbsp;GC</span><br>&nbsp;&nbsp;&nbsp;&nbsp;failed&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(shouldParkAfterFailedAcquire(p,&nbsp;node)&nbsp;&amp;&amp;<br>&nbsp;&nbsp;&nbsp;&nbsp;parkAndCheckInterrupt())<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;只有是中斷導致阻塞結束時,才拋出中斷異常</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throw</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span>&nbsp;InterruptedException();<br>&nbsp;&nbsp;}<br>&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">finally</span>&nbsp;{<br>&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(failed)<br>&nbsp;&nbsp;&nbsp;cancelAcquire(node);<br>&nbsp;}<br>}<br></code></pre>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">中斷異常拋出後,將會執行finally 代碼塊,取消正在進行嘗試獲取鎖的節點。</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">到此為止,<strong style="font-weight: bold; color: black;">lock()獲取鎖的概要過程</strong>為</p>
<blockquote class="multiquote-1" data-tool="mdnice編輯器" style="border: none; display: block; font-size: 0.9em; overflow: auto; overflow-scrolling: touch; border-left: 3px solid rgba(0, 0, 0, 0.4); color: #6a737d; padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 10px; margin-bottom: 20px; margin-top: 20px; border-left-color: rgb(239, 112, 96); background: #fff9f9;">
<p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0px; color: black; line-height: 26px;">先嘗試獲取鎖、失敗就將線程封裝成節點並加入到隊列尾部、進入自旋鎖(第一次嘗試獲取鎖失敗,將前驅節點的waitStatus改為-1;第二次嘗試獲取鎖失敗,因為前驅節點的waitStatus=-1,所以執行阻塞當前線程操作避免死循環耗費資源)、等待頭節點線程釋放同步狀態之後,將發起解除阻塞指令或阻塞線程被中斷後,後繼節點再次嘗試獲取鎖。</p>
</blockquote>
<h3 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px;"><span class="prefix" style="display: none;"></span><span class="content">取消異常節點</span><span class="suffix" style="display: none;"></span></h3>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">前面提到,在AQS#shouldParkAfterFailedAcquire(pred, node) 中談到,當節點waitStatus&gt;0 時,也即是對帶有取消狀態的節點進行移除。那麼節點在什麼時候被改為CANCELLED 的呢?</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">在AQS#acquireQueued(node, arg)中,正常獲取到鎖時,failed都等於false,只有當發生異常時,failed 才等於true,從而執行到AQS#cancelAcquire(node)。也就是說cancelAcquire() 是用於處理獲取鎖過程中,對發生異常節點的進行移除。</p>
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(//files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">acquireQueued</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;Node&nbsp;node,&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span>&nbsp;arg)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;是否發生異常的標誌</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span>&nbsp;failed&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">for</span>&nbsp;(;;)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;Node&nbsp;p&nbsp;=&nbsp;node.predecessor();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(p&nbsp;==&nbsp;head&nbsp;&amp;&amp;&nbsp;tryAcquire(arg))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;failed&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;只要當前節點node正常獲取到鎖,就不會執行finally的cancelAcquire(node)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;interrupted;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">finally</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/*&nbsp;正常情況:只有return時,才會執行finally代碼,而只要return,failed都等於false,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;所以,failed&nbsp;是為了避免節點發生異常時,node沒有被移除。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(failed)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cancelAcquire(node);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">詳細看下AQS#cancelAcquire(node) 是怎麼處理的</p>
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(//files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br>&nbsp;*&nbsp;取消正在進行嘗試獲取鎖的節點<br>&nbsp;*/</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">cancelAcquire</span><span class="hljs-params" style="line-height: 26px;">(Node&nbsp;node)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;如果節點不存在,則忽略</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(node&nbsp;==&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;使當前節點變成虛節點</span><br>&nbsp;&nbsp;&nbsp;&nbsp;node.thread&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;跳過帶有「取消狀態」的前驅節點</span><br>&nbsp;&nbsp;&nbsp;&nbsp;Node&nbsp;pred&nbsp;=&nbsp;node.prev;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">while</span>&nbsp;(pred.waitStatus&nbsp;&gt;&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node.prev&nbsp;=&nbsp;pred&nbsp;=&nbsp;pred.prev;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;predNext節點</span><br>&nbsp;&nbsp;&nbsp;&nbsp;Node&nbsp;predNext&nbsp;=&nbsp;pred.next;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;修改當前節點的waitStatus為CANCELLED=1</span><br>&nbsp;&nbsp;&nbsp;&nbsp;node.waitStatus&nbsp;=&nbsp;Node.CANCELLED;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;如果當前節點為尾節點tail,則只需要移除自己即可。</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(node&nbsp;==&nbsp;tail&nbsp;&amp;&amp;&nbsp;compareAndSetTail(node,&nbsp;pred))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;pred節點變成了尾節點tail,所以&nbsp;pred.next=null</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;compareAndSetNext(pred,&nbsp;predNext,&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">else</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span>&nbsp;ws;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/*&nbsp;當前節點的前驅節點不為頭節點,則true;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;前驅節點的ws狀態為SIGNAL,則true;ws不為SIGNAL,但ws&lt;=0時(即不是取消狀態),則CAS操作改為SIGNAL,改成功則為true;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;前驅節點不是虛接點,則true<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(pred&nbsp;!=&nbsp;head<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&amp;&amp;&nbsp;((ws&nbsp;=&nbsp;pred.waitStatus)&nbsp;==&nbsp;Node.SIGNAL&nbsp;||&nbsp;(ws&nbsp;&lt;=&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>&nbsp;&amp;&amp;&nbsp;compareAndSetWaitStatus(pred,&nbsp;ws,&nbsp;Node.SIGNAL)))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&amp;&amp;&nbsp;pred.thread&nbsp;!=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/*&nbsp;如果上述都滿足,則將「當前節點的前驅節點的後繼節點」指向「當前節點的後繼節點」<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;說白了,節點的next指向就是由&nbsp;A-&gt;B-&gt;C&nbsp;改為&nbsp;A-&gt;C;B為當前節點。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;關於節點的prev指向就沒有變&nbsp;A&lt;-B&lt;-C&nbsp;還是&nbsp;A&lt;-B&lt;-C,也就是說B節點還沒真正斷開。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;節點的prev指向的修改需要判斷ws狀態是否為CANCELLED後,才做修改。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Node&nbsp;next&nbsp;=&nbsp;node.next;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(next&nbsp;!=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>&nbsp;&amp;&amp;&nbsp;next.waitStatus&nbsp;&lt;=&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;compareAndSetNext(pred,&nbsp;predNext,&nbsp;next);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">else</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;喚醒當前節點的後繼節點的阻塞線程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unparkSuccessor(node);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;當前節點的後繼節點指向自己</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node.next&nbsp;=&nbsp;node;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">以上都是對next節點的指向做修改,關於節點的prev指向的修改需要判斷ws狀態是否為CANCELLED後,才做修改,循環向前查找取消節點,把取消節點從隊列中剔除。其對應源碼如下</p>
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(//files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">shouldParkAfterFailedAcquire</span><span class="hljs-params" style="line-height: 26px;">(Node&nbsp;pred,&nbsp;Node&nbsp;node)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;獲取前驅節點的等待狀態</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span>&nbsp;ws&nbsp;=&nbsp;pred.waitStatus;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(ws&nbsp;&gt;&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;前驅節點被標上取消標誌了,需要跳過前驅節點並不斷重試</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">do</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;循環向前查找取消節點,把取消節點從隊列中剔除</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node.prev&nbsp;=&nbsp;pred&nbsp;=&nbsp;pred.prev;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">while</span>&nbsp;(pred.waitStatus&nbsp;&gt;&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pred.next&nbsp;=&nbsp;node;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">else</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;......<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;<br>}<br></code></pre>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">在AQS#unparkSuccessor() 通過線程阻塞工具類方法<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">LockSupport.unpark(thread)</code> 來喚醒後繼節點的阻塞線程。<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">AQS#unparkSuccessor()</code> 除了<strong style="font-weight: bold; color: black;">取消異常節點</strong>時用到外,還在<strong style="font-weight: bold; color: black;">鎖的釋放</strong>時調用,實現功能都是--喚醒當前節點的後繼節點的阻塞線程,後繼節點就會繼續執行自旋鎖來嘗試獲取鎖。</p>
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(//files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br>&nbsp;*&nbsp;喚醒當前節點的後繼節點的阻塞線程(如果存在)<br>&nbsp;*/</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">unparkSuccessor</span><span class="hljs-params" style="line-height: 26px;">(Node&nbsp;node)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;如果waitStatus&lt;0,則將waitStatus&nbsp;置為默認值0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span>&nbsp;ws&nbsp;=&nbsp;node.waitStatus;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(ws&nbsp;&lt;&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;compareAndSetWaitStatus(node,&nbsp;ws,&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;但如果為null&nbsp;或為取消狀態,則從tail向前遍歷以查找到實際未取消的後繼節點。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;Node&nbsp;s&nbsp;=&nbsp;node.next;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(s&nbsp;==&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>&nbsp;||&nbsp;s.waitStatus&nbsp;&gt;&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">for</span>&nbsp;(Node&nbsp;t&nbsp;=&nbsp;tail;&nbsp;t&nbsp;!=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>&nbsp;&amp;&amp;&nbsp;t&nbsp;!=&nbsp;node;&nbsp;t&nbsp;=&nbsp;t.prev)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(t.waitStatus&nbsp;&lt;=&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s&nbsp;=&nbsp;t;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;對後繼節點的線程釋放阻塞</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(s&nbsp;!=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LockSupport.unpark(s.thread);<br>}<br></code></pre>
<h2 data-tool="mdnice編輯器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span class="prefix" style="display: none;"></span><span class="content" style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">節點線程釋放鎖</span><span class="suffix"></span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">獲取鎖搞懂後,釋放鎖就是很簡單了</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;"><strong style="font-weight: bold; color: black;">處理流程</strong></p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">鎖在釋放時調用的關鍵流程:ReentrantLock#unlock() -&gt; Sync#release(1) -&gt; AQS#tryRelease(1) -&gt; LockSupport#unparkSuccessor(head)</p>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">unparkSuccessor(head)方法在前面已經講述過不再贅述,前三個方法源碼如下👇</p>
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(//files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;java.util.concurrent.locks.ReentrantLock</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">unlock</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;ReentrantLock&nbsp;API&nbsp;交由同步隊列模板方法實現</span><br>&nbsp;&nbsp;&nbsp;&nbsp;sync.release(<span class="hljs-number" style="color: #d19a66; line-height: 26px;">1</span>);<br>}<br><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;java.util.concurrent.locks.AbstractQueuedSynchronizer</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">release</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span>&nbsp;arg)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;嘗試釋放鎖,成功則喚醒後繼節點</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(tryRelease(arg))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Node&nbsp;h&nbsp;=&nbsp;head;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(h&nbsp;!=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>&nbsp;&amp;&amp;&nbsp;h.waitStatus&nbsp;!=&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;喚醒當前節點的後繼節點的阻塞線程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unparkSuccessor(h);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;<br>}<br><br><br><span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;java.util.concurrent.locks.ReentrantLock.Sync</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">tryRelease</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span>&nbsp;releases)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;state:每釋放1次鎖就會-1(相反:重入性,每獲取1次鎖就會+1)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">int</span>&nbsp;c&nbsp;=&nbsp;getState()&nbsp;-&nbsp;releases;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;當前線程是否是獨佔鎖線程,不是就拋出異常</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(Thread.currentThread()&nbsp;!=&nbsp;getExclusiveOwnerThread())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throw</span>&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span>&nbsp;IllegalMonitorStateException();<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span>&nbsp;free&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;當state=0時,說明獲取鎖的次數已經釋放完,可以解除獨佔鎖線程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;(c&nbsp;==&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;鎖釋放成功</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;free&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;獨佔鎖線程置為null</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setExclusiveOwnerThread(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">null</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">//&nbsp;記錄每次state的變化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;setState(c);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">return</span>&nbsp;free;<br>}<br></code></pre>
<p data-tool="mdnice編輯器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">最後附上以 ReentrantLock 的<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">lock()</code>為例,裏面幾乎畫出了獲取鎖的所有代碼的執行過程</p>
</section>
Tags: