中斷
JAVA中並沒有好的中斷線程的方式,早期引入的Thead.stop()和Thread.resume()容易導致死鎖(參考:http://docs.oracle.com/javase/6/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html),已經不推薦使用。
JAVA線程提供了協作式中斷,何爲協作是中斷,是相對搶佔式中斷而言的,簡單來講就是設置一箇中斷標誌位,不停地檢查這個標誌位的狀態,如果檢查到線程中斷,就中斷線程。JVM線程內部維護着一箇中斷標誌,程序員不能直接操作這個中斷標誌位,只能通過線程的以下幾個方法設置中斷位:
- public void interrupt()
- public static boolean interrupted()
- private native boolean isInterrupted(boolean ClearInterrupted);
- public boolean isInterrupted()
AQS中提供了支持中斷的方法
- private void doAcquireInterruptibly(int arg) throws InterruptedException;
- private void doAcquireSharedInterruptibly(int arg) throws InterruptedException;
- private boolean doAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException;
- if (Thread.interrupted())
- throw new InterruptedException();
- private static void selfInterrupt() {
- Thread.currentThread().interrupt();
- }
超時
AQS與JVM內置鎖的一個不同點在於AQS中提供了超時機制,即線程在等待一定時間後會立即返回。下面以doAcquireNanos爲例來分析:
- private boolean doAcquireNanos(int arg, long nanosTimeout)
- throws InterruptedException {
- long lastTime = System.nanoTime();
- final Node node = addWaiter(Node.EXCLUSIVE);
- boolean failed = true;
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head && tryAcquire(arg)) {
- setHead(node);
- p.next = null; // help GC
- failed = false;
- return true;
- }
- if (nanosTimeout <= 0)
- return false;
- if (shouldParkAfterFailedAcquire(p, node) &&
- nanosTimeout > spinForTimeoutThreshold)
- LockSupport.parkNanos(this, nanosTimeout);
- long now = System.nanoTime();
- nanosTimeout -= now - lastTime;
- lastTime = now;
- if (Thread.interrupted())
- throw new InterruptedException();
- }
- } finally {
- if (failed)
- cancelAcquire(node);
- }
- }
2、如果剩餘時間>0,就用總時間減去一次循環耗費的時間,繼續阻塞;
3、如果在這期間線程被中斷,就拋出中斷異常,如果有其他異常產生,就取消這次獲取。
取消
取消獲取的邏輯比較複雜,下面來分析一下:
- private void cancelAcquire(Node node) {
- // Ignore if node doesn't exist
- if (node == null)
- return;
- node.thread = null;
- // Skip cancelled predecessors
- Node pred = node.prev;
- while (pred.waitStatus > 0)
- node.prev = pred = pred.prev;
- // predNext is the apparent node to unsplice. CASes below will
- // fail if not, in which case, we lost race vs another cancel
- // or signal, so no further action is necessary.
- Node predNext = pred.next;
- // Can use unconditional write instead of CAS here.
- // After this atomic step, other Nodes can skip past us.
- // Before, we are free of interference from other threads.
- node.waitStatus = Node.CANCELLED;
- // If we are the tail, remove ourselves.
- if (node == tail && compareAndSetTail(node, pred)) {
- compareAndSetNext(pred, predNext, null);
- } else {
- // If successor needs signal, try to set pred's next-link
- // so it will get one. Otherwise wake it up to propagate.
- int ws;
- if (pred != head &&
- ((ws = pred.waitStatus) == Node.SIGNAL ||
- (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
- pred.thread != null) {
- Node next = node.next;
- if (next != null && next.waitStatus <= 0)
- compareAndSetNext(pred, predNext, next);
- } else {
- unparkSuccessor(node);
- }
- node.next = node; // help GC
- }
- }
2、將當前結點設置爲取消狀態;
3、如果當前結點是隊尾結點,則將當前結點從隊尾移除;否則執行4;
4、找到當前結點的繼任結點,前趨的next指針指向繼任結點(pred->next=current->next);
5、當前結點的next指針指向自己,前面提到這一方面爲了回收,一方面爲了使isOnSyncQueue方法簡單。
其他
AQS還提供了一些線程監控的方法:
- //獲取哪些線程在等待
- protected final Collection<Thread> getWaitingThreads();
- //獲取等待隊列的長度
- protected final int getWaitQueueLength();
- //是否有線程在等待
- protected final boolean hasWaiters()
- //是否擁有同步器
- final boolean isOwnedBy(AbstractQueuedSynchronizer sync)
- //是否在同步隊列中
- final boolean isOnSyncQueue(Node node)
- //支持共享模式的線程
- public final Collection<Thread> getSharedQueuedThreads()
- //支持獨佔模式的線程
- public final Collection<Thread> getExclusiveQueuedThreads();