一文了解JAVA线程的中断(Interrupt)机制
最编程
2024-01-11 18:25:54
...
「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」
介绍
Java的中断是一种协作机制,也就是说通过中断并不能直接中断另外一个线程,而需要被中断的线程自己处理中断。
在Java的中断模型中,每个线程都有一个boolean标识,代表着是否有中断请求(该请求可以来自所有线程,包括被中断的线程本身)。例如,当线程t1想中断线程t2,只需要在线程t1中将线程t2对象的中断标识置为true,然后线程2可以选择在合适的时候处理该中断请求,甚至可以不理会该请求,就像这个线程没有被中断一样。
中断方法
线程中断相关的方法主要是下面的3方法,他们具体的作用见下面的表格:
方法名 | 介绍 |
---|---|
void interrupt() | 中断线程,设置线程的中断位true |
boolean isInterrupted() | 检查线程的中断标记位,true-中断状态, false-非中断状态 |
static boolean interrupted() | 静态方法,返回当前线程的中断标记位,同时清除中断标记,改为false。比如当前线程已中断,调用interrupted(),返回true, 同时将当前线程的中断标记位改为false, 再次调用interrupted(),会发现返回false |
记忆方法推荐:
- interrupt()是一个动词,表示中断线程。
- Interrupted是一个形容词,用于检查线程的中断位,而isInterrupted()方法只是简单的检查,interrupted()处理的更加复杂。
可中断的阻塞
针对线程处于由sleep
, wait
, join
,LockSupport.park
等方法调用产生的阻塞状态时,调用interrupt方法,会抛出异常InterruptedException
,同时会清除中断标记位,自动改为false。
不可中断的阻塞
- java.io包中的同步Socket I/O
- java.io包中的同步I/O
- Selector的异步I/O
-
sychronized
加的锁
实践案例
中断sleep、wait、join等方法
private static void test1() throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.error("error", e);
}
}, "t1");
t1.start();
Thread.sleep(500);
t1.interrupt();
Thread.sleep(100);
log.info(" interrupt status : {}", t1.isInterrupted());
}
结论: 阻塞方法sleep响应中断,抛出InterruptedException
,同时清除中断标记位为false。
中断LockSupport.park方法
public static void test3() throws InterruptedException {
Thread t3 = new Thread(() -> {
log.debug("t3 park.....");
LockSupport.park();
log.debug("t3 unpark.....");
log.debug("interrupt status: [{}]", Thread.currentThread().isInterrupted());
log.debug("t3 第二次 park.....");
LockSupport.park();
log.debug("t3 中断位为true, park失效.....");
}, "t3");
t3.start();
Thread.sleep(1000);
t3.interrupt();
}
结论: 阻塞方法park响应中断, 不会抛出异常,同时不会清除中断标记位,任为true。
中断正常方法
private static void test2() throws InterruptedException {
Thread t2 = new Thread(() -> {
while (true) {
boolean isInterrupted = Thread.currentThread().isInterrupted();
if (isInterrupted) {
log.info("interrupt status: {}", isInterrupted);
break;
}
}
}, "t2");
t2.start();
Thread.sleep(500);
t2.interrupt();
Thread.sleep(100);
log.info(" thread status, {}, interrupt status : {}", t2.getState(), t2.isInterrupted());
}
结论: 打断正常运行的线程, 不会清空打断状态,同时线程结束后,重置中断状态位。
常见模式
这里提供针对interrupt中断使用的一种常见模式,方便大家在后续的开发过程中使用。
两阶段中止模式
使用场景
在一个线程中“优雅”地停止另一个线程的工作,在停止的时候给另一个线程"料理后事"的机会。比如有个监控线程在做监控,在主线程可以终止他的工作。
代码展示
class MonitorTask {
private Thread thread;
// 开始监控
public void start() {
thread = new Thread(() -> {
while (true) {
Thread curr = thread.currentThread();
// 如果当前线程是中断状态
if(curr.isInterrupted()) {
log.debug("清理资源,料理后事");
break;
}
try {
// 监控执行工作...
log.debug("监控执行ing");
Thread.sleep(1000);
} catch (InterruptedException e) {
log.error("error", e);
// 此处中断位被清除,变为false, 重新改为true,进入循环,处理线程后事
curr.interrupt();
}
}
}, "监控线程");
thread.start();
}
// 终止线程
public void stop() {
thread.interrupt();
}
}
MonitorTask monitorTask = new MonitorTask();
monitorTask.start();
Thread.sleep(1500);
monitorTask.stop();
推荐阅读
-
Java 类加载器的作用 - 简介:类加载器是 Java™ 中一个非常重要的概念。类加载器负责将 Java 类的字节码加载到 Java 虚拟机中。本文首先详细介绍了 Java 类加载器的基本概念,包括代理模型、加载类的具体过程和线程上下文类加载器等。然后介绍了如何开发自己的类加载器,最后介绍了类加载器在 Web 容器和 OSGi™ 中的应用。 类加载器是 Java 语言的一项创新,也是 Java 语言广受欢迎的重要原因之一。它允许将 Java 类动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 开始出现,最初是为了满足 Java Applets 的需求而开发的,Java Applets 需要从远程位置下载 Java 类文件并在浏览器中执行。现在,类加载器已广泛应用于网络容器和 OSGi。一般来说,Java 应用程序的开发人员不需要直接与类加载器交互;Java 虚拟机的默认行为足以应对大多数情况。但是,如果遇到需要与类加载器交互的情况,而您又不太了解类加载器的机制,就很容易花费大量时间调试异常,如 ClassNotFoundException 和 NoClassDefFoundError。本文将详细介绍 Java 的类加载器,帮助读者深入理解 Java 语言中的这一重要概念。下面先介绍一些基本概念。 类加载器的基本概念 顾名思义,类加载器用于将 Java 类加载到 Java 虚拟机中。一般来说,Java 虚拟机以如下方式使用 Java 类:Java 源程序(.java 文件)经 Java 编译器编译后转换为 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码并将其转换为 java.lang 实例。每个实例都用来表示一个 Java 类。通过该实例的 newInstance 方法创建该类的对象。实际情况可能更加复杂,例如,Java 字节代码可能是由工具动态生成或通过网络下载的。 基本上,所有类加载器都是 java.lang.ClassLoader 类的实例。下面将详细介绍这个 Java 类。 java.lang.ClassLoader 类简介 java.lang.ClassLoader 类的基本职责是根据给定类的名称为其查找或生成相应的字节码,然后根据这些字节码定义一个 Java 类,即 java.lang.Class 类的实例。除此之外,ClassLoader 还负责加载 Java 应用程序所需的资源,如图像文件和配置文件。不过,本文只讨论它加载类的功能。为了履行加载类的职责,ClassLoader 提供了许多方法,其中比较重要的方法如表 1 所示。下文将详细介绍这些方法。 表 1.与加载类相关的 ClassLoader 方法
-
标题:一文搞定Redis面试,附Redis面试大纲+常见Redis面试题-一、基础篇 快速上手 ①. 什么是redis ②. 为什么使用redis ③. 安装 ④. 基本使用(常见数据结构的命令) Java操作redis ①. Jedis ②. SpringBoot 启动redis的方式 ①. 配置文件 ②. 生产环境启动方案 二、进阶篇 redis实现session共享 redis缓存的使用 ①. 注解式 ②. Spring Cache 数据库和缓存双写一致性问题——穿透 redis实现附近的人 redis实现计数器 redis事务 redis分布式锁的使用 redis集群 redis实现延时队列 redis实现限流 redis实现布隆过滤器 发布订阅 redis优化 三、原理篇 redis单线程为什么性能好 数据类型的底层实现 持久化机制 过期策略 内存淘汰 redis优化 哨兵模
-
一文了解JAVA线程的中断(Interrupt)机制