Java多線程之Interrupt中斷線程詳解
一、測試代碼
https://gitee.com/zture/spring-test/blob/master/multithreading/src/test/java/cn/diswares/blog/InterruptTests.java
二、測試
為瞭方便理解簡介中 interrupt 的概念, 寫個 DEMO 測試一下
/** * 調用 interrupt 並不會影響線程正常運行 */ @Test public void testInvokeInterrupt() throws InterruptedException { Thread t1 = new Thread(() -> { for (int i = 0; ; i++) { log.info(i + ""); } }); t1.start(); // 確保 t1.start() 成功執行 Thread.sleep(1); log.info("interrupt 前 t1 interrupt 狀態 = {}", t1.isInterrupted()); t1.interrupt(); log.info("interrupt 後 t1 interrupt 狀態 = {}", t1.isInterrupted()); log.info("t1 是否存活 = {}", t1.isAlive()); }
三、執行過程描述
- 首先 main 線程中啟動 t1線程
- t1 線程死循環輸出 i++
- main 線程確保 t1.start() 執行後
- 打印 t1 線程的線程中斷狀態
- 調用 t1.interrupt() 方法使線程中斷
- 打印 t1 線程的線程中斷狀態
四、輸出日志
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 狀態 = 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 狀態 = 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 ……
現象描述
- 調用 t1.interrupt() 執行前線程的 interrupt 狀態為 false
- 調用 t1.interrupt() 執行後線程的 interrupt 狀態為 true
- 線程並沒有被中斷, 可以成功死循環輸出循環次數
五、結論
Interrupt 的真正作用是給線程對象設置一個中斷標記, 並不會影響線程的正常運行
六、主要方法釋義
new Thread().interrupt()
中斷此線程(此線程不一定是當前線程,而是指調用該方法的Thread實例所代表的線程),但實際上隻是給線程設置一個中斷標志,線程仍會繼續運行。
Thread.interrupted()
註意: 這是個靜態方法
測試當前線程是否被中斷(檢查中斷標志), 返回一個當前線程的 interrupt 狀態, 並重置.
當我們第二次調用時中斷狀態已經被重置, 將返回一個false
為瞭方便理解. 寫一個 DEMO
七、DEMO
DEMO 非常簡單, 調用兩次 Thread.interrupted() 觀察 main 線程的 interrupt 標記
/** * 二次調用 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
八、拓展程序
由於是靜態方法. 我們來看一下另一個小程序.
- 跟之前一樣將 t1 程序中斷
- 調用 t1.interrupted()
- 註意這裡是個靜態方法
/** * 在主線程中調用 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 ……
拓展程序結論
- Thread.interrupted() 方法是靜態方法
- 它的實現為 Thread.currentThread(), 獲取的是當前正在執行的線程, JDK 原文註釋如下
Returns a reference to the currently executing thread object.
Returns: the currently executing thread.
- 所以這裡 t1.interrupted() 返回的其實是 main 線程的線程中斷標記
new Thread().isInterrupted()
返回線程對象的中斷標記, 不會改變中斷標記
- true: 中斷標記存在
- false: 未設置中斷標記狀態
優雅的結束一個線程
在 Java 中結束一個線程一般有下面三種手段:
- (禁用) Thread.stop() 這個方法已經被廢棄. 因為這種結束線程的方式過於暴力. 會將當前線程暴力終結. 同時線程持有的鎖也都會釋放, 並且用戶有任何額外的處理來控制, 會導致數據不一致
- volatile: 外部申明 volatile 開關變量, 當開關條件不滿足時結束
- (推薦) interrupt: 最優雅的方案
九、實戰
最初的 DEMO 是個死循環, 那我們對其改造一下. 讓它能夠優雅的結束
/** * 調用 interrupt 並不會影響線程正常運行 */ @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() 成功執行 TimeUnit.SECONDS.sleep(1); t1.interrupt(); TimeUnit.SECONDS.sleep(1); log.info(t1.getState().toString()); }
到此這篇關於Java多線程之Interrupt中斷線程詳解的文章就介紹到這瞭,更多相關Java Interrupt中斷線程內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- java中斷機制實例講解
- 淺談Thread.sleep()為什麼要拋出中斷異常
- Java線程中斷interrupt的常用方法
- Java並發編程之線程中斷
- java高並發InterruptedException異常引發思考