AQS(acquireQueued(Node, int) 3)--隊列同步器


1.acquireQueued(Node, int)

   源碼:

        final boolean acquireQueued(final Node node, int arg) {

               // 標識是否獲取資源失敗                

               boolean failed = true;

               try {

                    // 標識當前線程是否被中斷過

                   boolean interrupted = false;

                   // 自旋操作

                   for (;;) {

                        // 獲取當前節點的前繼節點

                       final Node p = node.predecessor();

                       // 如果前繼節點為頭結點,說明排隊馬上排到自己了,可以嘗試獲取資源,若獲取資源成功,則執行下述操作

                      if (p == head && tryAcquire(arg)) {

                         // 將當前節點設置為頭結點

                        setHead(node);

                         // 說明前繼節點已經釋放掉資源了,將其next置空,以方便虛擬機回收掉該前繼節點

                         p.next = null; // help GC

                         // 標識獲取資源成功

                         failed = false;

                         // 返回中斷標記

                         return interrupted;

                      }

                      // 若前繼節點不是頭結點,或者獲取資源失敗,

                      // 則需要通過shouldParkAfterFailedAcquire函數

                      // 判斷是否需要阻塞該節點持有的線程

                     // 若shouldParkAfterFailedAcquire函數返回true,

                     // 則繼續執行parkAndCheckInterrupt()函數,

                     // 將該線程阻塞並檢查是否可以被中斷,若返回true,則將interrupted標志置於true

                     if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())

                           interrupted = true;

                 }

            } finally {

                 // 最終獲取資源失敗,則當前節點放棄獲取資源

                 if (failed)

                        cancelAcquire(node);

            }

      }

    具體看一下shouldParkAfterFailedAcquire函數:

    // shouldParkAfterFailedAcquire是通過前繼節點的waitStatus值來判斷是否阻塞當前節點的線程的

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {

        // 獲取前繼節點的waitStatus值ws

       int ws = pred.waitStatus;

       // 如果ws的值為Node.SIGNAL(-1),則直接返回true

      // 說明前繼節點完成資源的釋放或者中斷后,會通知當前節點的,回家等通知就好了,不用自旋頻繁地來打聽消息

      if (ws == Node.SIGNAL) return true;

      // 如果前繼節點的ws值大於0,即為1,說明前繼節點處於放棄狀態(Cancelled)

      // 那就繼續往前遍歷,直到當前節點的前繼節點的ws值為0或負數

      // 此處代碼很關鍵,節點往前移動就是通過這里來實現的,直到節點的前繼節點滿足

      // if (p == head && tryAcquire(arg))條件,acquireQueued方法才能夠跳出自旋過程

      if (ws > 0) {

           do {

                 node.prev = pred = pred.prev;

           } while (pred.waitStatus > 0);

                pred.next = node;

           } else {

              // 將前繼節點的ws值設置為Node.SIGNAL,以保證下次自旋時,shouldParkAfterFailedAcquire直接返回true

              compareAndSetWaitStatus(pred, ws, Node.SIGNAL);

         }

          return false;

    }

    parkAndCheckInterrupt()函數則簡單很多,主要調用LockSupport類的park()方法阻塞當前線程,並返回線程是否被中斷過。

    private final boolean parkAndCheckInterrupt() {

             LockSupport.park(this);

             return Thread.interrupted();

     }

     至此,獨占模式下,線程獲取資源acquire的代碼就跟完了,總結一下過程:

     第一步:首先線程通過tryAcquire(arg)嘗試獲取共享資源,若獲取成功則直接返回,若不成功,

                    則將該線程以獨占模式添加到等待隊列尾部,

                    tryAcquire(arg)由繼承AQS的自定義同步器來具體實現;

      第二步:當前線程加入等待隊列后,會通過acquireQueued方法基於CAS自旋不斷嘗試獲取資源,直至獲取到資源;

      第三步:若在自旋過程中,線程被中斷過,acquireQueued方法會標記此次中斷,並返回true。

      第四步:若acquireQueued方法獲取到資源后,返回true,則執行線程自我中斷操作selfInterrupt()。

      static void selfInterrupt() {

                         Thread.currentThread().interrupt();

      }

 

2.釋放資源(獨占模式)

    講完獲取資源,對應的講一下AQS的釋放資源過程,其入口函數為:

     public final boolean release(int arg) {

           if (tryRelease(arg)) {

             // 獲取到等待隊列的頭結點h

            Node h = head;

            // 若頭結點不為空且其ws值非0,則喚醒h的后繼節點

           if (h != null && h.waitStatus != 0)

               unparkSuccessor(h);

               return true;

             }

            return false;

         }

     總結:邏輯並不復雜,通過tryRelease(arg)來釋放資源,和tryAcquire類似,tryRelease也是有繼承AQS的自定義同步器來具體實現

     函數:

        tryRelease(int) --該方法嘗試釋放指定量的資源。

        protected boolean tryRelease(int arg) {

                throw new UnsupportedOperationException();

         }

 

學習來源:https://www.jianshu.com/p/0f876ead2846


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM