淺析在Java中如何優(yōu)雅的停止一個線程
寫在開頭
經(jīng)過上幾篇博文的學(xué)習(xí),我們知道在Java中可以通過new Thread().start()創(chuàng)建一個線程,那今天我們就來思考另外一個問題:線程的終止
自然終止有兩種情況:
1. 線程的任務(wù)執(zhí)行完成;
2. 線程在執(zhí)行任務(wù)過程中發(fā)生異常。
start之后,如果線程沒有走到終止狀態(tài),我們該如何停止這個線程呢?
為什么stop終止不可用
翻看Thread源碼后,發(fā)現(xiàn)其提供過一個stop()方法,可以用來終止線程,我們看一下它的源碼。
【源碼解析1】
@Deprecated public final void stop() { SecurityManager security = System.getSecurityManager(); if (security != null) { checkAccess(); if (this != Thread.currentThread()) { security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); } } // A zero status value corresponds to "NEW", it can't change to // not-NEW because we hold the lock. if (threadStatus != 0) { resume(); // Wake up thread if it was suspended; no-op otherwise } // The VM can handle all thread states stop0(new ThreadDeath()); }
這個方法使用了@Deprecated修飾,代表著它是廢棄的方法,在Java的編碼規(guī)約中,過時的方法不建議繼續(xù)使用,并且在這個方法的注釋中官方也提示說這是一個不安全的強制惡意中斷方法,會破壞線程的原子性
因此,在這里強烈建議大家不要再用stop方法去停止線程了!
如何優(yōu)雅的停止一個線程
我們知道線程只有從 runnable 狀態(tài)(可運行/運行狀態(tài)) 才能進入terminated 狀態(tài)(終止狀態(tài)),如果線程處于 blocked、waiting、timed_waiting 狀態(tài)(休眠狀態(tài)),就需要通過 Thread 類的 interrupt() 方法,讓線程從休眠狀態(tài)進入 runnable 狀態(tài),從而結(jié)束線程。
這里就涉及到了一個概念“線程中斷”,這是一種協(xié)作機制,當其他線程通知需要被中斷的線程后,線程中斷的狀態(tài)被設(shè)置為 true,但是具體被要求中斷的線程要怎么處理,完全由被中斷線程自己決定,可以在合適的時機中斷請求,也可以完全不處理繼續(xù)執(zhí)行下去,這樣一來,安全性就得到了保障。
Thread類中提供線程中斷的方法如下:
- Thread.interrupt():中斷線程。這里的中斷線程并不會立即停止線程,而是設(shè)置線程的中斷狀態(tài)為 true(默認是 flase);
- Thread.currentThread().isInterrupted():測試當前線程是否被中斷。線程的中斷狀態(tài)會受這個方法的影響,調(diào)用一次可以使線程中斷狀態(tài)變?yōu)?true,調(diào)用兩次會使這個線程的中斷狀態(tài)重新轉(zhuǎn)為 false;
- Thread.isInterrupted():測試當前線程是否被中斷。與上面方法不同的是調(diào)用這個方法并不會影響線程的中斷狀態(tài)。
Ok,寫了那么多,我們來寫一個小的demo測試一下線程中斷的方法。
【代碼示例】
public class Test { public static void main(String[] args) { //測試系統(tǒng)監(jiān)控器 testSystemMonitor(); } /** * 測試系統(tǒng)監(jiān)控器 */ public static void testSystemMonitor() { SystemMonitor sm = new SystemMonitor(); sm.start(); try { //運行 10 秒后停止監(jiān)控 Thread.sleep(10 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("監(jiān)控任務(wù)啟動 10 秒后,停止..."); sm.stop(); } } /*系統(tǒng)監(jiān)控器*/ class SystemMonitor { private Thread t; //線程中斷標識 private volatile boolean stop = false; /** * 啟動一個線程監(jiān)控系統(tǒng) */ void start() { t = new Thread(() -> { while (!stop) {//判斷當前線程是否被打斷 System.out.println("正在監(jiān)控系統(tǒng)..."); try { Thread.sleep(3 * 1000L);//執(zhí)行 3 秒 System.out.println("任務(wù)執(zhí)行 3 秒"); System.out.println("監(jiān)控的系統(tǒng)正常!"); } catch (InterruptedException e) { System.out.println("任務(wù)執(zhí)行被中斷..."); //重新設(shè)置線程為中斷狀態(tài),保證JVM拋異常情況下,中斷狀態(tài)仍為true。 Thread.currentThread().interrupt(); } } }); t.start(); } //線程中斷 void stop() { stop = true; t.interrupt(); } }
在這里我們先創(chuàng)建了一個SystemMonitor類作為系統(tǒng)檢測器,每3秒一循環(huán)的進行檢測,考慮到在Thread.currentThread().isInterrupted()可能在某些情況下中斷失效,所以我們這里自定義一個stop變量,作為線程中斷的標識,檢測線程啟動先對標識位進行判斷。
然后,我們在Test類中寫一個測試方法,調(diào)用這個系統(tǒng)監(jiān)控器,進行檢測,并設(shè)置10秒后,調(diào)用stop方法中斷檢測線程,將中斷標識stop設(shè)置為true。啟動代碼后,我們在控制臺可以看到這樣的輸出:
正在監(jiān)控系統(tǒng)...
任務(wù)執(zhí)行 3 秒
監(jiān)控的系統(tǒng)正常!
正在監(jiān)控系統(tǒng)...
任務(wù)執(zhí)行 3 秒
監(jiān)控的系統(tǒng)正常!
正在監(jiān)控系統(tǒng)...
任務(wù)執(zhí)行 3 秒
監(jiān)控的系統(tǒng)正常!
正在監(jiān)控系統(tǒng)...
監(jiān)控任務(wù)啟動 10 秒后,停止...
任務(wù)執(zhí)行被中斷...
與我們的預(yù)期一樣,監(jiān)控線程在執(zhí)行了3個循環(huán)的檢測任務(wù)后,被成功中斷調(diào)。到這里,我們就成功的、安全的、優(yōu)雅的停止了一個線程啦!
到此這篇關(guān)于淺析在Java中如何優(yōu)雅的停止一個線程的文章就介紹到這了,更多相關(guān)Java停止線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring+SpringMVC+JDBC實現(xiàn)登錄的示例(附源碼)
這篇文章主要介紹了Spring+SpringMVC+JDBC實現(xiàn)登錄的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05SpringMVC Mybatis配置多個數(shù)據(jù)源并切換代碼詳解
這篇文章主要介紹了SpringMVC Mybatis配置多個數(shù)據(jù)源并切換代碼詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-11-11SpringBoot利用filter實現(xiàn)xss防御功能
Cross-Site?Scripting(跨站腳本攻擊)簡稱?XSS,是一種代碼注入攻擊,攻擊者通過在目標網(wǎng)站上注入惡意腳本,使之在用戶的瀏覽器上運行,利用這些惡意腳本,攻擊者可獲取用戶的敏感信息,本文給大家介紹了SpringBoot利用filter實現(xiàn)xss防御功能,需要的朋友可以參考下2024-09-09SparkStreaming-Kafka通過指定偏移量獲取數(shù)據(jù)實現(xiàn)
這篇文章主要為大家介紹了SparkStreaming-Kafka通過指定偏移量獲取數(shù)據(jù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06基于Redis實現(xiàn)分布式應(yīng)用限流的方法
本篇文章主要介紹了基于 Redis 實現(xiàn)分布式應(yīng)用限流的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12mybatis實現(xiàn)對數(shù)據(jù)的增刪查改實例詳解
這篇文章主要介紹了mybatis實現(xiàn)對數(shù)據(jù)的增刪查改實例詳解的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-07-07出現(xiàn)次數(shù)超過一半(50%)的數(shù)
給出n個數(shù),需要我們找出出現(xiàn)次數(shù)超過一半的數(shù),下面小編給大家分享下我的實現(xiàn)思路及關(guān)鍵代碼,感興趣的朋友一起學(xué)習(xí)吧2016-07-07