Java線程的停止實(shí)現(xiàn)原理詳解
線程停止的原理
使用interrupt來通知,而不是強(qiáng)制
java提供了interrrupt讓一個(gè)線程來通知另一個(gè)線程停止
如果想中斷一個(gè)線程,但是那個(gè)線程不想去中斷,那就無能為力,我們沒有強(qiáng)制去中斷線程的手段,因?yàn)榫€程停止前需要做一定的收尾工作
所以正確停止線程,是如何用interrupt來通知那個(gè)線程,以及被停止的線程如何進(jìn)行配合
如何正確停止線程
在普通情況下停止線程
代碼展示
- 調(diào)用interrupt沒有作用
- 下面這段代碼,執(zhí)行interrupt之后,線程并沒有被中斷
- 因?yàn)楸粓?zhí)行的線程并沒有相應(yīng)中斷的方式
public class stopThreadWithoutSleep implements Runnable{ public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new stopThreadWithoutSleep()); thread.start(); Thread.sleep(1000); thread.interrupt(); } @Override public void run() { int num = 0; while(num <= Integer.MAX_VALUE / 2) { if (num % 10000 == 0) { System.out.println(num + "是10000的倍數(shù)"); } num++; } System.out.println("結(jié)束"); } } /* 由于太長(zhǎng),只展示結(jié)尾部分的結(jié)果 1073710000是10000的倍數(shù) 1073720000是10000的倍數(shù) 1073730000是10000的倍數(shù) 1073740000是10000的倍數(shù) 結(jié)束 * */
- 被執(zhí)行線程加上相應(yīng)中斷的操作之后
- 結(jié)果可知,被執(zhí)行線程相應(yīng)一秒之后就結(jié)束了
public class stopThreadWithoutSleep implements Runnable{ public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new stopThreadWithoutSleep()); thread.start(); Thread.sleep(1000); thread.interrupt(); } @Override public void run() { int num = 0; while(!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) { if (num % 10000 == 0) { System.out.println(num + "是10000的倍數(shù)"); } num++; } System.out.println("結(jié)束"); } } /* 由于太長(zhǎng),只展示結(jié)尾部分的結(jié)果 587830000是10000的倍數(shù) 587840000是10000的倍數(shù) 587850000是10000的倍數(shù) 587860000是10000的倍數(shù) 結(jié)束 * */
在阻塞情況下停止線程
代碼展示
- 中斷之后,拋出異常
- 線程在sleep的過程中,會(huì)catch到InterruptedException這個(gè)異常,從而相應(yīng)中斷
public class stopThreadWithSleep { public static void main(String[] args) { Runnable runnable = () -> { int num = 0; while (num <= 300 && !Thread.currentThread().isInterrupted()) { if (num % 100 == 0) { System.out.println(num + "是100的倍數(shù)"); } num++; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.start(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } thread.interrupt(); } } /* * 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 com.jx.JavaTest.stopThread.stopThreadWithSleep.lambda$main$0(stopThreadWithSleep.java:15) at java.lang.Thread.run(Thread.java:748) * */
線程在每次迭代后都阻塞
- 代碼展示 即使不在while判斷是否中斷,sleep也能中斷異常
public class stopThreadWithSleepEveryLoop { public static void main(String[] args) { Runnable runnable = () -> { int num = 0; try { while (num <= 10000) { if (num % 100 == 0) { System.out.println(num + "是100的倍數(shù)"); } num++; Thread.sleep(10); } } catch (InterruptedException e) { e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.start(); try { Thread.sleep(5000); } catch ( InterruptedException e) { e.printStackTrace(); } thread.interrupt(); } } /* * 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 com.jx.JavaTest.stopThread.stopThreadWithSleepEveryLoop.lambda$main$0(stopThreadWithSleepEveryLoop.java:15) at java.lang.Thread.run(Thread.java:748) Process finished with exit code 0 * * */
當(dāng)catch寫到while內(nèi),則不能正常中斷
public class CantInterrupt { public static void main(String[] args) { Runnable runnable = () -> { int num = 0; while (num <= 10000) { if (num % 100 == 0) { System.out.println(num + "是100的倍數(shù)"); } num ++; try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread thread = new Thread(runnable); thread.start(); try { Thread.sleep(5000); } catch ( InterruptedException e) { e.printStackTrace(); } thread.interrupt(); } } /* * 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 com.jx.JavaTest.stopThread.CantInterrupt.lambda$main$0(CantInterrupt.java:14) at java.lang.Thread.run(Thread.java:748) 400是100的倍數(shù) 500是100的倍數(shù) 600是100的倍數(shù) 700是100的倍數(shù) 800是100的倍數(shù) 900是100的倍數(shù) Process finished with exit code -1 * */
- 即使在while的判斷條件中,加上檢測(cè)中斷的機(jī)制,也不能正常中斷
- 因?yàn)閖ava的sleep函數(shù),一旦相應(yīng)中斷,就會(huì)將中斷的標(biāo)志位刪除
public class CantInterrupt { public static void main(String[] args) { Runnable runnable = () -> { int num = 0; while (num <= 10000 && !Thread.currentThread().isInterrupted()) { if (num % 100 == 0) { System.out.println(num + "是100的倍數(shù)"); } num++; try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread thread = new Thread(runnable); thread.start(); try { Thread.sleep(5000); } catch ( InterruptedException e) { e.printStackTrace(); } thread.interrupt(); } } /* 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 com.jx.JavaTest.stopThread.CantInterrupt.lambda$main$0(CantInterrupt.java:14) at java.lang.Thread.run(Thread.java:748) 400是100的倍數(shù) 500是100的倍數(shù) Process finished with exit code -1 * */
停止線程的最佳實(shí)踐
- 在方法簽名中拋出異常,在run方法中強(qiáng)制進(jìn)行try catch
public class StopThreadInProd implements Runnable{ public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new StopThreadInProd()); thread.start(); Thread.sleep(1000); thread.interrupt(); } @Override public void run() { while (true) { System.out.println("start"); try { throwInMethod(); } catch (InterruptedException e) { System.out.println("保存日志/關(guān)閉程序"); e.printStackTrace(); } } } private void throwInMethod() throws InterruptedException { Thread.sleep(2000); } } /* * start 保存日志/關(guān)閉程序 start java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.jx.JavaTest.stopThread.StopThreadInProd.throwInMethod(StopThreadInProd.java:26) at com.jx.JavaTest.stopThread.StopThreadInProd.run(StopThreadInProd.java:17) at java.lang.Thread.run(Thread.java:748) start start Process finished with exit code -1 * * */
- 在catch語句中調(diào)用Thread.currentThread().interrupt恢復(fù)中斷狀態(tài)
- 結(jié)果:拋出異常,程序結(jié)束
public class StopThreadInProd2 implements Runnable{ public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new StopThreadInProd2()); thread.start(); Thread.sleep(1000); thread.interrupt(); } @Override public void run() { while (true) { if (Thread.currentThread().isInterrupted()) { System.out.println("Interrupt"); break; } reInterrupt(); } } private void reInterrupt() { try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); } } } /* java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.jx.JavaTest.stopThread.StopThreadInProd2.reInterrupt(StopThreadInProd2.java:25) at com.jx.JavaTest.stopThread.StopThreadInProd2.run(StopThreadInProd2.java:19) at java.lang.Thread.run(Thread.java:748) Interrupt * * */
- 依照上面的代碼,如果方法沒有沒有重新拋出異常
- 結(jié)果:程序拋出異常,但是程序沒有停止運(yùn)行
public class StopThreadInProd2 implements Runnable{ public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new StopThreadInProd2()); thread.start(); Thread.sleep(1000); thread.interrupt(); } @Override public void run() { while (true) { if (Thread.currentThread().isInterrupted()) { System.out.println("Interrupt"); break; } reInterrupt(); } } private void reInterrupt() { try { Thread.sleep(2000); } catch (InterruptedException e) { // Thread.currentThread().interrupt(); e.printStackTrace(); } } } /* java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.jx.JavaTest.stopThread.StopThreadInProd2.reInterrupt(StopThreadInProd2.java:25) at com.jx.JavaTest.stopThread.StopThreadInProd2.run(StopThreadInProd2.java:19) at java.lang.Thread.run(Thread.java:748) * * */
錯(cuò)誤停止的方法
被棄用的stop,suspend和resume方法
- 使用stop停止線程,會(huì)導(dǎo)致線程運(yùn)行一半突然停止,沒辦法完成最基本的操作,會(huì)造成臟數(shù)據(jù)
- 下面這段代碼的結(jié)果會(huì)造成一個(gè)連隊(duì)只有部分人領(lǐng)取到了裝備
- stop是不安全的,會(huì)直接停止監(jiān)視器
- suspend和resume不會(huì)破壞對(duì)象,但是會(huì)讓線程掛起,不釋放鎖,容易造成死鎖
public class StopThread implements Runnable{ @Override public void run() { // 模擬指揮軍隊(duì),一共五個(gè)連隊(duì),每個(gè)連隊(duì)一百人 // 以連隊(duì)為單位發(fā)放武器 for (int i = 0; i < 5; i++) { System.out.println("連隊(duì)" + i + "領(lǐng)取武器"); for (int j = 0; j < 10; j++) { System.out.println(j); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("連隊(duì)" + i + "領(lǐng)取完畢"); } } public static void main(String[] args) { Thread thread = new Thread(new StopThread()); thread.start(); try { thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } thread.stop(); } } /* * 連隊(duì)0領(lǐng)取武器 0 1 2 3 4 5 6 7 8 9 連隊(duì)0領(lǐng)取完畢 連隊(duì)1領(lǐng)取武器 0 1 2 3 4 5 Process finished with exit code 0 * */
用volatile設(shè)置boolean標(biāo)記位
- 下面這段代碼,通過改變標(biāo)志位的值會(huì)成功終止線程
public class Volatile implements Runnable { private volatile boolean canceled = false; @Override public void run() { int num = 0; try { while (num <= 10000 && !canceled) { if (num % 100 == 0) { System.out.println(num + " 是100的倍數(shù)"); } num++; Thread.sleep(1); } } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { Volatile v = new Volatile(); Thread thread = new Thread(v); thread.start(); Thread.sleep(1000); v.canceled = true; } } /* *0 是100的倍數(shù) 100 是100的倍數(shù) 200 是100的倍數(shù) 300 是100的倍數(shù) 400 是100的倍數(shù) 500 是100的倍數(shù) 600 是100的倍數(shù) Process finished with exit code 0 * * */
- 當(dāng)陷入阻塞的時(shí)候,是無法停止線程的
- 下面這段代碼的運(yùn)行結(jié)果,并沒有打印生產(chǎn)者停止運(yùn)行,說明根本沒有執(zhí)行生產(chǎn)者的finally那部分代碼
- 同時(shí)程序也沒停止
- 原因見生產(chǎn)者代碼 while循環(huán)中的注釋
// 模擬生產(chǎn)者和消費(fèi)者 public class cantStop { public static void main(String[] args) throws InterruptedException { // 阻塞隊(duì)列 // 滿了之后,放不進(jìn)去 // 空的時(shí)候取數(shù)據(jù),也會(huì)堵塞 ArrayBlockingQueue storage = new ArrayBlockingQueue(10); Producer producer = new Producer(storage); Thread producerThread = new Thread(producer); producerThread.start(); Thread.sleep(1000); Consumer consumer = new Consumer(storage); while (consumer.needMore()) { System.out.println(consumer.storage.take() + "被消費(fèi)"); Thread.sleep(100); } System.out.println("消費(fèi)者不需要更多數(shù)據(jù)"); // 消費(fèi)者不需要數(shù)據(jù),讓生產(chǎn)者停下來 producer.canceled = true; } } // 生產(chǎn)者 class Producer implements Runnable { public volatile boolean canceled = false; BlockingQueue storage; public Producer(BlockingQueue storage) { this.storage = storage; } @Override public void run() { int num = 0; try { while (num <= 10000 && !canceled) { if (num % 100 == 0) { // 當(dāng)堵塞隊(duì)列滿了之后,會(huì)堵塞在這里,而這段代碼沒有判斷機(jī)制 storage.put(num); System.out.println("num" + "生產(chǎn)"); } num++; } } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("生產(chǎn)者停止運(yùn)行"); } } } // 消費(fèi)者 class Consumer { BlockingQueue storage; public Consumer(BlockingQueue storage) { this.storage = storage; } public boolean needMore() { if (Math.random() > 0.9) { return false; } return true; } } /* * num生產(chǎn) num生產(chǎn) num生產(chǎn) num生產(chǎn) num生產(chǎn) num生產(chǎn) num生產(chǎn) num生產(chǎn) num生產(chǎn) num生產(chǎn) 0被消費(fèi) num生產(chǎn) 消費(fèi)者不需要更多數(shù)據(jù) * * */
- 將上面代碼用interrupt進(jìn)行中斷
- 程序成功停止
public class finxed { public static void main(String[] args) throws InterruptedException { finxed finxed = new finxed(); // 阻塞隊(duì)列 // 滿了之后,放不進(jìn)去 // 空的時(shí)候取數(shù)據(jù),也會(huì)堵塞 ArrayBlockingQueue storage = new ArrayBlockingQueue(10); Producer producer = finxed.new Producer(storage); Thread producerThread = new Thread(producer); producerThread.start(); Thread.sleep(1000); Consumer consumer = finxed.new Consumer(storage); while (consumer.needMore()) { System.out.println(consumer.storage.take() + "被消費(fèi)"); Thread.sleep(100); } System.out.println("消費(fèi)者不需要更多數(shù)據(jù)"); // 消費(fèi)者不需要數(shù)據(jù),讓生產(chǎn)者停下來 producerThread.interrupt(); } class Producer implements Runnable { public volatile boolean canceled = false; BlockingQueue storage; public Producer(BlockingQueue storage) { this.storage = storage; } @Override public void run() { int num = 0; try { while (num <= 10000 && !Thread.currentThread().isInterrupted()) { if (num % 100 == 0) { storage.put(num); System.out.println("num" + "生產(chǎn)"); } num++; } } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("生產(chǎn)者停止運(yùn)行"); } } } class Consumer { BlockingQueue storage; public Consumer(BlockingQueue storage) { this.storage = storage; } public boolean needMore() { if (Math.random() > 0.9) { return false; } return true; } } } /* * 2100被消費(fèi) num生產(chǎn) 2200被消費(fèi) num生產(chǎn) 2300被消費(fèi) num生產(chǎn) 消費(fèi)者不需要更多數(shù)據(jù) 生產(chǎn)者停止運(yùn)行 java.lang.InterruptedException at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048) at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:353) at com.jx.JavaTest.stopThread.volatiledmo.finxed$Producer.run(finxed.java:51) at java.lang.Thread.run(Thread.java:748) Process finished with exit code 0 * * */
interrupt源碼查看
- 這段代碼做的都是一些判斷,真正執(zhí)行中斷的代碼時(shí)interrupt0
- interrupt0是native代碼
public void interrupt() { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // Just to set the interrupt flag b.interrupt(this); return; } } interrupt0(); } private native void interrupt0();
interrupt相關(guān)函數(shù)練習(xí)
- isInterrupted獲取中斷標(biāo)志,獲取的是前面的線程
- interrupted獲取中斷標(biāo)志并重置,只關(guān)心執(zhí)行的線程,所以下面代碼執(zhí)行的是main線程
public class InterruptedTest { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Runnable() { @Override public void run() { while (true) { } } }); thread.start(); thread.interrupt(); // 獲取中斷標(biāo)志 System.out.println(thread.isInterrupted()); // true // 獲取中斷標(biāo)志并重置 System.out.println(thread.interrupted()); //false System.out.println(Thread.interrupted()); // false System.out.println(thread.isInterrupted()); //true thread.join(); System.out.println("over"); } }
到此這篇關(guān)于Java線程的停止實(shí)現(xiàn)原理詳解的文章就介紹到這了,更多相關(guān)Java線程的停止內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring框架基于注解的AOP之各種通知的使用與環(huán)繞通知實(shí)現(xiàn)詳解
這篇文章主要介紹了Spring框架基于注解的AOP之各種通知的使用及其環(huán)繞通知,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-11-11spring redis 如何實(shí)現(xiàn)模糊查找key
這篇文章主要介紹了spring redis 如何實(shí)現(xiàn)模糊查找key的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08詳解SpringBoot統(tǒng)一響應(yīng)體解決方案
這篇文章主要介紹了詳解SpringBoot統(tǒng)一響應(yīng)體解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07java(swing)+ mysql實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)源碼
這篇文章主要分享了java mysql實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)的源碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11詳解Java使用JDBC連接MySQL數(shù)據(jù)庫
本文詳細(xì)講解了Java使用JDBC連接MySQL數(shù)據(jù)庫的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-01-01分別在Groovy和Java中創(chuàng)建并初始化映射的不同分析
這篇文章主要為大家介紹了分別在Groovy和Java中創(chuàng)建并初始化映射的不同分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03SpringBoot集成Mybatis的實(shí)現(xiàn)步驟
這篇文章主要介紹了SpringBoot集成Mybatis的實(shí)現(xiàn)步驟,本文通過SpringBoot +MyBatis 實(shí)現(xiàn)對(duì)數(shù)據(jù)庫學(xué)生表的查詢操作,需要的朋友可以參考下2020-12-12Java 入門圖形用戶界面設(shè)計(jì)之復(fù)選框
圖形界面(簡(jiǎn)稱GUI)是指采用圖形方式顯示的計(jì)算機(jī)操作用戶界面。與早期計(jì)算機(jī)使用的命令行界面相比,圖形界面對(duì)于用戶來說在視覺上更易于接受,本篇精講Java語言中關(guān)于圖形用戶界面的復(fù)選框2022-02-02