摘要:線程中斷線程中斷就是一種協作機制。它并不會真正的中斷一個正在運行的線程,而只是發出中斷請求,然后由線程在下一個合適的時刻中斷自己。
線程池生命周期包括:
RUNNING:接收新的任務并處理隊列中的任務
SHUTDOWN:不接收新的任務,但是處理隊列中的任務
STOP:不接收新的任務,不處理隊列中的任務,同時中斷處理中的任務
TIDYING:所有的任務處理完成,有效的線程數是0
TERMINATED:terminated()方法執行完畢
轉換成TIDYING狀態的線程會運行terminated方法。執行完terminated()方法之后,所有等待在awaitTermination()就會返回。
轉換過程為:
線程池是空的即有效線程數是0取消
如果代碼能夠在某個操作正常完全之前置入“完成”狀態,那么這個操作就稱為可取消的。java中提供了協作式機制,使請求取消的任務和代碼遵循一種協商好的協議。
線程中斷線程中斷就是一種協作機制。它并不會真正的中斷一個正在運行的線程,而只是發出中斷請求,然后由線程在下一個合適的時刻中斷自己。
Thread中的中斷方法包括
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(); }
調用它根據線程的不同場景,有不同的結果
如果線程阻塞的是一個可以中斷的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); }
如果線程阻塞在Selector,執行它的 wakeup方法,因而selector會立即返回,同時會設置中斷標志
//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); }
如果線程阻塞在wait/join/sleep,線程的中斷標志會被清除,并拋出InterruptedException
非上述三種情況,僅設置中斷標志
可以看出調用interrupt并不意味著立即停止目標線程正在進行的工作,而只是傳遞了請求中斷的消息
interrupted清除當前線程的中斷狀態,并返回之前的值。它實際執行的就是當前線程的isInterrupted(true)
public static boolean interrupted() { return currentThread().isInterrupted(true); }
假設當前線程是中斷的,此時調用會返回true,如果在下次調用之前沒有中斷,此時調用會返回falseisInterrupted
返回目標線程的中斷狀態,只有線程狀態是中斷才會返回true,其它時候返回false
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則是立馬關閉正在執行的任務,并返回還沒有開始的任務
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72392.html
摘要:在領域,實現并發程序的主要手段就是多線程。可運行狀態指的是線程可以分配執行。當等待的事件出現了,線程就會從休眠狀態轉換到可運行狀態。導出線程棧,分析線程狀態是診斷并發問題的一個重要工具。 在 Java 領域,實現并發程序的主要手段就是多線程。線程是操作系統里的一個概念,雖然各種不同的開發語言如 Java、C# 等都對其進行了封裝,但原理和思路都是相同都。Java 語言里的線程本質上就是...
摘要:所以,在時執行也是為了保證線程池在狀態下必須要有一個線程來執行任務。 這篇文章對ThreadPoolExecutor創建的線程池如何操作線程的生命周期通過源碼的方式進行詳細解析。通過對execute方法、addWorker方法、Worker類、runWorker方法、getTask方法、processWorkerExit從源碼角度詳細闡述,文末有彩蛋。 exexcte方法 public...
摘要:方法可以將當前線程放入等待集合中,并釋放當前線程持有的鎖。此后,該線程不會接收到的調度,并進入休眠狀態。該線程會喚醒,并嘗試恢復之前的狀態。 并發 最近重新復習了一邊并發的知識,發現自己之前對于并發的了解只是皮毛。這里總結以下Java并發需要掌握的點。 使用并發的一個重要原因是提高執行效率。由于I/O等情況阻塞,單個任務并不能充分利用CPU時間。所以在單處理器的機器上也應該使用并發。為...
摘要:最后,我們會通過對源代碼的剖析深入了解線程池的運行過程和具體設計,真正達到知其然而知其所以然的水平。創建線程池既然線程池是一個類,那么最直接的使用方法一定是一個類的對象,例如。單線程線程池單線程線程 我們一般不會選擇直接使用線程類Thread進行多線程編程,而是使用更方便的線程池來進行任務的調度和管理。線程池就像共享單車,我們只要在我們有需要的時候去獲取就可以了。甚至可以說線程池更棒,...
閱讀 2958·2021-11-08 13:20
閱讀 1031·2021-09-22 15:20
閱讀 660·2019-08-30 15:53
閱讀 1964·2019-08-30 15:43
閱讀 1278·2019-08-29 17:21
閱讀 540·2019-08-29 12:15
閱讀 2375·2019-08-28 17:51
閱讀 3142·2019-08-26 13:26