Java線程中斷機(jī)制interrupt、isInterrupted、interrupted方法詳解
什么是中斷
- 一個線程不應(yīng)該由其他線程來強(qiáng)制中斷或停止,而是應(yīng)該由線程自己自行停止,所以,Thread.stop、Thread.suspend、Thread. resume都已經(jīng)被廢棄了
- 在Java中沒有辦法立即停止一條線程,然而停止線程卻顯得尤為重要,如取消一個耗時操作。因此,Java提供了一種用于停止線程的機(jī)制 — 中斷
- 中斷只是一種協(xié)作機(jī)制,Java沒有給中斷增加任何語法,中斷的過程完全需要程序員自己實(shí)現(xiàn)
- 若要中斷一個線程,你需要手動調(diào)用該線程的interrupt方法,該方法也僅僅是將線程對象的中斷標(biāo)識設(shè)為true
- 每個線程對象中都有一個標(biāo)識,用于標(biāo)識線程是否被中斷;該標(biāo)識位為true表示中斷,為false表示未中斷,通過調(diào)用線程對象的interrupt方法將線程的標(biāo)識位設(shè)為true,可以在別的線程中調(diào)用,也可以在自己的線程中調(diào)用
源碼解讀(中斷的相關(guān)API)
void interrupt( )實(shí)例方法
interrupt( )僅僅是設(shè)置線程的中斷狀態(tài)為true,不會停止線程
源碼解讀(如果這個線程因?yàn)閣ait()、join()、sleep()方法在用的過程中被打斷(interupt),會拋出Interrupte dException)
boolean isInterrupted( )實(shí)例方法
判斷當(dāng)前線程是否被中斷(通過檢查中斷標(biāo)識位) 實(shí)例方法
static boolean interrupted( )靜態(tài)方法
判斷線程是否被中斷,并清楚當(dāng)前中斷狀態(tài),這個方法做了兩件事 第一件事 返回當(dāng)前線程的中斷狀態(tài)
第二件事 將當(dāng)前線程的中斷狀態(tài)設(shè)為false
原理:假設(shè)有兩個線程A、B,線程B調(diào)用了interrupt方法,這個時候我們連續(xù)調(diào)用兩次isInterrupted方法,第一次會返回true,然后這個方法會將中斷標(biāo)識位設(shè)置位false,所以第二次調(diào)用將返回false
比較靜態(tài)方法interrupted和實(shí)例方法isInterrupted 靜態(tài)方法interrupted將會清除中斷狀態(tài)(傳入的參數(shù)ClearInterrupted位true)實(shí)例方法isInterrupted則不會(傳入的參數(shù)ClearInterrupted為false)
如何使用中斷標(biāo)識停止線程
在需要中斷的線程中不斷監(jiān)聽中斷狀態(tài),一旦發(fā)生中斷,就執(zhí)行型對于的中斷處理業(yè)務(wù)邏輯
三種中斷標(biāo)識停止線程的方式
- 通過一個volatile變量實(shí)現(xiàn)
- 通過AtomicBoolean
- 通過Thread類自帶的中斷API方法實(shí)現(xiàn)
public class InterruptDemo{ static volatile boolean isStop = false; static AtomicBoolean atomicBoolean = new AtomicBoolean(false); public static void m3(){ Thread t1 = new Thread(() -> { while (true) { if (Thread.currentThread().isInterrupted()) { System.out.println("-----isInterrupted() = true,程序結(jié)束。"); break; } System.out.println("------hello Interrupt"); } }, "t1"); t1.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { t1.interrupt();//修改t1線程的中斷標(biāo)志位為true },"t2").start(); } /** * 通過AtomicBoolean */ public static void m2(){ new Thread(() -> { while(true) { if(atomicBoolean.get()) { System.out.println("-----atomicBoolean.get() = true,程序結(jié)束。"); break; } System.out.println("------hello atomicBoolean"); } },"t1").start(); //暫停幾秒鐘線程 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { atomicBoolean.set(true); },"t2").start(); } /** * 通過一個volatile變量實(shí)現(xiàn) */ public static void m1(){ new Thread(() -> { while(true) { if(isStop) { System.out.println("-----isStop = true,程序結(jié)束。"); break; } System.out.println("------hello isStop"); } },"t1").start(); //暫停幾秒鐘線程 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { isStop = true; },"t2").start(); } }
當(dāng)前線程的中斷標(biāo)識為true,是不是就立刻停止?
1、線程調(diào)用interrupt()時①. 如果線程處于正常活動狀態(tài),那么會將線程的中斷標(biāo)志設(shè)置位true,僅此而已。被設(shè)置中斷標(biāo)志的線程將繼續(xù)正常運(yùn)行,不受影響。所以,interrupt( )并不能真正的中斷線程,需要被調(diào)用的線程自己進(jìn)行配合才行
如果線程處于被阻塞狀態(tài)(例如處于sleep、wait、join等狀態(tài)),在別的線程中調(diào)用當(dāng)前線程對象的interrupt方法,那么線程立即被阻塞狀態(tài),并拋出一個InterruptedException異常)
2、中斷只是一種協(xié)同機(jī)制,修改中斷標(biāo)識位僅此而已,不是立即stop打斷
3 、sleep、wait 等方法拋出InterruptedException后,中斷標(biāo)識也被清空置為false,我們在catch沒有通過調(diào)用th.interrupt( )方法再次將中斷標(biāo)識位設(shè)置位true,這就是導(dǎo)致無限循環(huán)了
總結(jié)
package com.bilibili.juc.interrupt; import java.util.concurrent.TimeUnit; /** * @auther zzyy * @create 2022-01-20 10:52 */ public class InterruptDemo3 { public static void main(String[] args) { Thread t1 = new Thread(() -> { while (true) { if(Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName()+"\t " + "中斷標(biāo)志位:"+Thread.currentThread().isInterrupted()+" 程序停止"); break; } try { Thread.sleep(200); } catch (InterruptedException e) { Thread.currentThread().interrupt();//為什么要在異常處,再調(diào)用一次?? e.printStackTrace(); } System.out.println("-----hello InterruptDemo3"); } }, "t1"); t1.start(); //暫停幾秒鐘線程 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> t1.interrupt(),"t2").start(); } } /** * 1 中斷標(biāo)志位,默認(rèn)false * 2 t2 ----> t1發(fā)出了中斷協(xié)商,t2調(diào)用t1.interrupt(),中斷標(biāo)志位true * 3 中斷標(biāo)志位true,正常情況,程序停止,^_^ * 4 中斷標(biāo)志位true,異常情況, 就是t1過程中調(diào)用了sleep 、join 、 wait 方法 會拋出一個 InterruptedException(中斷異常), * 將會把中斷狀態(tài)將被清除,并且將收到InterruptedException 。中斷標(biāo)志位重新設(shè)置為false 導(dǎo)致無限循環(huán) * * 5 所以如果要線程中調(diào)用sleep 、join 、 wait 方法 需要在catch塊中,需要再次給中斷標(biāo)志位設(shè)置為true,2次調(diào)用停止程序才OK */
到此這篇關(guān)于Java線程中斷機(jī)制interrupt、isInterrupted、interrupted方法詳解的文章就介紹到這了,更多相關(guān)Java線程中斷機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
比較java中Future與FutureTask之間的關(guān)系
在本篇文章里我們給大家分享了java中Future與FutureTask之間的關(guān)系的內(nèi)容,有需要的朋友們可以跟著學(xué)習(xí)下。2018-10-10MyBatis-Plus 批量插入數(shù)據(jù)的操作方法
spring boot+mybatis plus環(huán)境,單條插入用的是BaseMapper自帶的insert方法,本文重點(diǎn)給大家介紹MyBatis-Plus 批量插入數(shù)據(jù)的操作方法,感興趣的朋友一起看看吧2021-09-09Java運(yùn)行時動態(tài)生成類實(shí)現(xiàn)過程詳解
這篇文章主要介紹了Java運(yùn)行時動態(tài)生成類實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07