正確結(jié)束Java線程的方法
使用標(biāo)志位
很簡(jiǎn)單地設(shè)置一個(gè)標(biāo)志位,名稱就叫做isCancelled。啟動(dòng)線程后,定期檢查這個(gè)標(biāo)志位。如果isCancelled=true,那么線程就馬上結(jié)束。
public class MyThread implements Runnable{
private volatile boolean isCancelled;
public void run(){
while(!isCancelled){
//do something
}
}
public void cancel(){ isCancelled=true; }
}
注意的是,isCancelled需要為volatile,保證線程讀取時(shí)isCancelled是最新數(shù)據(jù)。
我以前經(jīng)常用這種簡(jiǎn)單方法,在大多時(shí)候也很有效,但并不完善??紤]下,如果線程執(zhí)行的方法被阻塞,那么如何執(zhí)行isCancelled的檢查呢?線程有可能永遠(yuǎn)不會(huì)去檢查標(biāo)志位,也就卡住了。
使用中斷
Java提供了中斷機(jī)制,Thread類下有三個(gè)重要方法。
- public void interrupt()
- public boolean isInterrupted()
- public static boolean interrupted(); // 清除中斷標(biāo)志,并返回原狀態(tài)
每個(gè)線程都有個(gè)boolean類型的中斷狀態(tài)。當(dāng)使用Thread的interrupt()方法時(shí),線程的中斷狀態(tài)會(huì)被設(shè)置為true。
下面的例子啟動(dòng)了一個(gè)線程,循環(huán)執(zhí)行打印一些信息。使用isInterrupted()方法判斷線程是否被中斷,如果是就結(jié)束線程。
public class InterruptedExample {
public static void main(String[] args) throws Exception {
InterruptedExample interruptedExample = new InterruptedExample();
interruptedExample.start();
}
public void start() {
MyThread myThread = new MyThread();
myThread.start();
try {
Thread.sleep(3000);
myThread.cancel();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private class MyThread extends Thread{
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("test");
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("interrupt");
//拋出InterruptedException后中斷標(biāo)志被清除,標(biāo)準(zhǔn)做法是再次調(diào)用interrupt恢復(fù)中斷
Thread.currentThread().interrupt();
}
}
System.out.println("stop");
}
public void cancel(){
interrupt();
}
}
}
對(duì)線程調(diào)用interrupt()方法,不會(huì)真正中斷正在運(yùn)行的線程,只是發(fā)出一個(gè)請(qǐng)求,由線程在合適時(shí)候結(jié)束自己。
例如Thread.sleep這個(gè)阻塞方法,接收到中斷請(qǐng)求,會(huì)拋出InterruptedException,讓上層代碼處理。這個(gè)時(shí)候,你可以什么都不做,但等于吞掉了中斷。因?yàn)閽伋鯥nterruptedException后,中斷標(biāo)記會(huì)被重新設(shè)置為false!看sleep()的注釋,也強(qiáng)調(diào)了這點(diǎn)。
@throws InterruptedException if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown. public static native void sleep(long millis) throws InterruptedException;
記得這個(gè)規(guī)則:什么時(shí)候都不應(yīng)該吞掉中斷!每個(gè)線程都應(yīng)該有合適的方法響應(yīng)中斷!
所以在InterruptedExample例子里,在接收到中斷請(qǐng)求時(shí),標(biāo)準(zhǔn)做法是執(zhí)行Thread.currentThread().interrupt()恢復(fù)中斷,讓線程退出。
從另一方面談起,你不能吞掉中斷,也不能中斷你不熟悉的線程。如果線程沒(méi)有響應(yīng)中斷的方法,你無(wú)論調(diào)用多少次interrupt()方法,也像泥牛入海。
用Java庫(kù)的方法比自己寫(xiě)的要好
自己手動(dòng)調(diào)用interrupt()方法來(lái)中斷程序,OK。但是Java庫(kù)提供了一些類來(lái)實(shí)現(xiàn)中斷,更好更強(qiáng)大。
Executor框架提供了Java線程池的能力,ExecutorService擴(kuò)展了Executor,提供了管理線程生命周期的關(guān)鍵能力。其中,ExecutorService.submit返回了Future對(duì)象來(lái)描述一個(gè)線程任務(wù),它有一個(gè)cancel()方法。
下面的例子擴(kuò)展了上面的InterruptedExample,要求線程在限定時(shí)間內(nèi)得到結(jié)果,否則觸發(fā)超時(shí)停止。
public class InterruptByFuture {
public static void main(String[] args) throws Exception {
ExecutorService es = Executors.newSingleThreadExecutor();
Future<?> task = es.submit(new MyThread());
try {
//限定時(shí)間獲取結(jié)果
task.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
//超時(shí)觸發(fā)線程中止
System.out.println("thread over time");
} catch (ExecutionException e) {
throw e;
} finally {
boolean mayInterruptIfRunning = true;
task.cancel(mayInterruptIfRunning);
}
}
private static class MyThread extends Thread {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("count");
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("interrupt");
Thread.currentThread().interrupt();
}
}
System.out.println("thread stop");
}
public void cancel() {
interrupt();
}
}
}
Future的get方法可以傳入時(shí)間,如果限定時(shí)間內(nèi)沒(méi)有得到結(jié)果,將會(huì)拋出TimeoutException。此時(shí),可以調(diào)用Future的cancel()方法,對(duì)任務(wù)所在線程發(fā)出中斷請(qǐng)求。
cancel()有個(gè)參數(shù)mayInterruptIfRunning,表示任務(wù)是否能夠接收到中斷。
- mayInterruptIfRunning=true時(shí),任務(wù)如果在某個(gè)線程中運(yùn)行,那么這個(gè)線程能夠被中斷;
- mayInterruptIfRunning=false時(shí),任務(wù)如果還未啟動(dòng),就不要運(yùn)行它,應(yīng)用于不處理中斷的任務(wù)
要注意,mayInterruptIfRunning=true表示線程能接收中斷,但線程是否實(shí)現(xiàn)了中斷不得而知。線程要正確響應(yīng)中斷,才能真正被cancel。
線程池的shutdownNow()會(huì)嘗試停止池內(nèi)所有在執(zhí)行的線程,原理也是發(fā)出中斷請(qǐng)求。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
idea配置檢查XML中SQL語(yǔ)法及書(shū)寫(xiě)sql語(yǔ)句智能提示的方法
idea連接了數(shù)據(jù)庫(kù),也可以執(zhí)行SQL查到數(shù)據(jù),但是無(wú)法識(shí)別sql語(yǔ)句中的表導(dǎo)致沒(méi)有提示,下面這篇文章主要給大家介紹了關(guān)于idea配置檢查XML中SQL語(yǔ)法及書(shū)寫(xiě)sql語(yǔ)句智能提示的相關(guān)資料,需要的朋友可以參考下2023-03-03
Spring Cloud實(shí)現(xiàn)5分鐘級(jí)區(qū)域切換的操作方法
Spring Cloud 2023.x通過(guò)智能路由預(yù)熱、多活數(shù)據(jù)同步和自動(dòng)化流量切換,實(shí)現(xiàn)5分鐘內(nèi)完成跨區(qū)域故障轉(zhuǎn)移,本文以某電商平臺(tái)從AWS亞太切換至阿里云華東的實(shí)戰(zhàn)為例,詳解關(guān)鍵技術(shù)路徑,需要的朋友可以參考下2025-04-04
jquery uploadify和apache Fileupload實(shí)現(xiàn)異步上傳文件示例
這篇文章主要介紹了jquery uploadify和apache Fileupload實(shí)現(xiàn)異步上傳文件示例,需要的朋友可以參考下2014-05-05
Java中注解@JsonFormat與@DateTimeFormat的使用
從數(shù)據(jù)庫(kù)獲取時(shí)間傳到前端進(jìn)行展示的時(shí)候,我們有時(shí)候可能無(wú)法得到一個(gè)滿意的時(shí)間格式的時(shí)間日期,本文主要介紹了Java中注解@JsonFormat與@DateTimeFormat的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08
SpringBoot如何集成PageHelper分頁(yè)功能
這篇文章主要介紹了SpringBoot如何集成PageHelper分頁(yè)功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03

