Java中原來在Thread中提供了
stop()方法
來終止線程,但這個方法是不安全的,所以一般不建議使用。文本將介紹兩種可以優雅的終止線程的方式...
<!-- more -->
第一種
在JAVA《Java多線程模式》
中有一種叫Two-Phase Termination(兩步終止)的模式
可以優雅的終止線程,這種模式采用了兩個步驟來終止線程,所以叫兩步終止模式
。
- 先將執行標志位
isShutdown
設為false
,使工作中的線程轉變為終止處理
中的狀態 - 真正去執行終止操作,這樣的做法可以保證線程的安全性、生命性和響應性。
class Worker extends Thread {private volatile boolean isShutdown = true;public void shutdown() {System.out.println("接收到關閉通知......");this.isShutdown = false;interrupt();}@Overridepublic void run() {while (this.isShutdown) {System.out.println("正在工作:" + System.currentTimeMillis());try {Thread.sleep(1000);} catch (InterruptedException e) {System.out.println("打斷正在工作的線程......");}}System.out.println("銷毀......");}
}public class ThreadClose {public static void main(String[] args) throws InterruptedException {Worker worker = new Worker();worker.start();//開始工作Thread.sleep(3 * 1000);worker.shutdown();//優雅關閉}
}
運行日志
正在工作:1505828036769
正在工作:1505828037770
正在工作:1505828038771
接收到關閉通知......
打斷正在工作的線程......
銷毀......
- 安全性:不會在線程正在執行關鍵區域
--Critical Section
的時候突然結束掉 - 生命性:一定會進行終止處理,
shutdown()
中,會調用interrupt()
,保證即使線程處于sleep
或wait
狀態也可以被立即終止 - 響應性:將
isShutdown
設為volatile
,能保證線程收到終止請求后,會盡快開始終止處理。
存在的問題:針對沒有阻塞的情況:設置標志變量,讓線程正常自然死亡,和諧!,但是如果在調用shutdown
發生阻塞情況呢?
第二種
在 《多線程第一章》的時候,介紹過守護線程
的作用,那么是不是可以通過開啟守護線程
的方式去監聽
功能
1.當工作結束就關閉主線程(主線程銷毀守護線程也會跟著一同銷毀)
2.如果任務長時間未完成,停止工作任務,減少開銷
編碼
1.定義主線程與發送的指令
2.在主線程run
方法中創建一個守護線程,用來執行我們投遞的任務
3.前面已經介紹過join
的功能,它可以阻塞主線程,等待子線程完成后主線程繼續執行
4.如果join
釋放后,發送完成指令
private Thread executeService;
private volatile boolean finished = false;
public void execute(Runnable task) {executeService = new Thread(() -> {Thread runner = new Thread(task);runner.setDaemon(true);runner.start();try {runner.join();//前面已經說過join與線程了finished = true;} catch (InterruptedException e) {System.out.println("打斷正在工作的線程......");}});executeService.start();
}
5.創建listener(long mills)
,監聽工作情況
6.監聽任務是否完成,如果未完成監聽當前是否逾期,逾期打斷線程結束監聽
public void listener(long mills) {System.out.println("開啟監聽......");long currentTime = System.currentTimeMillis();while (!finished) {if ((System.currentTimeMillis() - currentTime) >= mills) {System.out.println("工作耗時過長,開始打斷...");executeService.interrupt();//打斷線程break;}try {executeService.sleep(100L);//每隔100毫秒檢測一次} catch (InterruptedException e) {e.printStackTrace();}}
}
7.測試
public static void main(String[] args) {WorkerService service = new WorkerService();long start = System.currentTimeMillis();service.execute(() -> {try {Thread.sleep(3 * 1000);// TODO 模擬加載數據} catch (InterruptedException e) {e.printStackTrace();}});service.listener(4 * 1000);System.out.println("一共耗時:" + (System.currentTimeMillis() - start));
}
listener(4 * 1000) 的運行日志,當任務完成會直接退出,并不會一直占用
開啟監聽......
一共耗時:3049
listener(2 * 1000) 的運行日志,當任務超時直接打斷線程,減少資源占用
開啟監聽......
工作耗時過長,開始打斷...
一共耗時:2050
打斷正在工作的線程......
- 說點什么
全文代碼:https://gitee.com/battcn/battcn-concurent/tree/master/Chapter1-1/battcn-thread/src/main/java/com/battcn/chapter4
- 個人QQ:1837307557
- battcn開源群(適合新手):391619659
微信公眾號:battcn
(歡迎調戲)