Java 正確終止線(xiàn)程的方法
Thread類(lèi)中有一個(gè)已經(jīng)廢棄的 stop() 方法,它可以終止線(xiàn)程,但由于它不管三七二十一,直接終止線(xiàn)程,所以被廢棄了。比如,當(dāng)線(xiàn)程被停止后還需要進(jìn)行一些善后操作(如,關(guān)閉外部資源),使用這個(gè)方法就無(wú)能為力了??梢酝ㄟ^(guò)線(xiàn)程中斷來(lái)實(shí)現(xiàn)線(xiàn)程終止。
首先來(lái)看一下Java線(xiàn)程中斷的一些內(nèi)容:
- Java平臺(tái)為每個(gè)線(xiàn)程維護(hù)了一個(gè)布爾型的中斷標(biāo)記,可以通過(guò)下列方法獲取該標(biāo)記的值:
interrupt() 中斷某個(gè)線(xiàn)程
isInterrupted() 返回該線(xiàn)程的中斷標(biāo)記
interrupted() 返回并重置該線(xiàn)程的中斷標(biāo)記(置為false)
- 中斷僅是發(fā)起線(xiàn)程對(duì)目標(biāo)線(xiàn)程的一種請(qǐng)求,也就是說(shuō),目標(biāo)線(xiàn)程對(duì)這種請(qǐng)求可以相應(yīng),也可以忽略。
- Java標(biāo)準(zhǔn)庫(kù)中與線(xiàn)程阻塞相關(guān)的方法對(duì)中斷的相應(yīng)方式都是拋出 InterruptedException 異常,并且按照慣例,拋出異常前都會(huì)重置中斷標(biāo)記為false,因此這些方法會(huì)清空線(xiàn)程的中斷標(biāo)記。
- Java標(biāo)準(zhǔn)庫(kù)中與線(xiàn)程阻塞相關(guān)的方法在進(jìn)行阻塞前會(huì)判斷中斷標(biāo)記是否為true,為true則拋出異常;如果在阻塞后調(diào)用中斷方法的話(huà),那么JVM會(huì)設(shè)置該線(xiàn)程的中斷標(biāo)記,然后將該線(xiàn)程喚醒,因此中斷具有喚醒線(xiàn)程的作用。
由上面幾點(diǎn)和第二句加粗的話(huà)可知,可以使用線(xiàn)程中斷來(lái)實(shí)現(xiàn)線(xiàn)程終止,只要目標(biāo)線(xiàn)程判斷一下中斷標(biāo)記即可,即使被中斷的線(xiàn)程正處于阻塞狀態(tài),也能把他喚醒起來(lái)終止;由第一句加粗的話(huà)可知,直接使用線(xiàn)程中斷實(shí)現(xiàn)線(xiàn)程終止是存在風(fēng)險(xiǎn)的,因?yàn)榭赡苷{(diào)用了一些Java標(biāo)準(zhǔn)庫(kù)的阻塞方法,而導(dǎo)致了中斷標(biāo)記被清空,也就無(wú)法獲得中斷標(biāo)記了(總是false),因此需要自己創(chuàng)建一個(gè)中斷標(biāo)記配合使用。
如,下面是一個(gè)可中斷的任務(wù)執(zhí)行器,他會(huì)在每次執(zhí)行任務(wù)前,判斷一下自定i的終止標(biāo)記和剩余的任務(wù)數(shù)(善后);提供的shutdown方法除了將工作線(xiàn)程中斷外(主要作用是喚醒可能處于阻塞狀態(tài)的任務(wù)),還會(huì)將終止交集 terminated 置為 true。
執(zhí)行 main 方法,可以發(fā)現(xiàn),首先會(huì)打印出“客戶(hù)端調(diào)用了 shutdown 方法”,然后過(guò)了四秒,main線(xiàn)程才會(huì)終止,可知shutdown方法正確地將目標(biāo)線(xiàn)程終止了。關(guān)于“按照慣例,Java標(biāo)準(zhǔn)庫(kù)中拋出InterruptedException異常的和線(xiàn)程相關(guān)的阻塞方法會(huì)清空中斷標(biāo)記”,可以將條件中的 !interminated 替換成 !Thread.currentThread().isInterrupted(),然后再執(zhí)行main方法測(cè)試,可以發(fā)現(xiàn)main線(xiàn)程始終無(wú)法終止,因?yàn)?sleep() 方法清空了中斷標(biāo)記,所以 !Thread.currentThread().isInterrupted() 始終為true,導(dǎo)致工作線(xiàn)程始終無(wú)法終止。
public class TerminableTaskRunner { // 存儲(chǔ)要執(zhí)行的任務(wù) private final BlockingQueue<Runnable> tasks; // 線(xiàn)程終止標(biāo)志 private volatile boolean terminated; // 剩余的任務(wù)數(shù) private final AtomicInteger count; // 實(shí)際執(zhí)行任務(wù)的線(xiàn)程 private volatile Thread workThread; public TerminableTaskRunner(int capacity) { this.tasks = new LinkedBlockingDeque<>(capacity); this.count = new AtomicInteger(0); this.workThread = new WorkThread(); workThread.start(); } public void submit(Runnable task) { this.tasks.add(task); this.count.incrementAndGet(); } public void shutdown() { terminated = true; // 線(xiàn)程終止標(biāo)志,由于中斷標(biāo)志可能會(huì)被覆蓋,所以需要自己創(chuàng)建一個(gè)標(biāo)志 if (workThread != null) workThread.interrupt(); // 喚醒線(xiàn)程 } private class WorkThread extends Thread { @Override public void run() { Runnable task; try { while (!terminated || tasks.size() >= 1) { task = tasks.take(); try { task.run(); // 可能會(huì)清空當(dāng)前線(xiàn)程的中斷標(biāo)記,如task.run()在內(nèi)部調(diào)用的阻塞方法拋出了InterruptedException } catch (Throwable e) { e.printStackTrace(); } count.decrementAndGet(); } } catch (InterruptedException e) { // 一旦調(diào)用shutdown且tasks.take()阻塞住,就拋出該異常,沒(méi)有任務(wù)要執(zhí)行,直接終止 workThread = null; } } } public static void main(String[] args) { TerminableTaskRunner taskRunner = new TerminableTaskRunner(4); for (int i = 0; i < 4; i++) { taskRunner.submit(()->{ try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { System.out.println("客戶(hù)端調(diào)用了 shutdown 方法"); } }); } taskRunner.shutdown(); } }
以上就是Java 正確終止線(xiàn)程的方法的詳細(xì)內(nèi)容,更多關(guān)于Java 終止線(xiàn)程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Eclipse啟動(dòng)Tomcat超時(shí)問(wèn)題的解決方法
2013-03-03通過(guò)JDK源碼學(xué)習(xí)InputStream詳解
InputStream抽象類(lèi)是所有字節(jié)輸入流的類(lèi)的超類(lèi)。這篇文章主要給大家介紹了關(guān)于通過(guò)JDK源碼學(xué)習(xí)InputStream的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11詳解springboot + profile(不同環(huán)境讀取不同配置)
本篇文章主要介紹了springboot + profile(不同環(huán)境讀取不同配置),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05Java Web Fragment在項(xiàng)目中使用方法詳解
這篇文章主要介紹了Web Fragment在項(xiàng)目中使用方法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02springboot多模塊多環(huán)境配置文件問(wèn)題(動(dòng)態(tài)配置生產(chǎn)和開(kāi)發(fā)環(huán)境)
這篇文章主要介紹了springboot多模塊多環(huán)境配置文件問(wèn)題(動(dòng)態(tài)配置生產(chǎn)和開(kāi)發(fā)環(huán)境),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04Spring框架基于AOP實(shí)現(xiàn)簡(jiǎn)單日志管理步驟解析
這篇文章主要介紹了Spring框架基于AOP實(shí)現(xiàn)簡(jiǎn)單日志管理步驟解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06去掉 IDEA 中 mybatis配置文件的局部背景顏色(圖解)
這篇文章通過(guò)圖文并茂的形式給大家介紹了去掉IntelliJ IDEA 中 mybatis配置文件的局部背景顏色及mybatis 對(duì)應(yīng)的 xml 文件警告的方法圖解,需要的朋友可以參考下2018-09-09Java鍵值對(duì)Pair的使用方式和操作實(shí)現(xiàn)
鍵值對(duì)是一種常見(jiàn)的數(shù)據(jù)結(jié)構(gòu),它由一個(gè)唯一的鍵和與之關(guān)聯(lián)的值組成,本文就來(lái)介紹一下Java鍵值對(duì)Pair的使用方式和操作實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12