對中斷interrupt的理解

1、中斷html

線程的幾種狀態:新建、就緒、運行、阻塞、死亡。參考:線程的幾種狀態轉換 java

線程的可運行狀態並不表明線程必定在運行(runnable != running ) 。 你們都知道:全部現代桌面和服務器操做系統都使用了搶佔式的線程調度策略 。一旦線程開始執行,並非老是保持持續運行狀態的。當系統分給它的時間片(很是小的運行時間單位)用完之後,無論程序有沒有執行完,線程被強制放棄CPU,進入就緒狀態,直到下次被調度後開始繼續執行。也就是說, Runnable可運行狀態的線程處於兩種可能的狀況下:(1)佔用CPU運行中,(2)等待調度的就緒狀態。 這裏要聲明一下:處於等待調度的就緒狀態線程和處於阻塞的線程是徹底不一樣的。就緒的線程是由於時間片用完而放棄CPU,其隨時都有可能再次得到CPU而運行,這一切取決於分時OS的線程調度策略。編程

在不少操做系統的專業術語中,這種因時間片用完而被剝奪CPU的狀況咱們叫作線程中斷 。注意這和咱們下面要將得中斷線程是兩個徹底不一樣的概念。事實上,咱們不可能經過應用程序來控制CPU的線程中斷,除非咱們可以自由調用OS的內核。
中斷能夠理解爲線程的一個標識位屬性,表示一個運行中的線程是否被其餘線程進行了中斷操做。中斷比如其餘線程對該線程打了一個招呼,其餘線程經過調用該線程的interrupt()方法對其進行中斷操做。服務器

一個正在運行的線程除了正常的時間片中斷以外,可否被其餘線程控制?或者說其餘線程可否讓指定線程放棄CPU或者提早結束運行? 除了線程同步機制以外,還有兩種方法:
(1) stop(), suspend(), resume() 和Runtime.runFinalizersOnExit() ,但這些方法已經被廢棄。
(2) interrupt() 方法多線程

例:建立了一個線程countThread,它不斷地進行變量累加,而主線程嘗試對其進行中斷操做和中止操做。併發

import java.util.concurrent.TimeUnit;

public class Shutdown {
    public static void main(String[] args) throws Exception {
        Runner one = new Runner();
        Thread countThread = new Thread(one, "CountThread");
        countThread.start();
        // 睡眠1秒,main線程對CountThread進行中斷,使CountThread可以感知中斷而結束
        TimeUnit.SECONDS.sleep(1); //Thread.sleep(1000);
 countThread.interrupt();
        System.out.println("是否中止1:" + countThread.isInterrupted());
        Runner two = new Runner();
        countThread = new Thread(two, "CountThread");
        countThread.start();
        // 睡眠1秒,main線程對Runner two進行取消,使CountThread可以感知on爲false而結束
        TimeUnit.SECONDS.sleep(1);
        two.cancel();
        System.out.println("是否中止2:" + countThread.isInterrupted());
    }

    private static class Runner implements Runnable {
        private long i;

        private volatile boolean on = true;

        @Override
        public void run() {
            while (on && !Thread.currentThread().isInterrupted()) {
                i++;
            }
            System.out.println("Count i = " + i);
        }

        public void cancel() {
            on = false;
        }
    }
}

運行結果:ide

是否中止1:true
Count i = 574867187
是否中止2:false
Count i = 581254533

main線程經過中斷操做和cancel()方法都可使線程countThread終止。
當咱們調用countThread.interrput()的時候,線程countThread的中斷狀態(interrupted status) 會被置位。咱們能夠經過Thread.currentThread().isInterrupted() 來檢查這個布爾型的中斷狀態。測試

在Core Java中有這樣一句話:"沒有任何語言方面的需求要求一個被中斷的程序應該終止。中斷一個線程只是爲了引發該線程的注意,被中斷線程能夠決定如何應對中斷 "。spa

while循環有一個決定因素就是須要不停的檢查本身的中斷狀態。當外部線程調用該線程的interrupt 時,使得中斷狀態置位。這時該線程將終止循環,再也不執行循環中的內容。操作系統

這說明: interrupt中斷的是線程的某一部分業務邏輯,前提是線程須要檢查本身的中斷狀態(isInterrupted())。

參考:

java線程之中斷線程Interrupted用法 

《Java併發編程的藝術》

另:Thread.sleep和TimeUnit.SECONDS.sleep的區別與聯繫 

2、interrupt()、interrupted() 和 isInterrupted()方法的區別

一、interrupt()方法
interrupt()方法用於中斷線程。調用該方法的線程的狀態爲將被置爲"中斷"狀態。
中斷能夠理解爲線程的一個標識位屬性,它表示一個運行中的線程是否被其餘線程進行了中斷操做。中斷比如其餘線程對該線程打了個招呼,其餘線程經過調用該線程的interrupt()方法對其進行中斷操做。
注意:線程中斷僅僅是置線程的中斷狀態位,不會中止線程。須要用戶本身去監視線程的狀態爲並作處理。

二、interrupted() 和 isInterrupted()

public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

public boolean isInterrupted() {
    return isInterrupted(false);
}

private native boolean isInterrupted(boolean ClearInterrupted);

從源代碼能夠看出:
interrupted()測試的是當前線程(current thread)的中斷狀態,且這個方法會清除中斷狀態。是靜態方法(它測試的是當前線程的中斷狀態)
isInterrupted()測試的是調用該方法的對象所表示的線程,且這個方法不會清除中斷狀態。是實例方法(它測試的是實例對象所表示的線程的中斷狀態)

關於方法isInterrupted( boolean ClearInterrupted):
經過參數名ClearInterrupted能夠知道,這個參數表明是否要清除狀態位。
若是這個參數爲true,說明返回線程的狀態位後,要清掉原來的狀態位(恢復成原來狀況)。這個參數爲false,就是直接返回線程的狀態位。

這兩個方法很好區分,只有當前線程才能清除本身的中斷位(對應interrupted()方法)

例:1. interrupted()方法

 1 public class InterruptedTest {
 2     public static void main(String[] args) throws InterruptedException {
 3         MyThread thread = new MyThread();
 4         thread.start();
 5         Thread.sleep(1000);
 6 
 7         System.out.println("當前正在執行的線程:" + Thread.currentThread().getName());
 8 
 9         thread.interrupt(); // Thread.currentThread().interrupt();
10 
11         System.out.println("是否中止?=" + thread.interrupted());// false,main線程沒有被中斷
12     }
13 }
14 
15 class MyThread extends Thread {
16     @Override
17     public void run() {
18         int i = 0;
19         super.run();
20         for (; i < 500000; i++) {
21             i++;
22         }
23         System.out.println("i: " + i);
24     }
25 }

運行結果:

i: 500000
當前正在執行的線程:main
是否中止?=false

第4行啓動thread線程,第5行使main線程睡眠1秒鐘從而使得thread線程有機會得到CPU執行。
main線程睡眠1s鍾後,恢復執行到第7行,請求中斷 thread線程。
第11行測試線程是否處於中斷狀態,這裏測試的是哪一個線程呢?答案是main線程。由於:

(1)interrupted()測試的是當前的線程的中斷狀態

(2)main線程執行了第11行語句,故main線程是當前線程

若是將第9行換成Thread.currentThread().interrupt();,則運行結果爲:

i: 500000
當前正在執行的線程:main
是否中止?=true

2. isInterrupted()方法

 1 public class InterruptedTest {
 2     public static void main(String[] args) throws InterruptedException {
 3         MyThread thread = new MyThread();
 4         thread.start();
 5         //Thread.sleep(1000);
 6 
 7         System.out.println("當前正在執行的線程:" + Thread.currentThread().getName());
 8 
 9         thread.interrupt(); 
10 
11         System.out.println("是否中止?=" + thread.isInterrupted());// false,main線程沒有被中斷
12     }
13 }
14 
15 class MyThread extends Thread {
16     @Override
17     public void run() {
18         int i = 0;
19         super.run();
20         for (; i < 500000; i++) {
21             i++;
22         }
23         System.out.println("i: " + i);
24     }
25 }

運行結果:

當前正在執行的線程:main
是否中止?=true
i: 500000

在第11行,是thread對象調用的isInterrupted()方法。所以,測試的是thread對象所表明的線程的中斷狀態。因爲在第9行,main線程請求中斷 thread線程,故在第11行的結果爲: true

 

另外:當線程被阻塞的時候,好比被Object.wait, Thread.join和Thread.sleep三種方法之一阻塞時。調用它的interrput()方法。可想而知,沒有佔用CPU運行的線程是不可能給本身的中斷狀態置位的。這就會產生一個InterruptedException異常。例:

public class InterruptedSleep {
    public static void main(String[] args) {
        Runnable tr = new TestRunnable();
        Thread th1 = new Thread(tr);
        th1.start(); // 開始執行分線程
        while (true) {
            th1.interrupt(); // 中斷這個分線程
        }
    }
}

class TestRunnable implements Runnable {
    public void run() {
        try {
            Thread.sleep(1000000); // 這個線程將被阻塞1000秒
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("是否中止:" + Thread.currentThread().isInterrupted());
        }
    }
}

運行結果:

java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at threadArt.TestRunnable.run(InterruptedSleep.java:17)
    at java.lang.Thread.run(Thread.java:745)
是否中止:true

所以:

要中斷一個Java線程,可調用線程類(Thread)對象的實例方法:interrupte();然而interrupte()方法並不會當即執行中斷操做;具體而言,這個方法只會給線程設置一個爲true的中斷標誌(中斷標誌只是一個布爾類型的變量),而設置以後,則根據線程當前的狀態進行不一樣的後續操做

1. 若是線程的當前狀態處於非阻塞狀態,那麼僅僅是線程的中斷標誌被修改成true而已

2. 若是線程的當前狀態處於阻塞狀態,那麼在將中斷標誌設置爲true後,還會有以下三種狀況之一的操做:

(1) 若是是wait、sleep以及jion三個方法引發的阻塞,那麼會將線程的中斷標誌從新設置爲false,並拋出一個InterruptedException
(2) 若是是java.nio.channels.InterruptibleChannel進行的io操做引發的阻塞,則會對線程拋出一個ClosedByInterruptedException;(待驗證)
(3) 若是是輪詢(java.nio.channels.Selectors)引發的線程阻塞,則當即返回,不會拋出異常。(待驗證)
若是在中斷時,線程正處於非阻塞狀態,則將中斷標誌修改成true,而在此基礎上,一旦進入阻塞狀態,則按照阻塞狀態的狀況來進行處理;例如,一個線程在運行狀態中,其中斷標誌被設置爲true,則此後,一旦線程調用了wait、jion、sleep方法中的一種,立馬拋出一個InterruptedException,且中斷標誌被清除,從新設置爲false
經過上面的分析,咱們能夠總結,調用線程類的interrupted方法,其本質只是設置該線程的中斷標誌,將中斷標誌設置爲true,並根據線程狀態決定是否拋出異常。所以,經過interrupted方法真正實現線程的中斷原理是:開發人員根據中斷標誌的具體值,來決定如何退出線程。

參考:
JAVA多線程之中斷機制(stop()、interrupted()、isInterrupted())
使用interrupte中斷線程的真正用途 

相關文章
相關標籤/搜索