java中線程池的生命週期與線程中斷

線程池生命週期包括:html

  • RUNNING:接收新的任務並處理隊列中的任務
  • SHUTDOWN:不接收新的任務,可是處理隊列中的任務
  • STOP:不接收新的任務,不處理隊列中的任務,同時中斷處理中的任務
  • TIDYING:全部的任務處理完成,有效的線程數是0
  • TERMINATED:terminated()方法執行完畢
轉換成TIDYING狀態的線程會運行terminated方法。執行完terminated()方法以後,全部等待在awaitTermination()就會返回。

轉換過程爲:java

圖片描述

線程池是空的即有效線程數是0

取消

若是代碼可以在某個操做正常徹底以前置入「完成」狀態,那麼這個操做就稱爲可取消的。java中提供了協做式機制,使請求取消的任務和代碼遵循一種協商好的協議。linux

線程中斷

線程中斷就是一種協做機制。它並不會真正的中斷一個正在運行的線程,而只是發出中斷請求,而後由線程在下一個合適的時刻中斷本身。
Thread中的中斷方法包括安全

interrupt

public void interrupt() {
    if (this != Thread.currentThread())
        checkAccess();//非當前線程有可能拋出SecurityException

    synchronized (blockerLock) {
        //用於執行可終端的IO操做對應的方法
        Interruptible b = blocker;
        if (b != null) {
            //僅設置終端標記
            interrupt0(); 
            //執行實現了Interruptible接口的防範
            b.interrupt(this);
            return;
        }
    }
    //僅設置終端標記
    interrupt0();
}

調用它根據線程的不一樣場景,有不一樣的結果oracle

  1. 若是線程阻塞的是一個能夠中斷的channel,那麼channel會被關閉,同時線程會收到java.nio.channels.ClosedByInterruptException,而且會設置中斷標誌異步

    //AbstractInterruptibleChannel中:
    protected final void begin() {
            if (interruptor == null) {
                interruptor = new Interruptible() {
                        public void interrupt(Thread target) {
                            synchronized (closeLock) {
                                if (!open)
                                    return;
                                open = false;
                                interrupted = target;
                                try {
                                //關閉channel
                                AbstractInterruptibleChannel.this.implCloseChannel();
                                } catch (IOException x) { }
                            }
                        }};
            }
            blockedOn(interruptor);
            Thread me = Thread.currentThread();
            if (me.isInterrupted())
                interruptor.interrupt(me);
        }
  2. 若是線程阻塞在Selector,執行它的 wakeup方法,於是selector會當即返回,同時會設置中斷標誌ide

    //AbstractSelector中:
    protected final void begin() {
        if (interruptor == null) {
            interruptor = new Interruptible() {
                    public void interrupt(Thread ignore) {
                        //執行wakeup,Selector當即返回
                        AbstractSelector.this.wakeup();
                    }};
        }
        AbstractInterruptibleChannel.blockedOn(interruptor);
        Thread me = Thread.currentThread();
        if (me.isInterrupted())
            interruptor.interrupt(me);
    }
  3. 若是線程阻塞在wait/join/sleep,線程的中斷標誌會被清除,並拋出InterruptedException
  4. 非上述三種狀況,僅設置中斷標誌

能夠看出調用interrupt並不意味着當即中止目標線程正在進行的工做,而只是傳遞了請求中斷的消息ui

interrupted

清除當前線程的中斷狀態,並返回以前的值。它實際執行的就是當前線程的isInterrupted(true)this

public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}
假設當前線程是中斷的,此時調用會返回true,若是在下次調用以前沒有中斷,此時調用會返回false

isInterrupted

返回目標線程的中斷狀態,只有線程狀態是中斷纔會返回true,其它時候返回falsespa

public boolean isInterrupted() {
    return isInterrupted(false);
}

能夠看到interrupted和isInterrupted 調用的都是isInterrupted方法,只不過參數不同。它的參數實際表明的是是否要清除中斷標記,爲true也就清除,在java中的定義以下

private native boolean isInterrupted(boolean ClearInterrupted);
參考linux上的實現爲
```
bool os::is_interrupted(Thread* thread, bool clear_interrupted) {
  assert(Thread::current() == thread || Threads_lock->owned_by_self(),
    "possibility of dangling Thread pointer");

  OSThread* osthread = thread->osthread();

  bool interrupted = osthread->interrupted();

  if (interrupted && clear_interrupted) {
    osthread->set_interrupted(false); //若是中斷了,而且要清除中斷標記,就改變終端標記
    // consider thread->_SleepEvent->reset() ... optional optimization
  }

  return interrupted;
}
```

響應中斷 - 處理InterruptedException

通常策略以下

  • 傳遞異常,使當前方法也成爲可中斷的
  • 恢復中斷狀態,使得調用棧中的上層代碼可以對其進行處理

處理不可中斷的阻塞

並不是全部的可阻塞方法或者阻塞機制都能響應中斷,中止線程的方法相似於中斷

  • Java.io中的Socket I/O。InputStream和OutputStream中的read和write等不會響應中斷,能夠關閉底層的套接字拋出SocketException
  • Java.io中的同步I/O。大多數的標準的channel都實現了InterruptibleChannel,它內部通常都是拋出ClosedByInterruptException,並關閉鏈路
  • Selector的異步I/O。阻塞在了Selector.select,經過調用wakeup或者close來提早返回。
  • 獲取某個鎖。因爲線程等待某個內置鎖,它會認爲本身能等到,因此不會處理中斷,經過Lock的lockInterruptibly能夠同時實現等待鎖而且響應中斷
Thread.stop自己是不安全的。中止一個線程會釋放它全部的鎖的監視器,若是有任何一個受這些監視器保護的對象出現了狀態不一致,其它的線程也會以不一致的狀態查看這個對象,其它線程在這個對象上的任何操做都是沒法預料的
爲何廢棄了Thread.stop

關閉

應用程序準備退出時,這些服務所擁有的線程也應該結束。
ExecutorService提供了兩種方法:shutdown和shutdownNow

  • shutdown在執行完隊列中的全部任務以後,才關閉,它並不會接收新的任務
  • shutdownNow則是立馬關閉正在執行的任務,並返回尚未開始的任務
相關文章
相關標籤/搜索