AQS源碼分析(超時、中斷與其他)

中斷

JAVA中並沒有好的中斷線程的方式,早期引入的Thead.stop()和Thread.resume()容易導致死鎖(參考:http://docs.oracle.com/javase/6/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html),已經不推薦使用。

JAVA線程提供了協作式中斷,何爲協作是中斷,是相對搶佔式中斷而言的,簡單來講就是設置一箇中斷標誌位,不停地檢查這個標誌位的狀態,如果檢查到線程中斷,就中斷線程。JVM線程內部維護着一箇中斷標誌,程序員不能直接操作這個中斷標誌位,只能通過線程的以下幾個方法設置中斷位:

[html] view plain copy
 print?
  1. public void interrupt()  
  2. public static boolean interrupted()   
  3. private native boolean isInterrupted(boolean ClearInterrupted);  
  4. public boolean isInterrupted()  

AQS中提供了支持中斷的方法

[html] view plain copy
 print?
  1. private void doAcquireInterruptibly(int arg) throws InterruptedException;  
  2. private void doAcquireSharedInterruptibly(int arg) throws InterruptedException;   
  3. private boolean doAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException;  
這幾個方法都拋出了InterruptedException,這些方法都會先出處中斷異常,處理的代碼如下:

[html] view plain copy
 print?
  1. if (Thread.interrupted())  
  2.     throw new InterruptedException();  
我們還看到有些方法並沒有申請拋出InterruptedException,當它被中斷時,設置了線程的中斷位。

[html] view plain copy
 print?
  1. private static void selfInterrupt() {  
  2.     Thread.currentThread().interrupt();  
  3. }  

超時

AQS與JVM內置鎖的一個不同點在於AQS中提供了超時機制,即線程在等待一定時間後會立即返回。下面以doAcquireNanos爲例來分析:
[java] view plain copy
 print?
  1. private boolean doAcquireNanos(int arg, long nanosTimeout)  
  2.     throws InterruptedException {  
  3.     long lastTime = System.nanoTime();  
  4.     final Node node = addWaiter(Node.EXCLUSIVE);  
  5.     boolean failed = true;  
  6.     try {  
  7.         for (;;) {  
  8.             final Node p = node.predecessor();  
  9.             if (p == head && tryAcquire(arg)) {  
  10.                 setHead(node);  
  11.                 p.next = null// help GC  
  12.                 failed = false;  
  13.                 return true;  
  14.             }  
  15.             if (nanosTimeout <= 0)  
  16.                 return false;  
  17.             if (shouldParkAfterFailedAcquire(p, node) &&  
  18.                 nanosTimeout > spinForTimeoutThreshold)  
  19.                 LockSupport.parkNanos(this, nanosTimeout);  
  20.             long now = System.nanoTime();  
  21.             nanosTimeout -= now - lastTime;  
  22.             lastTime = now;  
  23.             if (Thread.interrupted())  
  24.                 throw new InterruptedException();  
  25.         }  
  26.     } finally {  
  27.         if (failed)  
  28.             cancelAcquire(node);  
  29.     }  
  30. }  
1、首先取得當前系統時間,在循環等待的過程中,如果剩餘時間<=0立即返回;
2、如果剩餘時間>0,就用總時間減去一次循環耗費的時間,繼續阻塞;
3、如果在這期間線程被中斷,就拋出中斷異常,如果有其他異常產生,就取消這次獲取。

取消

取消獲取的邏輯比較複雜,下面來分析一下:
[java] view plain copy
 print?
  1. private void cancelAcquire(Node node) {  
  2.       // Ignore if node doesn't exist  
  3.       if (node == null)  
  4.           return;  
  5.   
  6.       node.thread = null;  
  7.   
  8.       // Skip cancelled predecessors  
  9.       Node pred = node.prev;  
  10.       while (pred.waitStatus > 0)  
  11.           node.prev = pred = pred.prev;  
  12.   
  13.       // predNext is the apparent node to unsplice. CASes below will  
  14.       // fail if not, in which case, we lost race vs another cancel  
  15.       // or signal, so no further action is necessary.  
  16.       Node predNext = pred.next;  
  17.   
  18.       // Can use unconditional write instead of CAS here.  
  19.       // After this atomic step, other Nodes can skip past us.  
  20.       // Before, we are free of interference from other threads.  
  21.       node.waitStatus = Node.CANCELLED;  
  22.   
  23.       // If we are the tail, remove ourselves.  
  24.       if (node == tail && compareAndSetTail(node, pred)) {  
  25.           compareAndSetNext(pred, predNext, null);  
  26.       } else {  
  27.           // If successor needs signal, try to set pred's next-link  
  28.           // so it will get one. Otherwise wake it up to propagate.  
  29.           int ws;  
  30.           if (pred != head &&  
  31.               ((ws = pred.waitStatus) == Node.SIGNAL ||  
  32.                (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&  
  33.               pred.thread != null) {  
  34.               Node next = node.next;  
  35.               if (next != null && next.waitStatus <= 0)  
  36.                   compareAndSetNext(pred, predNext, next);  
  37.           } else {  
  38.               unparkSuccessor(node);  
  39.           }  
  40.   
  41.           node.next = node; // help GC  
  42.       }  
  43.   }  
1、首先取得當前結點的前趨結點,如果前趨結點也被取消直接跳過,繼續向前找非取消的結點;
2、將當前結點設置爲取消狀態;
3、如果當前結點是隊尾結點,則將當前結點從隊尾移除;否則執行4;
4、找到當前結點的繼任結點,前趨的next指針指向繼任結點(pred->next=current->next);
5、當前結點的next指針指向自己,前面提到這一方面爲了回收,一方面爲了使isOnSyncQueue方法簡單。

其他

AQS還提供了一些線程監控的方法:
[java] view plain copy
 print?
  1. //獲取哪些線程在等待  
  2. protected final Collection<Thread> getWaitingThreads();   
  3. //獲取等待隊列的長度  
  4. protected final int getWaitQueueLength();   
  5. //是否有線程在等待  
  6. protected final boolean hasWaiters()  
  7. //是否擁有同步器  
  8. final boolean isOwnedBy(AbstractQueuedSynchronizer sync)  
  9. //是否在同步隊列中  
  10. final boolean isOnSyncQueue(Node node)  
  11. //支持共享模式的線程  
  12. public final Collection<Thread> getSharedQueuedThreads()  
  13. //支持獨佔模式的線程  
  14. public final Collection<Thread> getExclusiveQueuedThreads();  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章