[并發(fā)J.U.C] 用例子理解線(xiàn)程中斷

前言

本文主要集中在Java線(xiàn)程的中斷的使用和操作上.
完整代碼:代碼

方法

關(guān)于中斷的有三個(gè)方法都在java.lang.thread下,分別如下:

方法 描述
public boolean isInterrupted() 判斷某個(gè)線(xiàn)程是否中斷
public static boolean interrupted() 判斷當(dāng)前線(xiàn)程是否中斷,與isInterrupted()的區(qū)別在于如果當(dāng)前線(xiàn)程中斷了,該方法在返回true后,會(huì)進(jìn)行復(fù)位(即把中斷標(biāo)識(shí)符設(shè)置為false)
public void interrupt() 中斷某個(gè)線(xiàn)程

再看一個(gè)例子之前先注意一點(diǎn):

1.如果某個(gè)線(xiàn)程已經(jīng)處于終結(jié)狀態(tài),即使該線(xiàn)程被中斷過(guò),在調(diào)用該線(xiàn)程對(duì)象的isInterrupted()方法會(huì)依舊返回false

例子1

通過(guò)該例子簡(jiǎn)單理解一下中斷的操作

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class Test1 {

    static CountDownLatch countdown = new CountDownLatch(1);
    //countdown 是為了保證mythread-1充分運(yùn)行完
    
    public static void main(String[] args) {
        Thread thread = new Thread(new Runner1(), "mythread-1");
        thread.start();
        try {
            TimeUnit.NANOSECONDS.sleep(1);
            thread.interrupt();
            TimeUnit.NANOSECONDS.sleep(1);
            thread.interrupt();
            countdown.await(); 
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        printThreadInfo(Thread.currentThread());
        printThreadInfo(thread);
        System.out.println("main ends!");
    }
    
    static void printThreadInfo(Thread thread) {
        System.out.println(thread.getName() + ", interrupte :" + thread.isInterrupted());
    }
    
    static class Runner1 implements Runnable {
        public void run() {
            System.out.println("Thread-name:" + Thread.currentThread().getName() + ", interrupted:" + Thread.currentThread().isInterrupted());
            int i = 0;
            while(true) {
                if (Thread.interrupted()) {
                    System.out.println(Thread.currentThread().getName() + " receives interrupt signal in first loop");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + " prints " + (i++) + " in first loop.");
            }
            System.out.println(Thread.currentThread().getName() + " does not stop!");
            printThreadInfo(Thread.currentThread());
            
            
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println(Thread.currentThread().getName() + " receives interrupt signal in second loop");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + " prints " + (i++) + " in second loop.");

            }
            printThreadInfo(Thread.currentThread());
            countdown.countDown();
        }
    }
}

首先定義了一個(gè)CountDownLatch為了保證讓mythread-1充分運(yùn)行后在運(yùn)main線(xiàn)程await()后面的語(yǔ)句.

線(xiàn)程mythread-1有兩個(gè)循環(huán),第一個(gè)用Thread.interrupted()來(lái)判斷當(dāng)前線(xiàn)程是否中斷,如果有中斷,它會(huì)進(jìn)行復(fù)位.第二個(gè)循環(huán)使用isInterrupted()來(lái)判斷當(dāng)前線(xiàn)程是否有中斷.

主線(xiàn)程main中調(diào)用了兩次interrupt()中斷操作來(lái)告訴mythread-1.

我的電腦上運(yùn)行結(jié)果如下:

Thread-name:mythread-1, interrupted:false
mythread-1 prints 0 in first loop.
mythread-1 prints 1 in first loop.
mythread-1 prints 2 in first loop.
mythread-1 prints 3 in first loop.
mythread-1 prints 4 in first loop.
mythread-1 prints 5 in first loop.
mythread-1 prints 6 in first loop.
mythread-1 prints 7 in first loop.
mythread-1 prints 8 in first loop.
mythread-1 prints 9 in first loop.
mythread-1 prints 10 in first loop.
mythread-1 prints 11 in first loop.
mythread-1 prints 12 in first loop.
mythread-1 prints 13 in first loop.
mythread-1 prints 14 in first loop.
mythread-1 prints 15 in first loop.
mythread-1 prints 16 in first loop.
mythread-1 prints 17 in first loop.
mythread-1 prints 18 in first loop.
mythread-1 prints 19 in first loop.
mythread-1 prints 20 in first loop.
mythread-1 prints 21 in first loop.
mythread-1 prints 22 in first loop.
mythread-1 prints 23 in first loop.
mythread-1 prints 24 in first loop.
mythread-1 prints 25 in first loop.
mythread-1 prints 26 in first loop.
mythread-1 prints 27 in first loop.
mythread-1 prints 28 in first loop.
mythread-1 prints 29 in first loop.
mythread-1 prints 30 in first loop.
mythread-1 prints 31 in first loop.
mythread-1 prints 32 in first loop.
mythread-1 prints 33 in first loop.
mythread-1 prints 34 in first loop.
mythread-1 prints 35 in first loop.
mythread-1 prints 36 in first loop.
mythread-1 prints 37 in first loop.
mythread-1 prints 38 in first loop.
mythread-1 prints 39 in first loop.
mythread-1 prints 40 in first loop.
mythread-1 prints 41 in first loop.
mythread-1 prints 42 in first loop.
mythread-1 prints 43 in first loop.
mythread-1 receives interrupt signal in first loop
mythread-1 does not stop!
mythread-1, interrupte :false
mythread-1 prints 44 in second loop.
mythread-1 prints 45 in second loop.
mythread-1 prints 46 in second loop.
mythread-1 prints 47 in second loop.
mythread-1 prints 48 in second loop.
mythread-1 prints 49 in second loop.
mythread-1 prints 50 in second loop.
mythread-1 prints 51 in second loop.
mythread-1 prints 52 in second loop.
mythread-1 prints 53 in second loop.
mythread-1 prints 54 in second loop.
mythread-1 prints 55 in second loop.
mythread-1 prints 56 in second loop.
mythread-1 prints 57 in second loop.
mythread-1 prints 58 in second loop.
mythread-1 prints 59 in second loop.
mythread-1 prints 60 in second loop.
mythread-1 prints 61 in second loop.
mythread-1 prints 62 in second loop.
mythread-1 prints 63 in second loop.
mythread-1 prints 64 in second loop.
mythread-1 prints 65 in second loop.
mythread-1 prints 66 in second loop.
mythread-1 prints 67 in second loop.
mythread-1 receives interrupt signal in second loop
mythread-1, interrupte :true
main, interrupte :false
mythread-1, interrupte :false
main ends!

從結(jié)果中可以看到主線(xiàn)程第一次調(diào)用thread.interrupt();的時(shí)候會(huì)mythread-1線(xiàn)程中的第一個(gè)循環(huán)利用Thread.interrupted()會(huì)檢查到中斷狀態(tài)已經(jīng)判斷為true然后會(huì)立馬復(fù)位為false,從跳出第一個(gè)循環(huán)后打印的中斷狀態(tài)信息就可以知道線(xiàn)程狀態(tài)已經(jīng)被復(fù)位為false了.

主線(xiàn)程第二次調(diào)用thread.interrupt();的時(shí)候會(huì)mythread-1線(xiàn)程中的第二個(gè)循環(huán)利用Thread.currentThread().isInterrupted()會(huì)檢查到中斷狀態(tài)會(huì)判斷為true,跳出循環(huán)后會(huì)發(fā)現(xiàn)這次并沒(méi)有復(fù)位.
所以從這個(gè)例子中就可以明顯區(qū)分interrupted()isInterrupted()了.

最后在主線(xiàn)程打印mythread-1的中斷狀態(tài)信息,返回的是false已經(jīng)該線(xiàn)程已經(jīng)處于終結(jié)狀態(tài)了.

不過(guò)從這個(gè)例子中也可以看到幾點(diǎn)疑惑:
1. 線(xiàn)程收到線(xiàn)程中斷信息后并沒(méi)有立刻停止線(xiàn)程,而是繼續(xù)運(yùn)行.
2.InterruptedException是什么?有什么用?

中斷的理解

中斷可以理解為線(xiàn)程的一個(gè)標(biāo)識(shí)位屬性,它表示一個(gè)運(yùn)行中的線(xiàn)程是否被其他線(xiàn)程進(jìn)行了中斷操作也就是該線(xiàn)程的中斷狀態(tài).

下面一段話(huà)我覺(jué)得寫(xiě)得很好,摘錄于線(xiàn)程中斷

中斷是一種協(xié)作機(jī)制。當(dāng)一個(gè)線(xiàn)程中斷另一個(gè)線(xiàn)程時(shí),被中斷的線(xiàn)程不一定要立即停止正在做的事
情。
相反,中斷是禮貌地請(qǐng)求另一個(gè)線(xiàn)程在它愿意并且方便的時(shí)候停止它正在做的事情。有些方法,
例如 Thread.sleep(),很認(rèn)真地對(duì)待這樣的請(qǐng)求,但每個(gè)方法不是一定要對(duì)中斷作出響應(yīng)。
對(duì)于中斷請(qǐng)求,不阻塞但是仍然要花較長(zhǎng)時(shí)間執(zhí)行的方法可以輪詢(xún)中斷狀態(tài),并在被中斷的時(shí)候
提前返回。 您可以隨意忽略中斷請(qǐng)求,但是這樣做的話(huà)會(huì)影響響應(yīng)。

因?yàn)檫@樣可以帶來(lái)靈活性,用戶(hù)可以根據(jù)自己的需求來(lái)決定是否進(jìn)行響應(yīng).

當(dāng)另一個(gè)線(xiàn)程通過(guò)調(diào)用 Thread.interrupt() 中斷一個(gè)線(xiàn)程時(shí),會(huì)出現(xiàn)以下兩種情況之一
1. 那個(gè)線(xiàn)程在執(zhí)行一個(gè)低級(jí)可中斷阻塞方法,例如 Thread.sleep()、 Thread.join() 或 Object.wait(),那么它將取消阻塞并拋出 InterruptedException. 因?yàn)檫@些阻塞方法,由于無(wú)法預(yù)測(cè)什么時(shí)間會(huì)結(jié)束,因此需要一種中斷機(jī)制來(lái)進(jìn)行通知該線(xiàn)程的調(diào)用者,讓調(diào)用者明白該線(xiàn)程已經(jīng)發(fā)生中斷并且提前返回.
注意:這些方法會(huì)在異常拋出的時(shí)候會(huì)清除當(dāng)前線(xiàn)程的中斷狀態(tài)wait()方法的源碼看一下就可以了

 The <i>interrupted status</i> of the current thread is cleared when this exception is thrown.

2. interrupt() 只是設(shè)置線(xiàn)程的中斷狀態(tài).

例子2

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class Test2 {
    
    public static void main(String[] args) {
        Object obj = new Object();
        Thread thread = new Thread(new Runner2(obj), "mythread-1");
        thread.start();
        try {
            TimeUnit.MILLISECONDS.sleep(1);
            thread.interrupt();
            TimeUnit.MILLISECONDS.sleep(1);
            thread.interrupt();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    static void printThreadInfo(String info, Thread thread) {
        System.out.println(info + thread.getName() + ", interrupte :" + thread.isInterrupted());
    }
    
    static class Runner2 implements Runnable {
        Object obj;
        public Runner2(Object obj) {
            this.obj = obj;
        }
        public void run() {
            synchronized(obj) {
                try {
                    System.out.println(Thread.currentThread().getName() + " before waits");
                    obj.wait();
                    System.out.println(Thread.currentThread().getName() + " after waits");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    printThreadInfo("in wait catch ", Thread.currentThread());
                } 
            }
            try {
                System.out.println(Thread.currentThread().getName() + " before sleep");
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + " after sleep");
            } catch (InterruptedException e) {
                e.printStackTrace();
                printThreadInfo("in sleep catch ", Thread.currentThread());
            } 
            System.out.println("end!");
        }
    }
}

例子上面為什么wait()方法中為什么要加一個(gè)鎖,這里有一篇文章可以看一下java 為什么wait(),notify(),notifyAll()必須在同步方法/代碼塊中調(diào)用?

結(jié)果如下: 正如我們預(yù)想的一樣,中斷會(huì)使這些方法從阻塞狀態(tài)中提前返回,并且在拋出異常是清除了中斷狀態(tài).

mythread-1 before waits
java.lang.InterruptedException
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Object.java:502)
    at com.understand.interrupt.Test2$Runner2.run(Test2.java:35)
    at java.lang.Thread.run(Thread.java:745)
in wait catch mythread-1, interrupte :false
mythread-1 before sleep
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.understand.interrupt.Test2$Runner2.run(Test2.java:44)
    at java.lang.Thread.run(Thread.java:745)
in sleep catch mythread-1, interrupte :false
end!

但是上面的例子有個(gè)問(wèn)題就是如果不去查看Log信息,主線(xiàn)程也就是調(diào)用者根本就不知道mythread-1線(xiàn)程發(fā)生過(guò)中斷.

所以接下來(lái)我們看看如果處理InterruptedException異常.

處理InterruptedException異常

拋出一個(gè)InterruptedException異常,說(shuō)明調(diào)用的方法是一個(gè)阻塞方法,因此自身來(lái)說(shuō)也是一個(gè)阻塞方法.

1.最簡(jiǎn)單的一種方式是直接繼續(xù)拋出異常給上層方法.如下所示:

public static void mySleep(long timeout) throws InterruptedException {
        System.out.println("i want to sleep.");
        TimeUnit.MILLISECONDS.sleep(timeout);
        System.out.println("wake up");
}

2.第二種方式是捕捉該異常,做一些清理工作,比如關(guān)閉流之類(lèi)的,然后再繼續(xù)拋出異常給上層方法.

public static void mySleep1(long timeout) throws InterruptedException {
        System.out.println("i want to sleep.");
        try {
            TimeUnit.MILLISECONDS.sleep(timeout);
        } catch (InterruptedException e) {
            //clear somthing
            throw e;
        }
        System.out.println("wake up");
}

3. 第三種方式是你調(diào)用的方法中不讓拋出異常,比如例子2中的線(xiàn)程中的run()方法,因此此時(shí)可以捕捉異常后保留中斷狀態(tài)讓調(diào)用者知道它狀態(tài),又由于在阻塞方法拋出異常的時(shí)候會(huì)清空中斷狀態(tài),所以此時(shí)可以在catch語(yǔ)句中調(diào)用Thread.currentThread().interrupt()方法設(shè)置一下中斷狀態(tài).如下所示:

static class Runner implements Runnable {
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace(); 
                Thread.currentThread().interrupt();
            }
        }
}

參考

1. https://www.ibm.com/developerworks/cn/java/j-jtp05236.html
2. https://blog.csdn.net/haluoluo211/article/details/49558155
3. Java并發(fā)編程的藝術(shù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容