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中跳出。

相关文章
相关标签/搜索