自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

線程不是你想中斷就能中斷

開發(fā) 前端
對于 Java 而言,最正確的停止線程的方式是使用 interrupt。但 interrupt僅僅起到通知被停止線程的作用。而對于被停止的線程而言,它擁有完全的自主權(quán),它既可以選擇立即停止,也可以選擇一段時間后停止,也可以選擇壓根不停止。
本文轉(zhuǎn)載自微信公眾號「JerryCodes」,作者KyleJerry。轉(zhuǎn)載本文請聯(lián)系JerryCodes公眾號。
  • 為什么不強制停止
  • 如何用 interrupt 停止線程
  • sleep 期間能否感受到中斷
  • 停止線程的方式有幾種
  • 總結(jié)

啟動線程需要調(diào)用 Thread 類的 start() 方法,并在 run() 方法中定義需要執(zhí)行的任務(wù)。啟動一個線程非常簡單,但如果想要正確停止它就沒那么容易了。

對于實現(xiàn)線程的幾種方式,可見我的上一篇文章

實現(xiàn)線程本質(zhì)上只有一種方式

為什么不強制停止

對于 Java 而言,最正確的停止線程的方式是使用 interrupt。但 interrupt僅僅起到通知被停止線程的作用。而對于被停止的線程而言,它擁有完全的自主權(quán),它既可以選擇立即停止,也可以選擇一段時間后停止,也可以選擇壓根不停止。

為什么 Java 不提供強制停止線程的能力呢?事實上,Java 希望程序間能夠相互通知、相互協(xié)作地管理線程,因為如果不了解對方正在做的工作,貿(mào)然強制停止線程就可能會造成一些安全的問題。

比如:線程正在寫入一個文件,這時收到終止信號,它就需要根據(jù)自身業(yè)務(wù)判斷,是選擇立即停止,還是將整個文件寫入成功后停止。如果選擇立即停止就可能造成數(shù)據(jù)不完整,不管是中斷命令發(fā)起者,還是接收者都不希望數(shù)據(jù)出現(xiàn)問題。

如何用 interrupt 停止線程

  1. while (!Thread.currentThread().isInterrupted()  
  2. && more work to do) { 
  3.     do more work 

我們一旦調(diào)用某個線程的 interrupt() 之后,這個線程的中斷標(biāo)記位就會被設(shè)置成true。每個線程都有這樣的標(biāo)記位,當(dāng)線程執(zhí)行時,應(yīng)該定期檢查這個標(biāo)記位,如果標(biāo)記位被設(shè)置成 true,就說明有程序想終止該線程。

回到源碼,可以看到在 while 循環(huán)體判斷語句中,首先通過

Thread.currentThread().isInterrupt()

判斷線程是否被中斷,隨后檢查是否還有工作要做。&& 邏輯表示只有當(dāng)兩個判斷條件同時滿足的情況下,才會去執(zhí)行下面的工作。

  1. public class StopThread implements Runnable { 
  2.   
  3.     @Override 
  4.     public void run() { 
  5.         int count = 0; 
  6.         while (!Thread.currentThread().isInterrupted() && count < 1000) { 
  7.             System.out.println("count = " + count++); 
  8.         } 
  9.     } 
  10.   
  11.     public static void main(String[] args) throws InterruptedException { 
  12.         Thread thread = new Thread(new StopThread()); 
  13.         thread.start(); 
  14.         Thread.sleep(5); 
  15.         thread.interrupt(); 
  16.     } 

在 StopThread 類的 run() 方法中,首先判斷線程是否被中斷,然后判斷 count 值是否小于 1000。

這個線程的工作內(nèi)容很簡單,就是打印 0~999 的數(shù)字,每打印一個數(shù)字 count 值加 1,可以看到,線程會在每次循環(huán)開始之前,檢查是否被中斷了。接下來在 main 函數(shù)中會啟動該線程,然后休眠 5 毫秒后立刻中斷線程,該線程會檢測到中斷信號,于是在還沒打印完1000個數(shù)的時候就會停下來,這種就屬于通過 interrupt 正確停止線程的情況。

sleep 期間能否感受到中斷

先說結(jié)論,可以。

  1. public class StopDuringSleep { 
  2.   
  3.     public static void main(String[] args) throws InterruptedException { 
  4.         Runnable runnable = () -> { 
  5.             int num = 0; 
  6.             try { 
  7.                 while (!Thread.currentThread().isInterrupted() && num <= 1000) { 
  8.                     System.out.println(num); 
  9.                     num++; 
  10.                     Thread.sleep(1000000); 
  11.                 } 
  12.             } catch (InterruptedException e) { 
  13.                 e.printStackTrace(); 
  14.             } 
  15.         }; 
  16.         Thread thread = new Thread(runnable); 
  17.         thread.start(); 
  18.         Thread.sleep(5); 
  19.         thread.interrupt(); 
  20.     } 

運行后的結(jié)果你猜怎么著,程序會拋出異常

如果 sleep、wait 等可以讓線程進(jìn)入阻塞的方法使線程休眠了,而處于休眠中的線程被中斷,那么線程是可以感受到中斷信號的,并且會拋出一個 InterruptedException 異常,同時清除中斷信號,將中斷標(biāo)記位設(shè)置成 false。這樣一來就不用擔(dān)心長時間休眠中線程感受不到中斷了,因為即便線程還在休眠,仍然能夠響應(yīng)中斷通知,并拋出異常。

 

但是這樣只能相應(yīng)一次中斷信號了,怎么辦?我的業(yè)務(wù)還沒有完成收尾,怎么辦?

合理利用好 try/catch

我們在實際開發(fā)中不能盲目吞掉中斷,如果不在方法簽名中聲明,也不在 catch 語句塊中再次恢復(fù)中斷,而是在 catch 中不作處理,我們稱這種行為是“屏蔽了中斷請求”。如果我們盲目地屏蔽了中斷請求,會導(dǎo)致中斷信號被完全忽略,最終導(dǎo)致線程無法正確停止。

  1. try { 
  2.         Thread.sleep(2000); 
  3.     } catch (InterruptedException e) { 
  4. //        此處處理中斷異常請求,業(yè)務(wù)收尾 
  5.     } 

停止線程的方式有幾種

  1. void shutdown; 
  2. boolean isShutdown; 
  3. boolean isTerminated; 
  4. boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; 
  5. List<Runnable> shutdownNow; 

下面我們就對這些方法逐一展開。

shutdown()

調(diào)用 shutdown() 方法之后線程池并不是立刻就被關(guān)閉,因為這時線程池中可能還有很多任務(wù)正在被執(zhí)行,或是任務(wù)隊列中有大量正在等待被執(zhí)行的任務(wù),調(diào)用 shutdown() 方法后線程池會在執(zhí)行完正在執(zhí)行的任務(wù)和隊列中等待的任務(wù)后才徹底關(guān)閉。但這并不代表 shutdown() 操作是沒有任何效果的,調(diào)用 shutdown() 方法后如果還有新的任務(wù)被提交,線程池則會根據(jù)拒絕策略直接拒絕后續(xù)新提交的任務(wù)。

isShutdown()

它可以返回 true 或者 false 來判斷線程池是否已經(jīng)開始了關(guān)閉工作,也就是是否執(zhí)行了 shutdown 或者 shutdownNow 方法。這里需要注意,如果調(diào)用 isShutdown() 方法的返回的結(jié)果為 true 并不代表線程池此時已經(jīng)徹底關(guān)閉了,這僅僅代表線程池開始了關(guān)閉的流程,也就是說,此時可能線程池中依然有線程在執(zhí)行任務(wù),隊列里也可能有等待被執(zhí)行的任務(wù)。

isTerminated()

這個方法可以檢測線程池是否真正“終結(jié)”了,這不僅代表線程池已關(guān)閉,同時代表線程池中的所有任務(wù)都已經(jīng)都執(zhí)行完畢了,因為我們剛才說過,調(diào)用 shutdown 方法之后,線程池會繼續(xù)執(zhí)行里面未完成的任務(wù),不僅包括線程正在執(zhí)行的任務(wù),還包括正在任務(wù)隊列中等待的任務(wù)。比如此時已經(jīng)調(diào)用了 shutdown 方法,但是有一個線程依然在執(zhí)行任務(wù),那么此時調(diào)用 isShutdown 方法返回的是 true ,而調(diào)用 isTerminated 方法返回的便是 false ,因為線程池中還有任務(wù)正在在被執(zhí)行,線程池并沒有真正“終結(jié)”。直到所有任務(wù)都執(zhí)行完畢了,調(diào)用 isTerminated() 方法才會返回 true,這表示線程池已關(guān)閉并且線程池內(nèi)部是空的,所有剩余的任務(wù)都執(zhí)行完畢了。

awaitTermination()

第四個方法叫作 awaitTermination(),它本身并不是用來關(guān)閉線程池的,而是主要用來判斷線程池狀態(tài)的。比如我們給 awaitTermination 方法傳入的參數(shù)是 10 秒,那么它就會陷入 10 秒鐘的等待,直到發(fā)生以下三種情況之一:

  • 等待期間(包括進(jìn)入等待狀態(tài)之前)線程池已關(guān)閉并且所有已提交的任務(wù)(包括正在執(zhí)行的和隊列中等待的都執(zhí)行完畢,相當(dāng)于線程池已經(jīng)“終結(jié)”了,方法便會返回true
  • 等待超時時間到后,第一種線程池“終結(jié)”的情況始終未發(fā)生,方法返回 false
  • 等待期間線程被中斷,方法會拋出 Interruptedexception異常

等待期間(包括進(jìn)入等待狀態(tài)之前)線程池已關(guān)閉并且所有已提交的任務(wù)(包括正在執(zhí)行的和隊列中等待的)都執(zhí)行完畢,相當(dāng)于線程池已經(jīng)“終結(jié)”了,方法便會返回 true;

等待超時時間到后,第一種線程池“終結(jié)”的情況始終未發(fā)生,方法返回 false;等待期間線程被中斷,方法會拋出 InterruptedException 異常。

shutdownNow()

最后一個方法是 shutdownNow(),也是 5 種方法里功能最強大的,它與第一種 shutdown 方法不同之處在于名字中多了一個單詞 Now,也就是表示立刻關(guān)閉的意思。在執(zhí)行 shutdownNow 方法之后,首先會給所有線程池中的線程發(fā)送 interrupt 中斷信號,嘗試中斷這些任務(wù)的執(zhí)行,然后會將任務(wù)隊列中正在等待的所有任務(wù)轉(zhuǎn)移到一個 List 中并返回,我們可以根據(jù)返回的任務(wù) List 來進(jìn)行一些補救的操作,例如記錄在案并在后期重試。

  1. public List<Runnable> shutdownNow() {  
  2.     List<Runnable> tasks; 
  3.     final ReentrantLock mainLock = this.mainLock; 
  4.     mainLock.lock(); 
  5.  
  6.     try {  
  7.         checkShutdownAccess(); 
  8.         advanceRunState(STOP); 
  9.         interruptWorkers(); 
  10.         tasks = drainQueue(); 
  11.     } finally {  
  12.         mainLock.unlock(); 
  13.     }  
  14.   
  15.     tryTerminate(); 
  16.     return tasks; 
  17.  } 

源碼中有一行 interruptWorkers() 代碼,這行代碼會讓每一個已經(jīng)啟動的線程都中斷,這樣線程就可以在執(zhí)行任務(wù)期間檢測到中斷信號并進(jìn)行相應(yīng)的處理,提前結(jié)束任務(wù)。這里需要注意的是,由于 Java 中不推薦強行停止線程的機制的限制,即便我們調(diào)用了 shutdownNow 方法,如果被中斷的線程對于中斷信號不理不睬,那么依然有可能導(dǎo)致任務(wù)不會停止。

總結(jié)

中斷和關(guān)閉線程的方式五花八門,看起來很相似,其實里頭大有門道。處理不好,可是會導(dǎo)致程序崩潰的。

責(zé)任編輯:武曉燕 來源: JerryCodes
相關(guān)推薦

2023-07-07 07:44:41

線程中斷LockSuppor

2009-04-23 09:07:03

JAVA終端線程

2015-08-03 09:54:26

Java線程Java

2017-06-04 16:24:27

線程線程池中斷

2025-01-14 10:09:43

硬中斷Linux系統(tǒng)

2021-12-10 08:45:45

Linux GIC Linux 系統(tǒng)

2021-12-14 08:51:23

Linux 中斷子系統(tǒng)Linux 系統(tǒng)

2016-10-18 14:16:57

2021-08-06 22:43:54

中斷架構(gòu)傳遞

2022-09-28 12:23:36

Promise代碼

2011-09-08 09:24:17

Google Docs服務(wù)中斷

2024-09-26 10:51:51

2021-03-24 17:18:41

鴻蒙HarmonyOS應(yīng)用開發(fā)

2021-03-30 15:30:44

鴻蒙HarmonyOS應(yīng)用開發(fā)

2022-08-11 14:19:40

云計算云中斷安全

2021-12-08 08:41:31

Linux 中斷子系統(tǒng)Linux 系統(tǒng)

2013-01-09 09:38:09

Github

2011-09-08 11:17:33

Google中斷故障文檔服務(wù)

2022-05-24 10:19:15

網(wǎng)絡(luò)中斷網(wǎng)絡(luò)

2010-09-25 14:51:40

無線網(wǎng)絡(luò)故障
點贊
收藏

51CTO技術(shù)棧公眾號