interrupt方法的使用

參考網頁

http://www.cnblogs.com/biGpython/archive/2012/03/05/2380858.htmlhtml

http://blog.csdn.net/hudashi/article/details/6958550java

http://hainiubl.com/topics/29#爲什麼調用wait方法有可能拋出InterruptedException異常python

★理論基礎(摘自《Java併發編程的藝術》)

《Java併發編程的藝術》是這麼寫的:編程

從Java的API中能夠看出,許多聲明拋出InterruptedException的方法(例如Thread.sleep(long millis)方法)這些方法在拋出InterruptedException以前,Java虛擬機會先將該線程的中斷標識位清除(即設置爲false),而後拋出InterruptedException,此時調用isInterrupted()方法將會返回false。併發

這是什麼意思呢?ide

如下面的 InterruptJoinTest 類代碼爲例說明(詳細代碼見InterruptJoinTest 類代碼)。main線程先啓動了t2線程,而後調用t2.interrupt()方法對t2線程進行中斷,this

t2.start();spa

t2.interrupt();.net

回顧下JDK中的源碼以下線程

注意紅框中的註釋,調用t2.interrupt()方法就是將t2的中斷標識位進行了置位(設置爲true)。

注意這裏,調用t2.interrupt()方法對t2線程進行中斷也就是對t2的中斷標識位進行了置位,置位後,t2的中斷標識位爲true,而後t2線程會拋出InterruptedException異常。再回過頭來看《Java併發編程的藝術》中的話:

在拋出InterruptedException以前,Java虛擬機會先將該線程的中斷標識位清除(即設置爲false),而後拋出InterruptedException

也就是說:調用t2.interrupt()方法將中斷標識位設置爲true;而後t2線程要拋出InterruptedException異常,t2拋出InterruptedException異常以前Java虛擬機又會將t2的中斷標識位設置爲false(即將t2的中斷標識位清除)。那麼意味着線程t2的中斷標識位被設置爲true的時間其實至關短暫,立刻隨着InterruptedException異常的拋出又被重置爲了false。

以上這些說明,是爲了更好的理解下面的代碼示例。

研究InterruptedException類代碼的執行結果,會有更深的體會。

interrupt與join

代碼

public class InterruptJoinTest {

    public static void main(String... args) {
        T1 t1 = new T1();
        T2 t2 = new T2(t1);

        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        t2.start();
        t2.interrupt();
    }
}

class T1 extends Thread {

    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "======" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }
}

class T2 extends Thread {

    private T1 t;

    public T2(T1 t) {
        this.t = t;
    }

    public void run() {
        try {
            t.join();
            System.out.println(Thread.currentThread().getName() + "######");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            System.out.println(Thread.currentThread().getName() + "$$$$$$$$$$$$$$$" + Thread.currentThread().isInterrupted());
        }
    }
}

標黃語句被註釋掉時的打印結果(即不調用t2線程的interrupt方法

Thread-0======0

Thread-0======1

Thread-0======2

Thread-0======3

Thread-0======4

Thread-1######

標黃語句不被註釋時的打印結果(即調用t2線程的interrupt方法

Thread-0======0

Thread-1$$$$$$$$$$$$$$$false

Thread-0======1

Thread-0======2

Thread-0======3

Thread-0======4

分析(調用t2線程的interrupt方法的場景下

t2線程中調用了t1.join(),因此t2線程進入了對象t1的等待隊列(即對象t1的wait set),t2線程要等待t1線程執行完畢才能繼續。此時t2線程在不斷地檢查自身中斷狀態的值(底層來實現)。

主線程即main線程中調用了t2.interrupt(),將t2線程的中斷狀態位設置爲true。同時t2線程不斷地檢查自身中斷狀態的值,發現了置位(中斷狀態位被設置爲true),因此拋出InterruptedException異常,進行了復位(設置爲false),而後t2線程進入了catch異常處理代碼塊。

t1線程沒受什麼影響,繼續本身的運行。

interrupt與wait——若是被interrupt的線程t只是建立了,線程還沒有start,此時調用t.interrupt()方法也沒有將線程t的中斷標識位設置爲true——線程start以前interrupt方法不起做用

代碼

import java.util.concurrent.TimeUnit;

public class InterruptWaitTest {

    volatile static boolean b = true;

    public static void main(String... strings) throws InterruptedException{
        final byte[] lock = new byte[0];
        Thread t = new Thread(new Runnable() {
            public void run() {
                while (b) {
                }

                synchronized (lock) {
                    try {
                        System.out.println(Thread.currentThread().getName() + " in waiting!!");
                        lock.wait();
                        System.out.println("@$%^^$^&*#E%^&*(");
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        System.out.println(Thread.currentThread().getName() + " end waiting!!");
                        System.out.println(Thread.currentThread().isInterrupted());
                    }
                }
            }
        }, "t");

        t.interrupt();
        TimeUnit.SECONDS.sleep(1);
        System.out.println("##########   " + t.isInterrupted());

        t.start();

//        t.interrupt();
//        System.out.println("@@@@@@@@@@@@@   " + t.isInterrupted());
//        b = false;


        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }
}

打印結果

##########   false

分析

線程t還沒運行,主線程即main線程中就調用了t.interrupt()方法,此時打印線程t的中斷狀態位,發現線程t的中斷狀態位並未被置位。因此線程t沒有受interrupt()方法的任何影響。啓動線程t後直接執行t裏面的run()方法。

interrupt與wait——若是被interrupt的線程已經start了,在進入wait以前,若是有線程調用了其interrupt方法,那這個wait等於什麼都沒作,會直接跳出來,即從wait set跳出

代碼——線程剛進入wait就檢測到中斷狀態位被置位,因此當即跳出wait set

import java.util.concurrent.TimeUnit;


public class InterruptWaitTest {

    volatile static boolean b = true;

    public static void main(String... strings) throws InterruptedException{
        final byte[] lock = new byte[0];
        Thread t = new Thread(new Runnable() {
            public void run() {
                while (b) {
                }

                synchronized (lock) {
                    try {
                        System.out.println(Thread.currentThread().getName() + " in waiting!!");
                        lock.wait();
                        System.out.println("@$%^^$^&*#E%^&*(");
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        System.out.println(Thread.currentThread().getName() + " end waiting!!");
                        System.out.println(Thread.currentThread().isInterrupted());
                    }
                }
            }
        }, "t");

//        t.interrupt();
//        TimeUnit.SECONDS.sleep(1);
//        System.out.println("##########   " + t.isInterrupted());

        t.start();

        t.interrupt();
        System.out.println("@@@@@@@@@@@@@   " + t.isInterrupted());
        b = false;


        TimeUnit.SECONDS.sleep(1);


    }
}

運行結果

@@@@@@@@@@@@@   true

t in waiting!!

t end waiting!!

false

分析

線程t在進入對象lock的等待隊列以前,先在主線程即main線程中被調用interrupt()方法。t.interrupt()方法調用完,線程t的中斷標識位已經被設置爲true。

由於b爲true,線程t在執行while空方法,不會去檢查中斷狀態。

b變爲false,線程t終於執行到lock.wait(),線程t進入了lock的等待隊列即wait set。線程t不斷循環檢查本身的中斷標識位,發現中斷標識位已經爲true,立刻會拋出 InterruptedException,進入catch異常處理代碼塊。

interrupt與wait總結

線程t若是在啓動前調用t.interrupt()方法是不起做用的。

線程t啓動後,調用t.interrupt()方法,在線程t進入某個對象的wait set後線程t會不斷檢查本身的中斷標識位狀態,若發現中斷標識位狀態已被設置爲true,線程t就會拋出InterruptedException異常。

注意,只有線程t進入某個對象的wait set,線程t纔會開始不斷循環檢查自身的中斷標識位狀態。不然即便線程t的中斷標識位被設置爲true,線程t不檢查中斷標識位,也不會拋出InterruptedException異常。

interrupt與sleep——《java併發編程的藝術》示例代碼

代碼

import java.util.concurrent.TimeUnit;

public class InterruptSleepTest {

    public static void main(String[] args) throws Exception {
        // sleepThread不停的嘗試睡眠
        Thread sleepThread = new Thread(new SleepRunner(), "SleepThread");
        sleepThread.setDaemon(true);
        // busyThread不停的運行
        Thread busyThread = new Thread(new BusyRunner(), "BusyThread");
        busyThread.setDaemon(true);
        sleepThread.start();
        busyThread.start();
        // 休眠5秒,讓sleepThread和busyThread充分運行
        TimeUnit.SECONDS.sleep(5);
        sleepThread.interrupt();
        busyThread.interrupt();
        System.out.println("SleepThread interrupted is " + sleepThread.isInterrupted());
        System.out.println("BusyThread interrupted is " + busyThread.isInterrupted());
        // 防止sleepThread和busyThread馬上退出
        TimeUnit.SECONDS.sleep(2);
    }

    static class SleepRunner implements Runnable {
        @Override
        public void run() {
            while (true) {
//             SleepUtils.second(10);
                try {
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    System.out.println("!@#$%^&*(!@#$%^&*(!@#$%^&*");
                }
            }
        }
    }

    static class BusyRunner implements Runnable {
        @Override
        public void run() {
            while (true) {
            }
        }
    }
}

打印結果

!@#$%^&*(!@#$%^&*(!@#$%^&*

SleepThread interrupted is false

BusyThread interrupted is true

分析

sleepThread.interrupt();

SleepRunner進行中斷,由於SleepRunner正在進行sleep,方法內部會不斷檢查中斷狀態的值,檢測到中斷狀態已經被置位(設置爲true),那麼就會拋出InterruptedException異常,而且將中斷狀態復位。因此看到的結果是跳進了catch異常處理代碼塊,且由於被複位,因此中斷狀態位是false。

busyThread.interrupt();

BusyRunner進行中斷,由於BusyRunner沒有sleep、wait、join,不會去檢查中斷狀態,因此線程A不會拋出 InterruptedException,而會一直執行着本身的操做。這樣interrupt()置位一直沒有清除,因此中斷狀態位仍是true。

程序執行時,最後停頓2秒後終止,爲何BusyRunner對應的線程沒有一直執行呢?不是while(true)嗎?由於 BusyRunner 是Daemon線程。main線程執行完畢後,JVM中就沒有非Daemon線程了,因此JVM就退出了。線程 BusyRunner 就跟着一塊兒結束了。

★小結--interrupt的應用——喚醒阻塞線程

喚醒的條件

線程t處於如下三種狀況下,

線程t由於調用某對象的wait()方法進入某對象的wait set;

線程t由於調用其餘線程的join()進入其餘線程(對象)的wait set;

線程t由於調用t.sleep()自身進入超時等待狀態。

實際上這三種狀況下線程t都處於wait set

線程t在這三種狀態下會不斷循環檢查自身的中斷標識位,此時若是有線程調用t.interrupt()方法,會將線程t的中斷標識位設置爲true,那麼線程t將接收到一箇中斷異常(InterruptedException),而後線程t的中斷標識位會被複位爲false,再而後線程t會進入catch異常處理代碼塊。

套路——喚醒處於wait set線程的步驟

  1. 拋出InterruptedException異常
  2. 復位線程中斷狀態位(設置爲false)
  3. 進入catch異常處理代碼塊

通過以上步驟,線程將從wait set中跳出。

相關文章
相關標籤/搜索