【Java深入学习】多线程之interrupt方法

100 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

什么是interrupt?

程序中,有些线程的中断需要外部干预,比如线程中存在while(true)循环,或者存在一些阻塞操作,比如 sleep、wait、join等。 中断线程的方式,如果直接使用stop、suspend等方法,对程序来说是不太严谨的,这些方法类似于直接杀死线程,可能会造成数据问题。 interrupt方法的使用可以解决该问题,调用指定线程的该方法,表示向指定线程发起通知,可以执行中断线程的操作了,而具体什么时候执行,由线程自己决定,这种方式可以让程序处理更加严谨。

interrupt有什么用

打断阻塞状态线程:

打断 sleep,wait,join 的线程 这几个方法都会让线程进入阻塞状态 打断 sleep 的线程, 会清空打断状态,以 sleep 为例

代码示例

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test11")
public class Test11 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("sleep...");
            try {
                Thread.sleep(5000); // wait, join
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t1");

        t1.start();
        Thread.sleep(2000);
        log.debug("interrupt");
        t1.interrupt();
        log.debug("打断标记:{}", t1.isInterrupted());
    }
}

结果:

image.png 解释:

按理来说 当被打断后 ==t1.isInterrupted()的结果应该是true 表明是被打断,但是现在为false,原因是当interrupt打断的是阻塞状态的线程时,会抛出异常 然后重置t1.isInterrupted()==为false,所以结果为false

打断正常状态的线程

代码示例

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test12")
public class Test12 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while(true) {
                boolean interrupted = Thread.currentThread().isInterrupted();
                if(interrupted) {
                    log.debug("被打断了, 退出循环");
                    break;
                }
            }
        }, "t1");
        t1.start();

        Thread.sleep(1000);
        log.debug("interrupt");
        t1.interrupt();
        log.debug("打断标记:{}", t1.isInterrupted());
    }
}

结果:

19:08:03.063 c.Test12 [main] - interrupt
19:08:03.066 c.Test12 [t1] - 被打断了, 退出循环
19:08:03.066 c.Test12 [main] - 打断标记:true

两阶段终止模式

提出问题:如何在一个线程T1中 “体面”的终止线程T2?这里的“体面”指的是给T2一个料理后事的机会。

解决思路

如何在一个线程T1中 “体面”的终止线程T2?这里的“体面”指的是给T2一个料理后事的机会。

@Slf4j(topic = "c.TwoPhaseTermination")
public class Test13 {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination tpt = new TwoPhaseTermination();
        tpt.start();
        Thread.sleep(3500);
        log.debug("停止监控");
        tpt.stop();
    }
}

@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination {
    // 监控线程
    private Thread monitorThread;

    // 启动监控线程
    public void start() {
        monitorThread = new Thread(() -> {
            while (true) {
                // 是否被打断
                if (monitorThread.isInterrupted()) {
                    log.debug("料理后事");
                    break;
                }
                try {
                    Thread.sleep(1000);
                    log.debug("执行监控记录");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    monitorThread.interrupt(); // 33行
                }
            }
        }, "monitor");
        monitorThread.start();
    }

    // 停止监控线程
    public void stop() {
        monitorThread.interrupt();
    }
}

结果:

  • 20:09:31.683 c.TwoPhaseTermination [monitor] - 执行监控记录
  • 20:09:32.687 c.TwoPhaseTermination [monitor] - 执行监控记录
  • 20:09:33.688 c.TwoPhaseTermination [monitor] - 执行监控记录
  • 20:09:34.181 c.TwoPhaseTermination [main] - 停止监控
  • 20:09:34.182 c.TwoPhaseTermination [monitor] - 料理后事
  • java.lang.InterruptedException: sleep interrupted
  • at java.lang.Thread.sleep(Native Method)
  • at cn.itcast.test.TwoPhaseTermination.lambdastartstart0(Test13.java:46)
  • at java.lang.Thread.run(Thread.java:748)

解释:

这个代码就是 main线程让monitor线程“体面”,对于本代码来说 在主线程睡眠3.5s后 打断monitor线程 又因为monitor线程每次循环都睡眠1s 所以大概率 中断的是sleep阶段 所以 进入异常处理,这里注意第33行 很关键我们知道 在抛出异常后 中断标志就会重置 所以我们这里再次进行了==monitorThread.interrupt();此时中断标记就为true,如果我们这里不进行monitorThread.interrupt();处理 则循环不会结束。万一我们中断了正常线程 则monitorThread.isInterrupted();==直接为true 中断线程