java中使用interrupt通知線程停止詳析
前言:
使用 interrupt 來通知線程停止運(yùn)行,而不是強(qiáng)制停止!
普通情況停止線程
public class RightWayStopThreadWithoutSleep implements Runnable { @Override public void run() { int num = 0; while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) { if (num % 10000 == 0) { System.out.println(num + "是1W的倍數(shù)"); } num++; } System.out.println("任務(wù)運(yùn)行結(jié)束!"); } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new RightWayStopThreadWithoutSleep()); thread.start(); // 等待1s Thread.sleep(1000); // 通知停止線程 thread.interrupt(); } }
使用 thread.interrupt() 通知線程停止
但是 線程需要配合:
在 while 中使用 Thread.currentThread().isInterrupted() 檢測(cè)線程當(dāng)前的狀態(tài)
運(yùn)行結(jié)果:
……
……
221730000是1W的倍數(shù)
221740000是1W的倍數(shù)
221750000是1W的倍數(shù)
221760000是1W的倍數(shù)
221770000是1W的倍數(shù)
221780000是1W的倍數(shù)
221790000是1W的倍數(shù)
221800000是1W的倍數(shù)
任務(wù)運(yùn)行結(jié)束!Process finished with exit code 0
在可能被阻塞情況下停止線程
public class RightWayStopThreadWithSleep { public static void main(String[] args) throws InterruptedException { Runnable runnable = () -> { int num = 0; while (num <= 300 && !Thread.currentThread().isInterrupted()) { if (num % 100 == 0) { System.out.println(num + "是100的倍數(shù)"); } num++; } try { // 等個(gè)1秒,模擬阻塞 Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("線程已停止??!"); e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.start(); // 等待時(shí)間要小于上面設(shè)置的1秒,不然線程都運(yùn)行結(jié)束了,才執(zhí)行到下面的thread.interrupt();代碼 Thread.sleep(500); // 通知停止線程 thread.interrupt(); } }
線程在sleep 1秒的過程中,收到interrupt信號(hào)被打斷,
線程正在sleep過程中響應(yīng)中斷的方式就是拋出 InterruptedException 異常
運(yùn)行結(jié)果:
0是100的倍數(shù)
100是100的倍數(shù)
200是100的倍數(shù)
300是100的倍數(shù)
線程已停止!!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at stopthreads.RightWayStopThreadWithSleep.lambda$main$0(RightWayStopThreadWithSleep.java:19)
at java.lang.Thread.run(Thread.java:748)Process finished with exit code 0
在每次迭代后都阻塞的情況下停止線程
public class RightWayStopThreadWithSleepEveryLoop { public static void main(String[] args) throws InterruptedException { Runnable runnable = () -> { int num = 0; try { while (num <= 10000) { if (num % 100 == 0) { System.out.println(num + "是100的倍數(shù)"); } num++; // 每次循環(huán)都要等待10毫秒,模擬阻塞 Thread.sleep(10); } } catch (InterruptedException e) { e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.start(); // 5秒后通知停止線程 Thread.sleep(5000); thread.interrupt(); } }
當(dāng)每次迭代都會(huì)讓線程阻塞一段時(shí)間的時(shí)候,在 while/for 循環(huán)條件判斷時(shí),
是不需要使用 *Thread.currentThread().isInterrupted() *判斷線程是否中斷的
運(yùn)行結(jié)果:
0是100的倍數(shù)
100是100的倍數(shù)
200是100的倍數(shù)
300是100的倍數(shù)
400是100的倍數(shù)
java.lang.InterruptedException: sleep interrupted
如果將上述代碼中的 try/catch 放在 while 循環(huán)內(nèi):
public class RightWayStopThreadWithSleepEveryLoop { public static void main(String[] args) throws InterruptedException { Runnable runnable = () -> { int num = 0; while (num <= 10000) { if (num % 100 == 0) { System.out.println(num + "是100的倍數(shù)"); } num++; try { // 每次循環(huán)都要等待10毫秒,模擬阻塞 Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread thread = new Thread(runnable); thread.start(); // 5秒后通知停止線程 Thread.sleep(5000); thread.interrupt(); } }
運(yùn)行結(jié)果:
0是100的倍數(shù)
100是100的倍數(shù)
200是100的倍數(shù)
300是100的倍數(shù)
400是100的倍數(shù)
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at stopthreads.RightWayStopThreadWithSleepEveryLoop.lambda$main$0(RightWayStopThreadWithSleepEveryLoop.java:18)
at java.lang.Thread.run(Thread.java:748)
500是100的倍數(shù)
600是100的倍數(shù)
700是100的倍數(shù)
……
……
會(huì)發(fā)現(xiàn)雖然拋出了異常,但是程序并沒有停止,還在繼續(xù)輸出,
即使在 while 條件判斷處添加 !Thread.currentThread().isInterrupted() 條件,依然不能停止程序!
原因是
java語言在設(shè)計(jì) sleep() 函數(shù)時(shí),有這樣一個(gè)理念:
就是當(dāng)它一旦響應(yīng)中斷,便會(huì)把 interrupt 標(biāo)記位清除。
也就是說,雖然線程在 sleep 過程中收到了 interrupt 中斷通知,并且也捕獲到了異常、打印了異常信息,
但是由于 sleep 設(shè)計(jì)理念,導(dǎo)致 Thread.currentThread().isInterrupted() 標(biāo)記位會(huì)被清除,
所以才會(huì)導(dǎo)致程序不能退出。
這里如果要停止線程,只需要在 catch 內(nèi) 再調(diào)用一次 interrupt(); 方法
try { // 每次循環(huán)都要等待10毫秒,模擬阻塞 Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); }
所以說,不要以為調(diào)用了 interrupt() 方法,線程就一定會(huì)停止。
兩種停止線程最佳方法
1. 捕獲了 InterruptedException 之后的優(yōu)先選擇:在方法簽名中拋出異常
public class RightWayStopThreadInProd implements Runnable { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new RightWayStopThreadInProd()); thread.start(); Thread.sleep(1000); thread.interrupt(); } @Override public void run() { while (true) { System.out.println("go..."); try { throwInMethod(); } catch (InterruptedException e) { // 捕獲異常,進(jìn)行保存日志、停止程序等操作 System.out.println("stop"); e.printStackTrace(); } } } /** * 如果方法內(nèi)要拋出異常,最好是將異常拋出去,由頂層的調(diào)用方去處理,而不是try/catch * 這樣調(diào)用方才能捕獲異常并作出其它操作 * @throws InterruptedException */ private void throwInMethod() throws InterruptedException { Thread.sleep(2000); } }
如果方法內(nèi)要拋出異常,最好是將異常拋出去,由頂層的調(diào)用方去處理,而不是 try/catch
這樣調(diào)用方才能捕獲異常并做出其它操作。
2. 在 catch 中調(diào)用 Thread.currentThread().interrupt(); 來恢復(fù)設(shè)置中斷狀態(tài)
public class RightWayStopThreadInProd2 implements Runnable { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new RightWayStopThreadInProd2()); thread.start(); Thread.sleep(1000); thread.interrupt(); } @Override public void run() { while (true) { if (Thread.currentThread().isInterrupted()) { System.out.println("程序運(yùn)行結(jié)束"); break; } reInterrupt(); } } private void reInterrupt() { try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); } } }
這里的 if (Thread.currentThread().isInterrupted()) 判斷,就是要你的代碼有響應(yīng)中斷的能力。
總結(jié)
- 調(diào)用 interrupt 方法不一定會(huì)中斷線程
- 通知線程停止,線程不會(huì)立即停止,而是會(huì)在合適的時(shí)候停止
- 代碼要有響應(yīng)中斷的能力
到此這篇關(guān)于java中使用interrupt通知線程停止詳析的文章就介紹到這了,更多相關(guān)java interrupt 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring配置文件解析之BeanDefinitionParserDelegate詳解
這篇文章主要介紹了Spring配置文件解析之BeanDefinitionParserDelegate詳解,對(duì)于Spring的配置文件的解析處理操作是在BeanDefinitionParserDelegate中進(jìn)行處理操作,接下來我們簡(jiǎn)單介紹一下BeanDefinitionParserDelegate所做的處理操作,需要的朋友可以參考下2024-02-02springboot打包部署到linux服務(wù)器的方法
這篇文章主要介紹了springboot打包部署到linux服務(wù)器的方法,通過實(shí)例代碼相結(jié)合的形式給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-06-06完美解決idea沒有tomcat server選項(xiàng)的問題
這篇文章主要介紹了完美解決idea沒有tomcat server選項(xiàng)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-01-01Java創(chuàng)建非阻塞的HTTP服務(wù)器的實(shí)現(xiàn)
本文主要介紹了Java創(chuàng)建非阻塞的HTTP服務(wù)器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-04-04Java怎么獲取當(dāng)前時(shí)間、計(jì)算程序運(yùn)行時(shí)間源碼詳解(超詳細(xì)!)
有的時(shí)候,我們需要查看某一段代碼的性能如何,最為簡(jiǎn)單的方式,可以通過計(jì)算該段代碼執(zhí)行的耗時(shí),來進(jìn)行簡(jiǎn)單的判斷,這篇文章主要給大家介紹了關(guān)于Java怎么獲取當(dāng)前時(shí)間、計(jì)算程序運(yùn)行時(shí)間的相關(guān)資料,需要的朋友可以參考下2024-07-07使用Spring?Retry實(shí)現(xiàn)業(yè)務(wù)異常重試
在系統(tǒng)中經(jīng)常遇到業(yè)務(wù)重試的邏輯,比如三方接口調(diào)用,timeout重試三遍,異常處理重試的兜底邏輯等,本文給大家介紹一下如何使用Spring?Retry優(yōu)雅的實(shí)現(xiàn)業(yè)務(wù)異常重試,需要的朋友可以參考下2024-01-01