多线程(一) -- java线程(四) -- interrupt方法详解

1. interrupt打断线程

1.1 打断sleep,wait,join(阻塞状态)的线程

public static void main(String[] args) throws InterruptedException{
    Thread t1 = new Thread(() -> {
       log.debug("sleep;;;");
        try {
            Thread.sleep(5000); // sleep,join,wait
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }, "t1");
    t1.start();
    Thread.sleep(1000);
    log.debug("interrupt");
    t1.interrupt();
    log.debug("打断标记:{}", t1.isInterrupted());
}

结果:

22:01:29.967 [t1] DEBUG com.multiThread.TestInterrupt1 - sleep;;;
22:01:30.963 [main] DEBUG com.multiThread.TestInterrupt1 - interrupt
22:01:30.963 [main] DEBUG com.multiThread.TestInterrupt1 - 打断标记:false
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at com.yhx.toali.multiThread.TestInterrupt1.lambda$main$0(TestInterrupt1.java:16)
	at java.base/java.lang.Thread.run(Thread.java:830)

可以看出阻塞状态的线程,被打断后会抛出一个异常;同时阻塞状态的线程被打断后,会将打断标记置为假

1.2 打断正常执行的线程

给定一个死循环,如何打断这个死循环:

public static void main(String[] args) throws InterruptedException{
    Thread t1 = new Thread(() -> {
       while (true) {

       }
    }, "t1");
    t1.start();
    Thread.sleep(1000);
    log.debug("interrupt");
    t1.interrupt();
}

执行结果:可以看到程序并没有停止,还在一直执行
在这里插入图片描述
我们可以通过获取打断状态来停止这个线程:

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

执行结果:
在这里插入图片描述

2. 两阶段终止模式

在一个线程t1中如何“优雅”地终止线程t2?这里的“优雅”指的是给t2一个料理后事的机会。

2.1 错误思路:

  • 使用线程对象的stop()方法停止线程
    • stop方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其他线程将永远无法获取锁
  • 使用System.exit(int)方法停止线程
    • 目的仅是停止一个线程,但是这种做法会让整个程序都停止

2.2 实现思路:

在这里插入图片描述

2.3 代码展示:

public class TestInterrupt3 {
    public static void main(String[] args) throws InterruptedException{
        TwoPhaseTermination twoPhaseTermination = new TwoPhaseTermination();
        twoPhaseTermination.start();

        Thread.sleep(3500);
        twoPhaseTermination.stop();
    }
}

@Slf4j
class TwoPhaseTermination {
    private Thread monitor;

    // 启动监控线程
    public void start() {
        monitor = new Thread(() -> {
            while (true) {
                Thread current = Thread.currentThread();
                if (current.isInterrupted()) { // 获取终止状态
                    log.debug("料理后事");
                    break;
                }
                try {
                    Thread.sleep(1000); //情况1
                    log.debug("执行监控记录"); // 情况2
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // 重新设置打断标记
                    current.interrupt();
                }
            }
        });
        monitor.start();
    }

    public void stop() {
        monitor.interrupt();
    }
}

执行结果:

22:44:24.932 [Thread-0] DEBUG com.yhx.toali.multiThread.TwoPhaseTermination - 执行监控记录
22:44:25.947 [Thread-0] DEBUG com.yhx.toali.multiThread.TwoPhaseTermination - 执行监控记录
22:44:26.948 [Thread-0] DEBUG com.yhx.toali.multiThread.TwoPhaseTermination - 执行监控记录
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at com.yhx.toali.multiThread.TwoPhaseTermination.lambda$start$0(TestInterrupt3.java:36)
	at java.base/java.lang.Thread.run(Thread.java:830)
22:44:27.432 [Thread-0] DEBUG com.yhx.toali.multiThread.TwoPhaseTermination - 料理后事

如果注释掉catch中的interrupt,查看执行结果:可以看到执行没有停止,这是因为:当线程在执行阻塞操作时(比如调用sleep/wait/yield/join方法时)调用了interrupt()会抛出InterruptException异常并且将该线程的”中断”标志位清空,会将线程的中断标志重新设置为false。所以在需要在线程是打断状态下重新调用interrupt方法,将线程状态设置为打断,触发料理后事方法。
在这里插入图片描述

3. interrupted()和isInterrupted()区别:

3.1 修改上述料理后事的代码:

if (current.isInterrupted()) {
    log.debug("料理后事");
    System.out.println(current.isInterrupted());
    break;
}

结果:

11:52:43.814 [Thread-0] DEBUG com.yhx.toali.multiThread.TwoPhaseTermination - 执行监控记录
11:52:44.823 [Thread-0] DEBUG com.yhx.toali.multiThread.TwoPhaseTermination - 执行监控记录
11:52:45.831 [Thread-0] DEBUG com.yhx.toali.multiThread.TwoPhaseTermination - 执行监控记录
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at com.yhx.toali.multiThread.TwoPhaseTermination.lambda$start$0(TestInterrupt3.java:42)
	at java.base/java.lang.Thread.run(Thread.java:830)
11:52:46.316 [Thread-0] DEBUG com.yhx.toali.multiThread.TwoPhaseTermination - 料理后事
true

3.2 修改上述current.isInterrupted()monitor.interrupted()

if (monitor.interrupted()) {
    log.debug("料理后事");
    System.out.println(current.isInterrupted());
    break;
}

结果:

11:54:41.316 [Thread-0] DEBUG com.yhx.toali.multiThread.TwoPhaseTermination - 执行监控记录
11:54:42.349 [Thread-0] DEBUG com.yhx.toali.multiThread.TwoPhaseTermination - 执行监控记录
11:54:43.356 [Thread-0] DEBUG com.yhx.toali.multiThread.TwoPhaseTermination - 执行监控记录
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at com.yhx.toali.multiThread.TwoPhaseTermination.lambda$start$0(TestInterrupt3.java:42)
	at java.base/java.lang.Thread.run(Thread.java:830)
11:54:43.793 [Thread-0] DEBUG com.yhx.toali.multiThread.TwoPhaseTermination - 料理后事
false

3.3 结论:

  1. 他们都是获取打断线程状态,但是isInterrupted不会清除打断标记,而interrupted会
    也就是说调用interrupted方法会返回当前线程中断状态,同时清除中断状态(置为false)
  2. Thread.interrupted() 线程静态方法,
    Thread.currentThread().isInterrupted() 线程实例方法

3.4 源码查看:

interrupted:

public static boolean interrupted() {
    Thread t = currentThread();
    boolean interrupted = t.interrupted;
    // We may have been interrupted the moment after we read the field,
    // so only clear the field if we saw that it was set and will return
    // true; otherwise we could lose an interrupt.
    if (interrupted) {
        t.interrupted = false;
        clearInterruptEvent();
    }
    return interrupted;
}

isInterrupted:

public boolean isInterrupted() {
    return interrupted;
}

可以看到他们拿到的都是Thread的成员变量interrupted的值,不过interrupted拿到之后还会设置其为fasle

private volatile boolean interrupted;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值