Java實(shí)現(xiàn)優(yōu)雅停止線程的有效方法詳解
在Java中停止線程意味著在完成其任務(wù)之前停止正在進(jìn)行的操作,本質(zhì)上是放棄當(dāng)前操作。
雖然可以使用 Thread.stop() 方法停止線程,但強(qiáng)烈建議不要這樣做。雖然它確實(shí)終止了正在運(yùn)行的線程,但此方法被認(rèn)為是不安全的并且已被棄用。
java中終止線程
在Java中,有3種方法可以終止正在運(yùn)行的線程:
- 使用標(biāo)志:您可以創(chuàng)建一個(gè)boolean類(lèi)型的標(biāo)志,線程定期檢查該標(biāo)志。當(dāng)該標(biāo)志設(shè)置為某個(gè)值時(shí),線程可以?xún)?yōu)雅地退出其執(zhí)行。
- 使用interrupt()方法:可以使用interrupt()方法向線程發(fā)送中斷信號(hào)。
- 使用Thread.stop()方法(不推薦):可以使用Thread.stop()方法強(qiáng)行停止正在運(yùn)行的線程。然而,這種方法不被鼓勵(lì)并且被認(rèn)為是不安全的,因?yàn)樗赡軐?dǎo)致應(yīng)用程序中出現(xiàn)不可預(yù)測(cè)為。它已被棄用,應(yīng)該避免。
interrupt方法
使用interrupt()方法不會(huì)像帶有break語(yǔ)句的for循環(huán)一樣立即停止循環(huán)。它會(huì)在當(dāng)前線程內(nèi)設(shè)置一個(gè)停止標(biāo)志,但它不會(huì)立即停止線程。
public class MyThread extends Thread { public void run(){ super.run(); for(int i=0; i<500000; i++){ System.out.println("i="+(i+1)); } } } public class Run { public static void main(String args[]){ Thread thread = new MyThread(); thread.start(); try { Thread.sleep(2000); thread.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } } } /* 結(jié)果: ... i=499994 i=499995 i=499996 i=499997 i=499998 i=499999 i=500000 */
如何檢查線程是否處于停止?fàn)顟B(tài)
Java中的提供了兩種方法:
- this.interrupted():該方法檢測(cè)當(dāng)前線程(調(diào)用該方法的線程)是否已被中斷。它還具有清除當(dāng)前線程的中斷狀態(tài)的作用。
- this.isInterrupted():此方法測(cè)試調(diào)用它的線程(不一定是當(dāng)前線程)是否已被中斷。它不會(huì)清除線程的中斷狀態(tài)。
這兩種方法有什么區(qū)別?
我們先看一下this.interrupted()方法:
public class MyThread extends Thread { public void run(){ super.run(); for(int i=0; i<500000; i++){ i++; // System.out.println("i="+(i+1)); } } } public class Run { public static void main(String args[]){ Thread thread = new MyThread(); thread.start(); try { Thread.sleep(2000); thread.interrupt(); System.out.println("stop 1??" + thread.interrupted()); System.out.println("stop 2??" + thread.interrupted()); } catch (InterruptedException e) { e.printStackTrace(); } } } /* --------------------------- 結(jié)果: stop 1??false stop 2??false */
可以看到 Run 類(lèi)中調(diào)用Thread對(duì)象的 Interrupt() 方法來(lái)檢查線程對(duì)象的線程是否已停止,控制臺(tái)輸出的內(nèi)容表明線程尚未停止。
這也證實(shí)了interrupted()方法,它用于檢測(cè)當(dāng)前線程(在此上下文中為主線程)是否已被中斷。由于主線程從未被中斷過(guò),所以打印的結(jié)果是兩個(gè)false。
如何讓主線程產(chǎn)生中斷效果
public class Run2 { public static void main(String args[]){ Thread.currentThread().interrupt(); System.out.println("stop 1??" + Thread.interrupted()); System.out.println("stop 2??" + Thread.interrupted()); System.out.println("End"); } } /* ------------------ 結(jié)果: stop 1??true stop 2??false End */
第二個(gè)值是 false,因?yàn)楦鶕?jù) Interrupted() 方法的官方文檔,它會(huì)檢測(cè)當(dāng)前線程(在這里就是主線程)的中斷狀態(tài),它會(huì)在調(diào)用時(shí)清除線程的中斷狀態(tài)。
換句話說(shuō),如果連續(xù)調(diào)用它,第二次調(diào)用返回 false,因?yàn)樗呀?jīng)清除了第一次調(diào)用設(shè)置的中斷狀態(tài)。
因此,如果連續(xù)調(diào)用interrupted()兩次,第二次調(diào)用將返回false,因?yàn)榈谝淮握{(diào)用清除了線程的中斷狀態(tài)。
isInterrupted()
現(xiàn)在讓我們看一下 isInterrupted() 方法。
public class Run3 { public static void main(String args[]){ Thread thread = new MyThread(); thread.start(); thread.interrupt(); System.out.println("stop 1??" + thread.isInterrupted()); System.out.println("stop 2??" + thread.isInterrupted()); } } /* --------------- 結(jié)果: stop 1??true stop 2??true */
isInterrupted() 不會(huì)清除中斷狀態(tài),這就是為什么在輸出中看到兩個(gè)true。
使用異常來(lái)停止線程
有了上面獲得的知識(shí),就可以在線程中使用for循環(huán)來(lái)檢查線程是否處于停止?fàn)顟B(tài)。 如果處于停止?fàn)顟B(tài),后續(xù)的代碼將不再運(yùn)行。
public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 500000; i++) { if (Thread.interrupted()) { System.out.println("Thread is interrupted. Exiting..."); return; //退出 } System.out.println("i="+(i+1)); } } } public class Main { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); try { Thread.sleep(2000); thread.interrupt(); // 設(shè)置中斷狀態(tài) } catch (InterruptedException e) { e.printStackTrace(); } } } /* ------------- 結(jié)果: ... i=202053 i=202054 i=202055 i=202056 Thread is interrupted. Exiting... */
在停止線程的同時(shí),將繼續(xù)執(zhí)行 for 循環(huán)之后的任何代碼。
改下代碼,讓我們看一下下面的例子:
public class MyThread extends Thread { public void run(){ super.run(); for(int i=0; i<500000; i++){ if(this.interrupted()) { System.out.println("Thread is interrupted. Exiting..."); break; } System.out.println("i="+(i+1)); } System.out.println("Out of for"); } } /* 結(jié)果: ... i=180136 i=180137 i=180138 i=180139 Thread is interrupted. Exiting... Out of for */
如何解決中斷后,代碼繼續(xù)執(zhí)行的問(wèn)題
public class MyThread extends Thread { public void run(){ super.run(); try { for(int i=0; i< 500000; i++){ if(this.interrupted()) { System.out.println("Thread is interrupted. Exiting..."); throw new InterruptedException(); } System.out.println("i="+(i+1)); } System.out.println("Out of for"); } catch (InterruptedException e) { System.out.println("In catch..."); e.printStackTrace(); } } } /* -------------------------------------------------------------------------- 結(jié)果: ... i=203798 i=203799 i=203800 Thread is interrupted. Exiting... In catch... java.lang.InterruptedException at thread.MyThread.run(MyThread.java:13) */
線程Sleep怎么停止
如果線程在 sleep() 狀態(tài)下停止會(huì)有什么影響?
public class MyThread extends Thread { public void run(){ super.run(); try { System.out.println("Thread begin..."); Thread.sleep(200000); System.out.println("Thread end..."); } catch (InterruptedException e) { System.out.println("Stop while sleeping" + this.isInterrupted()); e.printStackTrace(); } } } /* ----------------------------------------------------------------------- 結(jié)果: Thread begin... Stop while sleeping,the result of isInterrupted() is::false java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at thread.MyThread.run(MyThread.java:12) */
從打印結(jié)果來(lái)看,如果線程在睡眠狀態(tài)下停止,它將拋出InterruptedException異常進(jìn)入catch塊并清除停止?fàn)顟B(tài)值,將其設(shè)置為false。
強(qiáng)制停止線程
使用 stop() 方法終止線程是一種非常激進(jìn)的方法。
public class MyThread extends Thread { private int i = 0; public void run(){ super.run(); try { while (true){ System.out.println("i=" + i); i++; Thread.sleep(200); } } catch (InterruptedException e) { e.printStackTrace(); } } } public class Run { public static void main(String args[]) throws InterruptedException { Thread thread = new MyThread(); thread.start(); Thread.sleep(2000); thread.stop(); } } /* ----------------------------------------------- 結(jié)果: i=0 i=1 i=2 i=3 i=4 i=5 i=6 i=7 i=8 i=9 Process finished with exit code 0 */
當(dāng)調(diào)用 stop() 方法時(shí),它會(huì)拋出 java.lang.ThreadDeath 異常,但大多數(shù)情況下,不需要顯式捕獲該異常。
public class MyThread extends Thread { private int i = 0; public void run(){ super.run(); try { this.stop(); } catch (ThreadDeath e) { System.out.println("In catch"); e.printStackTrace(); } } } public class Run { public static void main(String args[]) throws InterruptedException { Thread thread = new MyThread(); thread.start(); } }
stop() 方法已被棄用,因?yàn)閺?qiáng)制停止線程可能會(huì)阻止某些必要的清理工作完成。
此外,它還可能導(dǎo)致鎖定對(duì)象解鎖,從而導(dǎo)致數(shù)據(jù)同步問(wèn)題和數(shù)據(jù)不一致問(wèn)題。 由
于 stop() 方法在 JDK 中已被標(biāo)記為已棄用/過(guò)時(shí),因此很明顯它存在功能缺陷。 因此,不建議在程序中使用 stop() 方法。
使用 return 來(lái)停止線程
將interrupt()方法與return結(jié)合起來(lái)也可以達(dá)到停止線程的效果。
public class MyThread extends Thread { public void run(){ while (true){ if(this.isInterrupted()){ System.out.println("The thread has been stopped."); return; } System.out.println("Time: " + System.currentTimeMillis()); } } } public class Run { public static void main(String args[]) throws InterruptedException { Thread thread = new MyThread(); thread.start(); Thread.sleep(2000); thread.interrupt(); } } /* ------------------------------------ 結(jié)果:... Time: 1696990194000 Time: 1696990194000 Time: 1696990194000 The thread has been stopped. */
但是,仍然建議使用“拋出異常”方法來(lái)停止線程,因?yàn)樗试S您通過(guò)在 catch 塊中重新拋出異常來(lái)傳播停止事件。
到此這篇關(guān)于Java實(shí)現(xiàn)優(yōu)雅停止線程的有效方法詳解的文章就介紹到這了,更多相關(guān)Java停止線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)簡(jiǎn)易學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)易學(xué)生管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07Java?深入理解創(chuàng)建型設(shè)計(jì)模式之抽象工廠模式
當(dāng)系統(tǒng)所提供的工廠所需生產(chǎn)的具體產(chǎn)品并不是一個(gè)簡(jiǎn)單的對(duì)象,而是多個(gè)位于不同產(chǎn)品等級(jí)結(jié)構(gòu)中屬于不同類(lèi)型的具體產(chǎn)品時(shí)需要使用抽象工廠模式,抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態(tài)2022-02-02java使用鏈表實(shí)現(xiàn)約瑟夫環(huán)
這篇文章主要為大家詳細(xì)介紹了java使用鏈表實(shí)現(xiàn)約瑟夫環(huán),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05解決@PostConstruct注解導(dǎo)致的程序無(wú)法啟動(dòng)(@PostConstruct的執(zhí)行)
這篇文章主要介紹了解決@PostConstruct注解導(dǎo)致的程序無(wú)法啟動(dòng)(@PostConstruct的執(zhí)行)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Spring?Boot整合持久層之JdbcTemplate多數(shù)據(jù)源
持久層是JavaEE中訪問(wèn)數(shù)據(jù)庫(kù)的核心操作,SpringBoot中對(duì)常見(jiàn)的持久層框架都提供了自動(dòng)化配置,例如JdbcTemplate、JPA 等,MyBatis 的自動(dòng)化配置則是MyBatis官方提供的。接下來(lái)分別向讀者介紹Spring Boot整合這持久層技術(shù)中的整合JdbcTemplate2022-08-08使用jmeter實(shí)現(xiàn)對(duì)jar包的調(diào)用方式
這篇文章主要介紹了使用jmeter實(shí)現(xiàn)對(duì)jar包的調(diào)用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03Spring如何使用xml創(chuàng)建bean對(duì)象
這篇文章主要介紹了Spring如何使用xml創(chuàng)建bean對(duì)象,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08