浅析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


猜你喜欢
- 一般学过C#的都知道,Array 一旦定义好,比如四个长度,当需要再往里面添加元素的时候,需要Array.Resize一下才可以。有鉴于此,
- 先看看效果图:源码下载:Android Navigation TabBar控件实现多彩标签栏代码:MainActivity.javapack
- 前言最近,新来的同事写接口,需要知道lua怎么调用C#脚本,趁这个机会也给大家分享一下。道理我也不多少,直接上干货。框架介绍本项目采用lua
- 1. 快速创建maven管理的SpringBoot项目1、访问 http://start.spring.io/2、 选择构建工具Maven
- 线性表是其组成元素间具有线性关系的一种数据结构,对线性表的基本操作主要有,获取元素,设置元素值,遍历,插入,删除,查找,替换,排序等。而线性
- 1、基本介绍随着分词在信息检索领域应用的越来越广泛,分词这门技术对大家并不陌生。对于英文分词处理相对简单,经过拆分单词、排斥停止词、提取词干
- //加载Excel public 
- 前言:数据抽象是一种仅向用户显示基本细节的属性。不向用户显示琐碎或非必需的单元。例如:汽车被视为汽车而不是其单个组件。数据抽象也可以定义为仅
- 在上一节,我们已经完成了项目的整体技术架构设计和具体的数据库设计,接下来,我们搭建整体的开发框架。开发工具选用Idea。开发工具只是为了提高
- 一、场景public class OrderModel {private List<String> favorableDescL
- 本文项目为大家分享了Java实现 * 面五子棋的具体代码,供大家参考,具体内容如下项目介绍:本次设计是基于知识点Java类和对象以及数组开发的
- //有何不足或者问题希望能够得到各位的多多指正,不胜感激import java.util.Scanner;/** *
- 本文主要介绍的是一个球形水波的百分比控件,市面上有各种形形色色的百分比控件,我一直觉得水波是最炫的,UI给了我这个机会,然而网上搜了一大堆,
- 1.配置代理系统管理---configure Global Security(全局安全设置)---Tcp port for inbound
- 1、使用AsyncTask异步任务实现,调用publishProgress()方法刷新进度来实现(已优化)public class MyAs
- 微信公众号开发之回复图文消息,供大家参考,具体内容如下图文消息的主要参数说明通过微信官方的消息接口指南,可以看到对图文消息的参数介绍,如下图
- 代码复现不要,思考一下会打印出什么?List<String> list1 = new ArrayList<>(Arr
- 简述用来干嘛的?当你在方法中调用了多个线程,对数据库进行了一些不为人知的操作后,还有一个操作需要留到前者都执行完的重头戏,就需要用到 Cou
- 本文实例讲述了Android编程之ListView和EditText发布帖子隐藏软键盘功能。分享给大家供大家参考,具体如下:在Android
- 实验题目:MapReduce:编程实验内容:本实验利用 Hadoop 提供的 Java API 进行编程进行 MapReduce 编程。实验