Java多線程之Interrupt中斷線程詳解
一、測(cè)試代碼
二、測(cè)試
為了方便理解簡(jiǎn)介中 interrupt 的概念, 寫(xiě)個(gè) DEMO 測(cè)試一下
/**
* 調(diào)用 interrupt 并不會(huì)影響線程正常運(yùn)行
*/
@Test
public void testInvokeInterrupt() throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; ; i++) {
log.info(i + "");
}
});
t1.start();
// 確保 t1.start() 成功執(zhí)行
Thread.sleep(1);
log.info("interrupt 前 t1 interrupt 狀態(tài) = {}", t1.isInterrupted());
t1.interrupt();
log.info("interrupt 后 t1 interrupt 狀態(tài) = {}", t1.isInterrupted());
log.info("t1 是否存活 = {}", t1.isAlive());
}
三、執(zhí)行過(guò)程描述
- 首先 main 線程中啟動(dòng) t1線程
- t1 線程死循環(huán)輸出 i++
- main 線程確保 t1.start() 執(zhí)行后
- 打印 t1 線程的線程中斷狀態(tài)
- 調(diào)用 t1.interrupt() 方法使線程中斷
- 打印 t1 線程的線程中斷狀態(tài)
四、輸出日志
ignore logs ......
20:29:57.632 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - 2561
20:29:57.633 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - 2562
20:29:57.633 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - 2563
20:29:57.486 [main] INFO cn.diswares.blog.interrupt.InterruptTests - interrupt 前 t1 interrupt 狀態(tài) = false
20:29:57.633 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - 2564
20:29:57.633 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - 2565
20:29:57.633 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - 2566
20:29:57.633 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - 2567
20:29:57.633 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - 2568
20:29:57.633 [main] INFO cn.diswares.blog.interrupt.InterruptTests - interrupt 后 t1 interrupt 狀態(tài) = true
20:29:57.633 [main] INFO cn.diswares.blog.interrupt.InterruptTests - t1 是否存活 = true
20:29:57.633 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - 2569
20:29:57.633 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - 2570
20:29:57.633 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - 2571
ignore logs ......
現(xiàn)象描述
- 調(diào)用 t1.interrupt() 執(zhí)行前線程的 interrupt 狀態(tài)為 false
- 調(diào)用 t1.interrupt() 執(zhí)行后線程的 interrupt 狀態(tài)為 true
- 線程并沒(méi)有被中斷, 可以成功死循環(huán)輸出循環(huán)次數(shù)
五、結(jié)論
Interrupt 的真正作用是給線程對(duì)象設(shè)置一個(gè)中斷標(biāo)記, 并不會(huì)影響線程的正常運(yùn)行
六、主要方法釋義
new Thread().interrupt()
中斷此線程(此線程不一定是當(dāng)前線程,而是指調(diào)用該方法的Thread實(shí)例所代表的線程),但實(shí)際上只是給線程設(shè)置一個(gè)中斷標(biāo)志,線程仍會(huì)繼續(xù)運(yùn)行。
Thread.interrupted()
注意: 這是個(gè)靜態(tài)方法
測(cè)試當(dāng)前線程是否被中斷(檢查中斷標(biāo)志), 返回一個(gè)當(dāng)前線程的 interrupt 狀態(tài), 并重置.
當(dāng)我們第二次調(diào)用時(shí)中斷狀態(tài)已經(jīng)被重置, 將返回一個(gè)false
為了方便理解. 寫(xiě)一個(gè) DEMO
七、DEMO
DEMO 非常簡(jiǎn)單, 調(diào)用兩次 Thread.interrupted() 觀察 main 線程的 interrupt 標(biāo)記
/**
* 二次調(diào)用 t1.interrupted()
*/
@Test
public void testDoubleInvokeInterrupted () throws InterruptedException {
Thread.currentThread().interrupt();
log.info("interrupted1 = {}", Thread.interrupted());
log.info("interrupted2 = {}", Thread.interrupted());
}
輸出日志
21:06:33.397 [main] INFO cn.diswares.blog.interrupt.InterruptTests - interrupted1 = true
21:06:33.402 [main] INFO cn.diswares.blog.interrupt.InterruptTests - interrupted2 = false
八、拓展程序
由于是靜態(tài)方法. 我們來(lái)看一下另一個(gè)小程序.
- 跟之前一樣將 t1 程序中斷
- 調(diào)用 t1.interrupted()
- 注意這里是個(gè)靜態(tài)方法
/**
* 在主線程中調(diào)用 t1.interrupted()
*/
@Test
public void testMainInterrupted() throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; ; i++) {
log.info("t1 is live");
}
});
t1.start();
Thread.sleep(1);
t1.interrupt();
Thread.sleep(1);
log.info("{}", t1.interrupted());
}
拓展程序日志
ignore logs ......
21:11:20.504 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - t1 is live
21:11:20.504 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - t1 is live
21:11:20.490 [main] INFO cn.diswares.blog.interrupt.InterruptTests - false
21:11:20.504 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - t1 is live
21:11:20.504 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - t1 is live
21:11:20.504 [Thread-1] INFO cn.diswares.blog.interrupt.InterruptTests - t1 is live
ignore logs ......
拓展程序結(jié)論
- Thread.interrupted() 方法是靜態(tài)方法
- 它的實(shí)現(xiàn)為 Thread.currentThread(), 獲取的是當(dāng)前正在執(zhí)行的線程, JDK 原文注釋如下
Returns a reference to the currently executing thread object.
Returns: the currently executing thread.
- 所以這里 t1.interrupted() 返回的其實(shí)是 main 線程的線程中斷標(biāo)記
new Thread().isInterrupted()
返回線程對(duì)象的中斷標(biāo)記, 不會(huì)改變中斷標(biāo)記
- true: 中斷標(biāo)記存在
- false: 未設(shè)置中斷標(biāo)記狀態(tài)
優(yōu)雅的結(jié)束一個(gè)線程
在 Java 中結(jié)束一個(gè)線程一般有下面三種手段:
- (禁用) Thread.stop() 這個(gè)方法已經(jīng)被廢棄. 因?yàn)檫@種結(jié)束線程的方式過(guò)于暴力. 會(huì)將當(dāng)前線程暴力終結(jié). 同時(shí)線程持有的鎖也都會(huì)釋放, 并且用戶有任何額外的處理來(lái)控制, 會(huì)導(dǎo)致數(shù)據(jù)不一致
- volatile: 外部申明 volatile 開(kāi)關(guān)變量, 當(dāng)開(kāi)關(guān)條件不滿足時(shí)結(jié)束
- (推薦) interrupt: 最優(yōu)雅的方案
九、實(shí)戰(zhàn)
最初的 DEMO 是個(gè)死循環(huán), 那我們對(duì)其改造一下. 讓它能夠優(yōu)雅的結(jié)束
/**
* 調(diào)用 interrupt 并不會(huì)影響線程正常運(yùn)行
*/
@Test
public void testGracefulEndThread() throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; ; i++) {
if (Thread.currentThread().isInterrupted()) {
log.info("{} = true, i = {}", Thread.currentThread().getName(), i);
break;
} else {
log.info("{} = false, i = {}", Thread.currentThread().getName(), i);
}
}
});
t1.start();
// 確保 t1.start() 成功執(zhí)行
TimeUnit.SECONDS.sleep(1);
t1.interrupt();
TimeUnit.SECONDS.sleep(1);
log.info(t1.getState().toString());
}
到此這篇關(guān)于Java多線程之Interrupt中斷線程詳解的文章就介紹到這了,更多相關(guān)Java Interrupt中斷線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
HttpServletRequest對(duì)象方法的用法小結(jié)
HttpServletRequest對(duì)象代表客戶端的請(qǐng)求,當(dāng)客戶端通過(guò)HTTP協(xié)議訪問(wèn)服務(wù)器時(shí),HTTP請(qǐng)求頭中的所有信息都封裝在這個(gè)對(duì)象中,開(kāi)發(fā)人員通過(guò)這個(gè)對(duì)象的相關(guān)方法,即可以獲得客戶的這些信息2017-03-03
springboot中使用@NotNull注解無(wú)效解決方法
這篇文章主要給大家介紹了關(guān)于springboot中使用@NotNull注解無(wú)效的解決方法,進(jìn)行參數(shù)校驗(yàn)的時(shí)候,加了@NotNull注解,@Validated注解和@Valid注解,但是參數(shù)校驗(yàn)的時(shí)候不生效,需要的朋友可以參考下2023-08-08
Java使用wait/notify實(shí)現(xiàn)線程間通信下篇
wait()和notify()是直接隸屬于Object類(lèi),也就是說(shuō)所有對(duì)象都擁有這一對(duì)方法,下面這篇文章主要給大家介紹了關(guān)于使用wait/notify實(shí)現(xiàn)線程間通信的相關(guān)資料,需要的朋友可以參考下2022-12-12
SpringBoot結(jié)果封裝和異常攔截的實(shí)現(xiàn)示例
SpringBoot 項(xiàng)目中,我們通常需要將結(jié)果數(shù)據(jù)封裝成特定的格式,以方便客戶端進(jìn)行處理,本文主要介紹了SpringBoot?優(yōu)雅的結(jié)果封裝和異常攔截,感興趣的可以了解一下2023-08-08
Java Date類(lèi)常用示例_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
在JDK1.0中,Date類(lèi)是唯一的一個(gè)代表時(shí)間的類(lèi),但是由于Date類(lèi)不便于實(shí)現(xiàn)國(guó)際化,所以從JDK1.1版本開(kāi)始,推薦使用Calendar類(lèi)進(jìn)行時(shí)間和日期處理。這里簡(jiǎn)單介紹一下Date類(lèi)的使用,需要的朋友可以參考下2017-05-05
Spring Boot集成MyBatis訪問(wèn)數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了Spring Boot集成MyBatis訪問(wèn)數(shù)據(jù)庫(kù)的方法,需要的朋友可以參考下2017-04-04

