浅析Java线程的中断机制

作者:傅易 时间:2023-11-19 20:11:50 

线程中断机制提供了一种方法,用于将线程从阻塞等待中唤醒,尝试打断目标线程的现有处理流程,使之响应新的命令。Java 留给开发者这一自由,我们应当予以善用。
今天我们聊聊 Java 线程的中断机制。

线程中断机制提供了一种方法,有两种常见用途:

将线程从阻塞等待中唤醒,并作出相应的“受控中断”处理。
尝试告知目标线程:请打断现有处理流程,响应新的命令。
以第一种用途为例,请看以下代码:


synchronized (lock) {
 try {
   while (!check()) {
     lock.wait(1000);
   }
 } catch (InterruptedException e) {
   e.printStackTrace();
 }
}

这段代码使用了 Java 提供的 wait/notify 机制,线程执行 lock.wait() 会阻塞,有三种情况使线程恢复运行。

1、超时 1000ms 结束,正常执行下一句代码。

2、另一个线程执行下述代码主动唤醒


synchronized (lock) {
 lock.notifyAll(); // or lock.notify();
}

这也会正常执行下一句代码。

3、另一个线程要求等待的线程“中断”


// 拿到等待中的线程的引用
Thread a;
a.interrupt();

被“中断”的线程 a,会在 lock.wait() 处抛出 InterruptedException 异常。

综上所述,你可以认为 object.wait() 内部在做这些事:


boolean checkTimeout = timeout > 0;
Thread current = Thread.currentThread();
lock.addWaiter(current);
while (!current.isNotified()) {
 if (current.isInterrupted()) {
   current.clearInterrupted();
   throw new InterruptedException();
 }
 if (checkTimeout) {
   if (timeout == 0) break;
   timeout--;
 }
}

这不完全准确,因为 wait 不使用这种“忙轮询”的方式做检查,但关于标志位的判断逻辑是正确的。

让我们从上文所述的“手动发出中断”这一操作开始探究


// sun.nio.ch.Interruptible
public interface Interruptible {
 void interrupt(Thread var1);
}
// java.lang.Thread
private volatile Interruptible blocker;
private final Object blockerLock = new Object();
public void interrupt() {
 if (this != Thread.currentThread())
   checkAccess();
 synchronized (blockerLock) {
   Interruptible b = blocker;
   if (b != null) {
     interrupt0();
     b.interrupt(this);
     return;
   }
 }
 interrupt0();
}
// Just to set the interrupt flag
private native void interrupt0();

能够看出,thread.interrupt() 先判断权限,然后实际调用 interrupt0() 设置线程的中断标志,如果当前线程有 nio 的 Interruptible 那么还会回调它。

注意,interrupt0() 只是设置了线程的中断标志。

当一个线程并不阻塞,没有在 object.wait(), thread.join(), Thread.sleep() 等不受 Java 程序逻辑控制的区域时,那么会发生什么事情?答案是不会发生任何事情,线程是否被打断只能通过主动地检查中断标志得知。

怎么检查?Thread 暴露了两个接口,Thread.interrupted() 和 thread.isInterrupted()。


// java.lang.Thread
public static boolean interrupted() {
 return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
 return isInterrupted(false);
}
private native boolean isInterrupted(boolean clearInterrupted);

能够看出,两者都是依靠内部的 isInterrupted(boolean),而它会返回线程是否被打断,并根据需要清空中断标志。

当一个函数调用会发生阻塞,Java 库函数在阻塞的源头签名里标记 throws InterruptedException,并要求编写 try catch 处理中断。

当线程发生了阻塞,就像上文所述,Java 检查到中断标志,先将其清除,然后抛出 InterruptedException。


// java.lang.Object
public final void wait() throws InterruptedException {
 wait(0);
}
public final native void wait(long timeout) throws InterruptedException;

如果一个线程收到 InterruptedException,之后仍然执行了会引发阻塞的代码,它将像“没事人”一样继续阻塞住。因为 Java 在内部将中断标志清除了!

我们常见地编写以下三类处理 InterruptedException 的代码:

将 InterruptedException 交由上层处理。


public void foo() throws InterruptedException {
 synchronized (lock) {
   lock.wait();
 }
}

遇到 InterruptedException 重设中断标志位。


try {
 synchronized (lock) {
   lock.wait();
 }
} catch (InterruptedException e) {
 Thread.currentThread().interrupt();
 //break;
}

先忙完,再重新抛出 InterruptedException。


public void bar() throws InterruptedException {
 InterruptedException ie = null;
 boolean done = false;
 while (!done) {
   synchronized (lock) {
     try {
       lock.wait();
     } catch (InterruptedException e) {
       ie = e;
       continue;
     }
   }
   done = true;
 }
 if (ie != null) {
   throw ie;
 }
}

如果一个线程无视中断标志和 InterruptedException,它仍然能够跑的很好。但这与我们设计多线程的初衷是违背的,我们希望线程之间是和谐的有序协作以实现特定功能,因此受控线程应当对中断作出响应。而 Java 留给开发者这一自由,我们应当予以善用。

来源:https://my.oschina.net/tridays/blog/1587259

标签:Java,中断机制
0
投稿

猜你喜欢

  • Android ListView填充数据的方法

    2022-12-22 06:33:28
  • 详细解读Java编程中面向字符的输入流

    2023-09-27 10:39:21
  • 使用jdk1.8实现将list根据指定的值去分组的操作

    2022-10-04 18:12:48
  • 详解Java线程堆栈

    2021-06-29 03:37:38
  • Android EditText每4位自动添加空格效果

    2022-04-30 20:08:17
  • java批量修改文件后缀名方法总结

    2022-03-15 15:46:04
  • 新手初学Java集合框架

    2022-10-06 03:01:51
  • 盘点MQ中的异常测试

    2022-05-06 07:39:24
  • 浅谈Maven镜像更换为阿里云中央仓库(精)

    2022-08-06 04:48:17
  • Spring中多配置文件及引用其他bean的方式

    2023-07-01 17:31:03
  • 详解JavaScript中的函数声明和函数表达式

    2023-04-26 01:56:07
  • java jdbc连接和使用详细介绍

    2023-01-13 18:12:52
  • Java多线程之同步工具类CountDownLatch

    2023-10-23 22:17:51
  • SpringBoot实现WebSocket即时通讯的示例代码

    2022-06-14 19:59:36
  • 基于Springboot一个注解搞定数据字典的实践方案

    2022-12-23 01:12:38
  • java IO流读取图片供前台显示代码分享

    2023-10-01 20:55:26
  • Java注释代码执行方法解析

    2023-09-28 00:16:01
  • C# 程序集和反射详解

    2022-12-29 20:24:18
  • MybatisPlus使用代码生成器遇到的小问题(推荐)

    2021-07-17 09:15:17
  • SpringBoot整合mybatis的方法详解

    2023-09-02 06:23:57
  • asp之家 软件编程 m.aspxhome.com