Java线程中断机制-如何中断线程
Java線程中斷機制-如何中斷線程
?版權(quán)聲明:本文為博主原創(chuàng)文章,歡迎指正或者轉(zhuǎn)載。 https://blog.csdn.net/qq_38663729/article/details/78232648
介紹:
對于線程一共分為五個狀態(tài):新建狀態(tài),就緒狀態(tài),阻塞狀態(tài),運行狀態(tài),死亡狀態(tài),有時候把阻塞狀態(tài)又分為同步阻塞和等待阻塞。
有時想讓主線程啟動的一個子線程結(jié)束運行,我們就需要讓這個子線程中斷,不再繼續(xù)執(zhí)行。線程是有中斷機制的,我們可以對每個線程進行中斷標記,注意只是標記,中斷與否還是虛擬機自己的事情,虛擬機自己家的事情,我們也就說說,不能實際操作控制他家。java中的Thread類是一個對線程進行操作的類,提供了中斷線程的方法interrupt(),在API中是這么定義的(中文的翻譯可能不準確)。
在Thread中其實還有一個stop()方法也是中斷線程的方法,但是這個方法太粗魯了,人家好好的線程正在運行,只要調(diào)用了stop()方法,線程就會中斷,不管是在進行什么操作。這個方法已經(jīng)被廢棄,他的執(zhí)行會帶來一些不可確定的狀況。
線程可以調(diào)用中斷自己的方法interrupt()方法,但是中斷也是有條件的,在線程調(diào)用interrupt()方法的時候虛擬機會在此線程上標記一個標志(這個中斷標志只是一個布爾類型的變量),代表這個線程可能被中斷,在后面的中斷操作也是根據(jù)這個中斷標志執(zhí)行的。可以說interrupt()方法是一種友好的方法,總是和虛擬機商量著來。如果一個線程處于了阻塞狀態(tài)(如線程調(diào)用了sleep()、join()、wait()、以及可中斷的通道上的 I/O 操作方法后可進入阻塞狀態(tài)),則在線程在檢查中斷標示時如果發(fā)現(xiàn)中斷標示為true,則會在這些阻塞方法(sleep()、join()、wait()及可中斷的通道上的 I/O 操作方法)調(diào)用處拋出InterruptedException異常,并且在拋出異常后立即將線程的中斷標示位清除,即重新設(shè)置為false。拋出異常是為了線程從阻塞狀態(tài)醒過來,并在結(jié)束線程前讓程序員有足夠的時間來處理中斷請求。
線程的一些狀態(tài)都可能影響到這個中斷標記從而結(jié)束中斷。下面這個例子驗證中斷標志被除掉的說法
package demo_thread;
public class Interrupt {
?public static void main(String[] args) {
? ?A a = new A();
? ?Thread t1 = new Thread(a);
? ?t1.start();
? ?t1.interrupt();
? ?System.out.println("執(zhí)行睡眠之前1:"+t1.isInterrupted());
? ?try {
? ? ?System.out.println("執(zhí)行睡眠之前2:"+t1.isInterrupted());
? ? ?t1.sleep(1000);//線程進入阻塞狀態(tài)
? ?} catch (InterruptedException e) {
? ? ?e.printStackTrace();
? ?}finally {
? ? System.out.println("執(zhí)行睡眠之后:"+t1.isInterrupted());
? ?}
?}
}
class A implements Runnable{
?@Override
?public void run() {
? ?System.out.println(Thread.currentThread());
?}
}
在main方法中創(chuàng)建一個線程,線程中重寫的run()方法輸出線程號,只是一個顯示分隔,具體看在執(zhí)行讓線程阻塞方法之前和之后線程的中斷標記。isInterrupted()方法是判斷線程是否執(zhí)行中斷的方法,就是判斷中斷標記是否存在的方法。
輸出:
執(zhí)行睡眠之前1:true 執(zhí)行睡眠之前2:true Thread[Thread-0,5,main] 執(zhí)行睡眠之后:false可以看到只要執(zhí)行了sleep()方法,線程的中斷標記就會被清除。分別調(diào)用執(zhí)行了join()、wait()方法,可以看到他們的共同點都是需要捕獲一個InterruptedException,在調(diào)用join()方法的時候得到了和sleep()方法一樣的輸出結(jié)果,但是在調(diào)用wait()方法的時候,雖然在后面調(diào)用notify()方法,但是是否獲得執(zhí)行權(quán)限還是無法預料的。
中斷一個線程只是為了引起該線程的注意,被中斷線程可以決定如何應(yīng)對中斷,中斷或者不中斷。某些線程非常重要,以至于它們應(yīng)該不理會中斷,而是在處理完拋出的異常之后繼續(xù)執(zhí)行,但是更普遍的情況是,一個線程將把中斷看作一個終止請求。
兩個很像的方法的區(qū)別:
interrupted():測試當前線程是否中斷。 該方法可以清除線程的中斷狀態(tài) 。換句話說,如果這個方法被連續(xù)調(diào)用兩次,那么第二個調(diào)用將返回false(除非當前線程再次中斷,在第一個調(diào)用已經(jīng)清除其中斷狀態(tài)之后,在第二個調(diào)用之前已經(jīng)檢查過)。
isInterrupted():測試這個線程是否被中斷。線程的中斷狀態(tài)不受此方法的影響。
如何中斷線程:
接下來看請求中斷之后發(fā)生的事情:在網(wǎng)上看到一個例子
package demo_thread;
?public class Interrupt {
? ?public static void main(String[] args) {
? ? ?try {
? ? ? MyThread thread = new MyThread();
? ? ? thread.start();
? ? ? Thread.sleep(2000);
? ? ? thread.interrupt();//請求中斷MyThread線程
? ? ?} catch (Exception e) {
? System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
class MyThread extends Thread {
?@Override
?public void run() {
? ?super.run();
? ?for (int i = 0; i < 500000; i++) {
? ? ?if (this.interrupted()) {
? ? ? ?System.out.println("should be stopped and exit");
? ? ? ?break;
? ? ?}
? ? System.out.println("i=" + (i + 1));
? ?}
? //盡管線程被中斷,但并沒有結(jié)束運行。這行代碼還是會被執(zhí)行
? System.out.println("this line is also executed. thread does not stopped");
? }
}
在文章的開頭處已經(jīng)說了,線程執(zhí)行中斷方法,只是給線程一個中斷標記,是否中斷是他自己的事
其中一次的輸出:
由于線程執(zhí)行了sleep()方法,進入阻塞狀態(tài),所以在從阻塞狀態(tài)從新回到運行狀態(tài)的時候,發(fā)現(xiàn)自己已經(jīng)有了中斷標記,執(zhí)行if中的語句,線程的睡眠和執(zhí)行總是和計算機內(nèi)部有關(guān)系,所以每次輸出的結(jié)束數(shù)字總是不一樣的。
通過輸出結(jié)果我們發(fā)現(xiàn)一個很重要的問題,我們要的是線程的中斷,也就是關(guān)于線程的方法需要停止執(zhí)行,但是在循環(huán)外面的輸出語句也執(zhí)行了,這個中斷只是中斷了循環(huán)的執(zhí)行。
那么我們應(yīng)該怎么中斷線程呢?利用線程中斷異常就是一個很好的辦法,既然是異常,通常異常可以中斷線程的運行,使程序掛掉,我們自己拋出一個異常讓線程中斷
class MyThread extends Thread {
? ? ? @Override
? ? ? public void run() {
? ? ? ?super.run();
? ? ? ?try {
? ? ? ? ?for (int i = 0; i < 500000; i++) {
? ? ? ? ? ?if (this.interrupted()) {
? ? ? ? ? ? ?System.out.println("should be stopped and exit");
? ? ? ? ? ? ?throw new InterruptedException();
? ? ? ? ? ? }
? ? ? ? ? System.out.println("i=" + (i + 1));
? ? ? ? ?}
? ? System.out.println("this line cannot be executed. cause thread throws exception");//這行語句不會被執(zhí)行!!!
? ? ? ?} catch (InterruptedException e) {
? ? ? ? ? System.out.println("catch interrupted exception");
? ? ? ? ? e.printStackTrace();
? ? ?}
? }
}
輸出:
但是通過拋出異常也帶來了問題,我們不能通過拋出異常來處理問題,還需要在捕獲異常的代碼中加上處理的語句,例如保存用戶的操作等等,讓這次中斷看起來是很正常的中斷。
我們還可以通過讓程序主動拋出異常,在線程阻塞的時候,執(zhí)行中斷操作,拋出異常,在循環(huán)中循環(huán)打印當前時間,
package demo_thread;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SleepInterruptTest {
?public static void main(String[] args) {
? ? SleepThread t1 = new SleepThread();
? ? t1.start();
? ? try {
? ? ? Thread.sleep(5000);
? ? } catch (InterruptedException e) {
? ? ? e.printStackTrace();
? ? }
? ?t1.interrupt();//主動打斷線程,使SleepThread線程拋出異常
? }
}
class SleepThread extends Thread{
? ? public void run(){
? ? ? while(true){
? ? ? ?try{
? ? ? ? ?SimpleDateFormat sim = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
? ? ? ? ?System.out.println(sim.format(new Date()));
? ? ? ? ?sleep(1000);
? ? ? ? }
? ? ? ?catch(InterruptedException e){
? ? ? ?System.out.println("線程中斷");
? ? ? ?return;
? ? }
? ?}
? ?}
}
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的Java线程中断机制-如何中断线程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hi3516A开发--ethtool安装
- 下一篇: 前后端分离Java后端跨越问题解决