溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何理解線程中斷的問題

發(fā)布時(shí)間:2021-10-20 15:07:12 來源:億速云 閱讀:184 作者:iii 欄目:web開發(fā)

本篇內(nèi)容主要講解“如何理解線程中斷的問題”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“如何理解線程中斷的問題”吧!

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

為什么不強(qiáng)制停止

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

為什么 Java 不提供強(qiáng)制停止線程的能力呢?事實(shí)上,Java  希望程序間能夠相互通知、相互協(xié)作地管理線程,因?yàn)槿绻涣私鈱Ψ秸谧龅墓ぷ?,貿(mào)然強(qiáng)制停止線程就可能會(huì)造成一些安全的問題。

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

如何用 interrupt 停止線程

while (!Thread.currentThread().isInterrupted()  && more work to do) {     do more work }

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

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

Thread.currentThread().isInterrupt()

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

public class StopThread implements Runnable {       @Override     public void run() {         int count = 0;         while (!Thread.currentThread().isInterrupted() && count < 1000) {             System.out.println("count = " + count++);         }     }       public static void main(String[] args) throws InterruptedException {         Thread thread = new Thread(new StopThread());         thread.start();         Thread.sleep(5);         thread.interrupt();     } }

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

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

sleep 期間能否感受到中斷

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

public class StopDuringSleep {       public static void main(String[] args) throws InterruptedException {         Runnable runnable = () -> {             int num = 0;             try {                 while (!Thread.currentThread().isInterrupted() && num <= 1000) {                     System.out.println(num);                     num++;                     Thread.sleep(1000000);                 }             } catch (InterruptedException e) {                 e.printStackTrace();             }         };         Thread thread = new Thread(runnable);         thread.start();         Thread.sleep(5);         thread.interrupt();     } }

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

如何理解線程中斷的問題

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

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

合理利用好 try/catch

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

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

停止線程的方式有幾種

void shutdown; boolean isShutdown; boolean isTerminated; boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; List<Runnable> shutdownNow;

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

shutdown()

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

isShutdown()

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

isTerminated()

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

awaitTermination()

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

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

  • 等待超時(shí)時(shí)間到后,第一種線程池“終結(jié)”的情況始終未發(fā)生,方法返回 false

  • 等待期間線程被中斷,方法會(huì)拋出 Interruptedexception異常

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

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

shutdownNow()

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

public List<Runnable> shutdownNow() {      List<Runnable> tasks;     final ReentrantLock mainLock = this.mainLock;     mainLock.lock();      try {          checkShutdownAccess();         advanceRunState(STOP);         interruptWorkers();         tasks = drainQueue();     } finally {          mainLock.unlock();     }        tryTerminate();     return tasks;  }

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

到此,相信大家對“如何理解線程中斷的問題”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI