当前位置: 首页 > news >正文

【JUC-Interrupt】中断相关概念

线程中断

  • 一、相关概念
  • 二、API
    • 2.1、isInterrupted方法
    • 2.2、interrupted方法
    • 2.3、interrupt
  • 三、总结:

一、相关概念

一个线程不应该由其他线程中断或停止,应该有线程自己来决定。

在Java中没有办法立即停止一个线程,因此提供了用于停止线程的协商机制中断

Java并没有给中断提供额外的语法,中断过程完全需要程序员自己实现,调用相关API如interrupt,也仅仅是将中断标志位设置为true

以实际例子体验一下:运行一个线程t1,线程t2打断t1的执行。

有三种实现方式:

  • volatilevolatile声明的变量,当值修改之后,其他线程立即可见。
  • AtomicBooleanAtomicBoolean本身就有原子性,不会出现线程安全问题,因此可以用于判断。
  • interrupt

先看volatile实现:

static volatile boolean isStop = false
private static void byVolatile() {new Thread(()->{// 模拟一直工作while (true) {if (isStop) {System.out.println("检测到中断信号,程序停止");break;}System.out.println("working.....");}}, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}new Thread(()->{isStop = true;}, "t2").start();}

AtomicBoolean

static AtomicBoolean stop = new AtomicBoolean(false);
private static void byInterrupt() {var t1 = new Thread(()->{while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println("检测到中断信号,程序停止");break;}System.out.println("working.....");}}, "t1");t1.start();try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}new Thread(()->{t1.interrupt(); // 打断t1}, "t2").start();}

通过默认的interrupt机制:

private static void byInterrupt() {var t1 = new Thread(()->{while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println("检测到中断信号,程序停止");break;}System.out.println("working.....");}}, "t1");t1.start();try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}new Thread(()->{t1.interrupt(); // 在t2线程中打断t1}, "t2").start();}

二、API

中断中有三个比较重要的API:interrupt()isInterrupted()interrupted()
在这里插入图片描述

2.1、isInterrupted方法

源码如下:

    public boolean isInterrupted() {return interrupted;}

注释:
Tests whether this thread has been interrupted. The interrupted status of the thread is unaffected by this method.
测试并获取当前线程的中断状态。

2.2、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;}

源码注释:

Tests whether the current thread has been interrupted. The interrupted status of the thread is cleared by this method. In other words, if this method were to be called twice in succession, the second call would return false (unless the current thread were interrupted again, after the first call had cleared its interrupted status and before the second call had examined it).

简单来说就是:

  1. 如果当前中断标志是true, 那么会返回true并清除当前状态标志,也就是设置为false
  2. 如果当前是false,那么会返回false
  3. 如果在中断标志为true的情况下,连着执行两次,结果依次为truefalse

2.3、interrupt

   public void interrupt() {if (this != Thread.currentThread()) {// Determines if the currently running thread has permission to modify this threadcheckAccess();// thread may be blocked in an I/O operationsynchronized (blockerLock) {Interruptible b = blocker;if (b != null) {interrupted = true;interrupt0();  // inform VM of interruptb.interrupt(this);return;}}}interrupted = true;// inform VM of interruptinterrupt0();}

这段代码上注释挺多的,我们慢慢读:

Unless the current thread is interrupting itself, which is always permitted, the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown.

翻译一下:

如果执行这个方法的线程不是本身(因为别的线程也能调用你的这个方法),将会调用checkkAccess方法检查调用者是否有权限调用,没有的话会返回SecurityException

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int) methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

翻译一下,这里很重要:

如果当前线程因为Object中的waitThead中的join、sleep方法阻塞,当调用interrupt方法的时候,会清除当前的标志位,并报InterruptedException 异常。

如果编程的时候没有注意上面这一点,可能没有办法中断线程,引起不可预知的错误。

If this thread is blocked in an I/ O operation upon an InterruptibleChannel then the channel will be closed, the thread’s interrupt status will be set, and the thread will receive a java. nio. channels. ClosedByInterruptException.

翻译:

如果线程因为I/O操作阻塞,调用interrupt将会设置中断位,但是会报一个错误

If this thread is blocked in a java. nio. channels. Selector then the thread’s interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector’s wakeup method were invoked

NIO部分不太清楚,大致意思也是会设置中断位。

If none of the previous conditions hold then this thread’s interrupt status will be set.
Interrupting a thread that is not alive need not have any effect.

翻译一下:

如果没有上述情况发生,线程中断标志位将会被正确设置

中断一个不活动的线程不会产生任何影响

下面用案例演示一下阻塞的时候调用interrupt会出现的问题:

下面是t1线程:

// 线程t1
Thread t1 = new Thread(() -> {while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println(Thread.currentThread().getName() + " 中断标志位为:" + Thread.currentThread().isInterrupted() + " 程序停止");break;}try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-------------hello InterruptDemo3");}
}, "t1");
t1.start();try {TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {e.printStackTrace();
}

看到t1的逻辑很简单,如果没有检测到中断标志位,会模拟200ms延迟,然后打印语句。

假如线程启动1s之后,来个t2线程,在执行体中中断了t1, 会不会发生我们上面学到的清空标志位呢?

 new Thread(() -> {t1.interrupt();}, "t2").start();

运行之后会发现,控制台报了异常,但是t1线程没有停止运作,因为在发生异常的时候,interrupt status已经被清除了,导致if 循环检测不到了,程序一直运行下去…

正确的做法是,在Object.wait, Thread.join(), Thread.sleep()catch块中,自己将中断标志位设置为true,防止上面错误:

// 线程t1
Thread t1 = new Thread(() -> {while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println(Thread.currentThread().getName() + " 中断标志位为:" + Thread.currentThread().isInterrupted() + " 程序停止");break;}//sleep方法抛出InterruptedException后,中断标识也被清空置为false,如果没有在//catch方法中调用interrupt方法再次将中断标识置为true,这将导致无限循环了try {Thread.sleep(200);} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 自己打断自己e.printStackTrace();}System.out.println("-------------hello InterruptDemo3");}
}, "t1");

三、总结:

方法名作用
isInterrupted返回当前线程的中断标志位 ,注意:如果当前线程是不活跃状态(执行完了),在JDK8中,会自动将中断标志位设置为false,在JDK17中,不会清除中断标志位,也就是说,打断了某个线程,即便是线程执行完毕,调用此方法获得的结果仍旧是true
interrupted静态方法,做两件事情:返回当前中断标志位并清除中断标志位(设为false)
interrupt设置中断标志位为true, 在阻塞的情况下可能会导致无法中断线程的情况,要在catch块中设置中断

解释一下上面的注意

/*** 执行interrupt方法将t1标志位设置为true后,t1没有中断,仍然完成了任务后再结束* 2000毫秒后,t1线程已经结束了,如果是JDK8,会自动恢复为false,JDK17则不会自动恢复标志。*/
public class InterruptDemo2 {public static void main(String[] args) {//实例方法interrupt()仅仅是设置线程的中断状态位为true,不会停止线程Thread t1 = new Thread(() -> {for (int i = 1; i <= 300; i++) {System.out.println("------: " + i);}/*** ------: 298* ------: 299* ------: 300* t1线程调用interrupt()后的中断标志位02:true*/System.out.println("t1线程调用interrupt()后的中断标志位02:" + Thread.currentThread().isInterrupted());}, "t1");t1.start();System.out.println("t1线程默认的中断标志位:" + t1.isInterrupted());//falsetry {TimeUnit.MILLISECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}t1.interrupt();//true/*** ------: 251* ------: 252* ------: 253* t1线程调用interrupt()后的中断标志位01:true*/System.out.println("t1线程调用interrupt()后的中断标志位01:" + t1.isInterrupted());//truetry {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}//2000毫秒后,t1线程已经结束了,如果是JDK8,会自动恢复为false// JDK17则不会自动恢复标志。System.out.println("t1线程调用interrupt()后的中断标志位03:" + t1.isInterrupted());//true (笔者用的JDK17)}
}

http://www.mrgr.cn/news/78085.html

相关文章:

  • 格网法计算平面点云面积(matlab版本)
  • 使用 F5 TTS 文字转音频
  • 性能监控利器:Ubuntu 22.04 上的 Zabbix 安装与配置指南
  • 【Go】-go中的锁机制
  • GPTZero:高效识别AI生成文本,保障学术诚信与内容原创性
  • Linux命令思维导图
  • 低代码开发平台搭建思考与实战
  • 嵌入式入门Day17
  • 【数据结构】链表的基本操作
  • Tkinter置顶弹窗提示操作成功
  • 分布式搜索引擎Elasticsearch(一)
  • Maven学习笔记
  • 设计模式——抽象工厂模式
  • 报表工具功能对比:免费易上手的山海鲸报表 vs 庞大用户群体的Tableau
  • [论文阅读-综述]Supervised Speech Separation Based on Deep Learning: An Overview
  • Android 应用测试的各种环境问题记录(Instrumentation测试)
  • [UE5学习] 一、使用源代码安装UE5.4
  • Dockerfile构建报错【ERROR: failed to solve: process】的解决办法
  • ES更新问题 Failed to close the XContentBuilder异常
  • 动态链接库工作原理 PLT GOT
  • 【数据挖掘】一、基于LDA的用户兴趣建模(兴趣标签生成模型)--用户兴趣挖掘模型
  • 《硬件架构的艺术》笔记(七):处理字节顺序
  • 车载显示display基础知识和评估
  • 02.02、返回倒数第 k 个节点
  • 如何制作项目网页
  • 【C++11】尽显锋芒