线程中断机制及响应

2017-11-12  本文已影响372人  刘建会

中断线程

thread.interrupt()用来中断线程,即将线程的中断状态位设置为true,注意中断操作并不会终止线程,不像stop()会立即终止一个运行中的线程,中断仅仅是将线程中断位设置为true(默认false)。线程会不断的检查中断位,如果线程处于阻塞状态(sleep、join、wait)且中断,就会抛出InterreptException来唤醒线程,交由应用程序处理;如果线程未阻塞且中断,也要交由应用程序处理;是终止线程,还是继续执行需要根据实际情况做出合理的响应。

如何响应线程中断

thread.interrupt()后,线程的中断位设置为true,没有任何语言方面的需求一个被中断的线程应该终止。中断只是为了引起该线程的注意,来决定如何应对中断。有些很重要的线程,以至于他们不理会中断,而是继续执行,但更多的情况下,线程应该把中断看做是一个终止请求,在终止线程前,有时间做一些收尾清理工作,如下:

    public void run() {
        try {
            /*
             * 不论while中是否调用过线程阻塞的方法,如sleep、join、wait,这里还是需要加上
             * !Thread.currentThread().isInterrupted()条件来判断线程是否终止。
             * 原因:即使调用了阻塞方法但线程可能仍没有阻塞,这样会更安全、更及时。
             */
            while (!Thread.currentThread().isInterrupted()) {
                //do more work
            }
        } catch (InterruptedException e) {
            //线程在阻塞期间被中断了
        } finally {
            //线程结束前做一些清理工作
        }
    }

注意:这里使用Thread.currentThread().isInterrupted(),而不是Thread.isInterrupted()来判断线程是否中断,因为Thread.isInterrupted()判断线程中断位为true后,会将中断位设置为false,而Thread.currentThread().isInterrupted()不会将中端位在设置为false。线程阻塞中断是抛出异常后,同样会将中断位设置为false。

    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                ...
                sleep(delay);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();//重新设置中断位,否则线程不能退出
            }
        }
    }

注意:synchornized、reentrantLock.lock()获取锁操作造成的阻塞在中断状态下不能抛出InterruptException,即获取锁操作是不能被中断的,要一直阻塞等到到它获取到锁为止。也就是说如果产生了死锁,则不能被中断。可以使用超时锁来打破死锁,reentrantLock.tryLock(timeout,unit)在timeout后会抛出InterruptException,唤醒线程做响应操作

在捕获到InterruptException后如何处理呢,通常有如下两种方式,这两种方式本质上是一样的,都是将中断交由用户应用程序来处理。

    try {
        sleep(delay);
    } catch (InterruptedException e) {
        Thread.currentThread().isInterrupted();
    }
    try {
        sleep(delay);
    } catch (InterruptedException e) {
        throw e;
    }

终止线程

让一个运行中的线程终止,通常有两种方法:1.使用信号量;2.使用线程中断。

class Scheduler{
    volatile boolean stop = false;// 线程中断信号量
    private Worker worker = new Worker("my task worker");
    void start(){
        worker.start();
        stop=false;
    }
    void stop(){
        stop=true;
    }

    class Worker extend Thread{
        public void run() {
            while (!stop) {
                long time = System.currentTimeMillis();
                /*
                 * 用while循环模拟sleep(),这里不要用sleep,否则在线程阻塞时可能会抛
                 * InterruptedException而退出,这样while检测stop条件就不会执行,失去了意义。
                 *
                 * 不论是模拟sleep,还是使用sleep,都建议线程任务中做这样的操作,以控制线程执行速率
                 */
                while ((System.currentTimeMillis() - time < 1000)) {
                    //do something
                }
            }
        }
    }
}

注意:信号量一定要是多线程可见的,这里使用volatile关键字,也可以使用Automicxxx

class Scheduler{
    private Worker worker = new Worker("my task worker");
    void start(){
        worker.start();
    }
    void stop(){
        worker.interrupt();//设置线程中断位为true
    }

    class Worker extend Thread{
        public void run() {
            while (!Thread.currentThread.isInterrupted()) {
                try{
                    // do something
                    Thread.sleep(10);
                }catch(InterruptException e){
                    // 重新设置线程状态位
                    Thread.currentThread.interrupted();
                }
            }
        }
    }
}

注意:这里也可以使用信号量代替Thread.currentThread.isInterrupted(),在捕获到异常后,设置信号量即可,但不太简便。

I/O阻塞中断

i/o操作可以阻塞线程很长时间,特别是牵扯到网络socket时,那么在i/o阻塞时,线程中断会有什么现象呢?有两种情况:

总结

上一篇 下一篇

猜你喜欢

热点阅读