詳解Java如何關(guān)閉線程以及線程池
前言
這個(gè)問題是一個(gè)高頻的面試題
而且在印象中是由stop方法執(zhí)行或者終端中的kill殺死
但是這些方法直接簡單粗暴,很不安全,而且也不推廣
不使用stop的方法
之所以不安全不推廣是因?yàn)椋?/p>
- stop方法不管線程邏輯是否完整,都會(huì)終止當(dāng)前正在運(yùn)行的線程
- 會(huì)破壞其原子邏輯(多線程加了鎖之解決資源共享,但是stop會(huì)將其所有鎖丟棄,造成混亂)
1. 關(guān)閉線程
1.1 volatile關(guān)鍵字
使用自定義的標(biāo)志位決定線程的執(zhí)行情況
具體思路大致如下:設(shè)置一個(gè) 父線程 的狀態(tài)變量,以其影響其子線程即可
public class test extends Thread { //標(biāo)識(shí)線程是否結(jié)束 public static boolean thread_stop = true; public void stopThread() { thread_stop = false; } public static void main(String[] args) { test t = new test(); t.start(); System.out.println("Father Thread Start"); try { //先讓線程跑起來 Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } //結(jié)束線程 //將其狀態(tài)變量直接改為false //thread_stop = false; //調(diào)用方法改為false(與狀態(tài)變量直接修改 一個(gè)道理) t.stopThread(); System.out.println("Father Thread end"); } @Override public void run() { while (thread_stop) { System.out.println("Child Thread Start"); } System.out.println("Child Thread end"); } }
但是網(wǎng)上說不加volatile是停不下來的,其實(shí)是可以停下來的
只不過
加了volatile有幾個(gè)好處:
- volatile可以保證狀態(tài)變量為系統(tǒng)內(nèi)存值而不是緩存里值(避免值不一致)
- volatile 關(guān)鍵字能夠是該變量對(duì)其他線程“可見”,即當(dāng)主線程修改變量并刷新到主內(nèi)存后,會(huì)讓其他線程去主內(nèi)存中讀取該變量。當(dāng)然,volatile并不能保證線程的安全性
具體加在狀態(tài)變量中的位置如下:
//標(biāo)識(shí)線程是否結(jié)束 public static volatile boolean thread_stop = true;
之后具體完整的輸出為:
具體完整的輸出為:
Father Thread start
Child Thread Start
Child Thread Start
。。。
。。。
Child Thread Start
Child Thread Start
Father Thread end
Child Thread end
1.2 intrrrupt()方法
不能終止一個(gè)正在執(zhí)行著的線程,它只是修改中斷標(biāo)志而已
這個(gè)方法分為兩種情況:
線程處于阻塞:立馬退出阻塞,拋出InterruptedException異常。通過捕獲這個(gè)異常,來讓線程退出
線程處于非阻塞:處于運(yùn)行狀態(tài)不受影響,僅僅標(biāo)記了線程的中斷為true。在適當(dāng)?shù)奈恢弥姓{(diào)用isInterrupted方法查看是否被中斷并且退出
public class test extends Thread { public static void main(String[] args) { test t = new test(); t.start(); System.out.println("Father Thread Start"); try { //先讓線程跑起來 Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } //結(jié)束線程 t.interrupt(); System.out.println("Father Thread end"); } @Override public void run() { //分配線程的中斷狀態(tài),并且此狀態(tài)可以由interrupted()方法生成 while (!Thread.interrupted()) { System.out.println("Child Thread Start"); } System.out.println("Child Thread end"); } }
執(zhí)行結(jié)果截圖:
具體完整的輸出為:
Father Thread start
Child Thread Start
Child Thread Start
。。。
。。。
Child Thread Start
Child Thread Start
Father Thread end
Child Thread end
2.關(guān)閉線程池
優(yōu)雅的關(guān)閉線程池:(比如ThreadPoolExecutor類)
可以通過shutdown方法逐步關(guān)閉池中的線程(溫和安全)
shutdown():拒收新任務(wù),不會(huì)立即終止線程池。而是要等所有任務(wù)緩存隊(duì)列中的任務(wù)都執(zhí)行完后才終止。
shutdownNow():拒收新任務(wù),立即終止線程池。并嘗試打斷正在執(zhí)行的任務(wù)。
并且清空任務(wù)緩存隊(duì)列,返回尚未執(zhí)行的任務(wù)
以下是對(duì)兩個(gè)線程池關(guān)閉的方法源代碼進(jìn)行分析
而且關(guān)閉的途中,這兩個(gè)方法也不是瞬間立馬關(guān)閉,等待關(guān)閉的同時(shí),還還調(diào)用awaitTermination方法來阻塞等待
2.1 shutdownNow()方法
查看java的源代碼
在try內(nèi)部結(jié)構(gòu)中
1.檢查其狀態(tài)
2.原子性的修改線程池的狀態(tài)為stop
3.遍歷工作隊(duì)列線程,調(diào)用interrupt方法
4.將隊(duì)列中還未執(zhí)行的放到任務(wù)隊(duì)列
源碼內(nèi)部:其邏輯就是修改線程池狀態(tài)為stop,工作隊(duì)列中調(diào)用interrupt方法
在調(diào)用shutdownNow方法:
- 正在執(zhí)行的線程會(huì)(getTask返回null)導(dǎo)致線程退出。
- 隊(duì)列中讀取的任務(wù)會(huì)阻塞,拋出異常之后。工作隊(duì)列就會(huì)調(diào)用interrupt方法
2.2 shutdown()方法
同樣也是看java的源代碼
同樣也是4步狀態(tài)
1.檢查其狀態(tài)
2.修改線程池狀態(tài)為SHUTDOWN
3.調(diào)用interruptIdleWorkers方法中斷空閑線程(只有加鎖成功的線程才會(huì)被調(diào)用interrupt方法)
而正在執(zhí)行的線程是加鎖失敗,不會(huì)被中斷
主要通過這個(gè)函數(shù)去區(qū)分判斷
以上就是詳解Java如何關(guān)閉線程以及線程池的詳細(xì)內(nèi)容,更多關(guān)于Java關(guān)閉線程 線程池的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot啟動(dòng)不加載bootstrap.yml文件的問題
這篇文章主要介紹了springboot啟動(dòng)不加載bootstrap.yml文件的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12Java FileDescriptor總結(jié)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
FileDescriptor 是“文件描述符”??梢员挥脕肀硎鹃_放文件、開放套接字等。接下來通過本文給大家分享Java FileDescriptor總結(jié),感興趣的朋友一起學(xué)習(xí)吧2017-05-05SpringMVC實(shí)現(xiàn)RESTful風(fēng)格:@PathVariable注解的使用方式
這篇文章主要介紹了SpringMVC實(shí)現(xiàn)RESTful風(fēng)格:@PathVariable注解的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11利用javaFX實(shí)現(xiàn)移動(dòng)一個(gè)小球的示例代碼
這篇文章主要介紹了利用javaFX實(shí)現(xiàn)移動(dòng)一個(gè)小球的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09IDEA中編寫并運(yùn)行shell腳本的實(shí)現(xiàn)
這篇文章主要介紹了IDEA中編寫并運(yùn)行shell腳本的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Java使用jni清屏功能的實(shí)現(xiàn)(只針對(duì)cmd)
JNI是Java Native Interface的縮寫,它提供了若干的API實(shí)現(xiàn)了Java和其他語言的通信(主要是C&C++)。這篇文章主要介紹了Java使用jni清屏功能的實(shí)現(xiàn)(只針對(duì)cmd) ,感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧2018-05-05