Java并发编程之线程中断

613 阅读3分钟

这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战

Java中的线程中断是一种线程间的协作模式,通过设置线程的中断标志并不能直接终止该线程的执行,而是被中断的线程根据中断状态自行处理。

void interrupt()方法: 中断线程,例如当线程A运行时,线程B可以调用线程A的interrupt()方法来设置线程A的中断标志为true并立即返回。设置标志仅仅是设置标志,线程A实际上并没有被中断,它继续往下执行。如果线程A调用了wait()、sleep()、join()方法而被阻塞,这时候线程B调用线程A的interrupt()方法,线程A会抛出InterruptedException异常。

boolean isInterrupted()方法: 检测当前线程是否被中断,如果是则返回true。

public static boolean isInterrupted() {
//传递flase说明不清除中断标志
    return isInterrupted(false);
}

boolean interrupted()方法: 检测当前线程是否被中断,如果是返回true。与isInterrupted不同的是,该方法如果发现当前线程被中断,则会清除中断标志,并且该方法是static方法,可以通过Thread类直接调用。在interrupted内部是获取当前调用线程的中断标志而不是调用interrupted方法的实例对象的中断标志。

public static boolean interrupted() {
//清除中断标志
    return currentThread().isInterrupted(true);
}

下面看一个根据中断标志判断线程是否终止的例子:

public class interruptest1 {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
//                如果当前线程被中断则退出循环
                while (!Thread.currentThread().isInterrupted()) {
                    System.out.println(Thread.currentThread() + " hello");
                }
            }
        });

        //启动子线程
        thread.start();

//        主线程休眠1s,以便中断前让子线程输出
        Thread.sleep(1000);
        System.out.println("main thread interrupt thread");
        thread.interrupt();

//        等待子线程执行完毕
        thread.join();
        System.out.println("main is over");
    }
}

输出如下:

image.png

接下来再看一个例子。当线程为了等待一些特定条件的到来时,一般会调用sleep、wait或者join方法来阻塞挂起线程。但如果在某个时间点提前满足返回条件时,可以使用线程的interrupt方法,强制抛出InterruptedException异常而返回。

public class interruptest2 {
    public static void main(String[] args) throws InterruptedException{
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("threadOne begin sleep for 2000 seconds");
                    Thread.sleep(200000);
                    System.out.println("threadOne awaking");
                } catch (InterruptedException e) {
                    System.out.println("threadOne is interrupted while sleeping");
                    return;
                }
                System.out.println("threadOne-leaving normally");
            }
        });
        
        //启动线程
        threadOne.start();
        
        //确保子线程进入休眠状态
        Thread.sleep(1000);
        
        //打断子线程休眠,让子线程从sleep返回
        threadOne.interrupt();
        
        //等待子线程执行完毕
        threadOne.join();

        System.out.println("main is over");
    }
}

输出如下:

image.png

我们再通过一个例子来了解interrupted()和isInterrupted()的区别。

public class interruptest3 {

    public static void main(String[] args) throws InterruptedException{
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                for(;;) {

                }
            }
        });
        //启动线程
        threadOne.start();

        //设置中断标志
        threadOne.interrupt();
        //获取中断标志
        System.out.println("isInterrupted: " + threadOne.isInterrupted());
        //获取中断标志,并重置
        System.out.println("isInterrupted: " + threadOne.interrupted());
        //获取中断标志,并重置
        System.out.println("isInterrupted: " + Thread.interrupted());
        //获取中断标志
        System.out.println("isInterrupted: " + threadOne.isInterrupted());

        threadOne.join();

        System.out.println("main thread is over");
    }
}

结果如下:

image.png

这是因为interrupted()方法获取的是当前线程的中断状态而不是threadOne线程的中断状态。 所以结果为true、 false、 false、 true。

而如果我们将代码修改如下后显然输出为flase,因为调用interrupted()后,中断标志被清除了:

public class interrupt4 {

    public static void main(String[] args) throws InterruptedException{
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                while(!Thread.currentThread().interrupted()) {

                }
                System.out.println("threadOne isInterrupted:" + Thread.currentThread().isInterrupted());
            }
        });
        //启动线程
        threadOne.start();

        //设置中断标志
        threadOne.interrupt();

        threadOne.join();

        System.out.println("main thread is over");
    }
}

image.png