Java多线程——3.中断线程
Java线程中断
join()方法与线程中断的协同使用
join()方法用于等待另一个线程完成,它与中断机制结合使用时可以实现更灵活的线程同步。
join()方法的基本特性
- 等待线程结束:调用
join()会阻塞当前线程,直到目标线程执行完毕 - 指定等待时间:
join(long millis)可以设置最大等待时间 - 已结束线程:对已终止的线程调用
join()会立即返回
public class JoinWithInterrupt {
public static void main(String[] args) {
Thread worker = new Thread(() -> {
try {
System.out.println("工作线程开始执行");
Thread.sleep(3000); // 模拟耗时任务
System.out.println("工作线程执行完毕");
} catch (InterruptedException e) {
System.out.println("工作线程被中断");
}
});
worker.start();
Thread waiter = new Thread(() -> {
try {
System.out.println("等待线程开始等待");
worker.join(2000); // 最多等待2秒
if (worker.isAlive()) {
System.out.println("等待超时,发送中断请求");
worker.interrupt(); // 超时后中断工作线程
worker.join(); // 等待工作线程处理中断
}
System.out.println("等待线程结束");
} catch (InterruptedException e) {
System.out.println("等待线程自身被中断");
}
});
waiter.start();
}
}
这个示例展示了join()与中断的协同:等待线程先等待工作线程2秒,然后.isAlive()判断线程如果还存活,则主动中断工作线程并等待其退出。
线程中断
线程中断并非强制终止线程,而是一种协作机制——通过向目标线程发送中断信号,由目标线程自行决定是否响应中断并终止执行。这种设计避免了强制终止线程可能导致的资源泄露等问题。
中断相关的核心方法
Java提供了三个与中断相关的核心方法:
void interrupt():向目标线程发送中断请求,设置中断标志位boolean isInterrupted():判断当前线程是否被中断(不清除中断标志)static boolean interrupted():判断当前线程是否被中断(清除中断标志)
public class InterruptBasic {
public static void main(String[] args) throws InterruptedException {
Thread worker = new Thread(() -> {
int count = 0;
// 检查中断标志位
while (!Thread.currentThread().isInterrupted()) {
count++;
System.out.println("工作中... 计数:" + count);
try {
Thread.sleep(100); // 模拟耗时操作
} catch (InterruptedException e) {
// 捕获中断异常,再次设置中断标志
Thread.currentThread().interrupt();
}
}
System.out.println("线程已响应中断,退出执行");
});
worker.start();
Thread.sleep(1000); // 主线程等待1秒
worker.interrupt(); // 发送中断请求
worker.join(); // 等待线程执行完毕
System.out.println("主线程结束");
}
}
在这个示例中,工作线程通过isInterrupted()持续检查中断状态,当主线程调用interrupt()后,工作线程检测到中断信号并退出循环。
使用标志位中断线程
除了内置的中断机制,还可以使用自定义标志位控制线程终止,这种方式需要注意线程间的可见性。
基于volatile的标志位
public class FlagInterrupt {
static class WorkerThread extends Thread {
// 用volatile保证可见性
public volatile boolean running = true;
@Override
public void run() {
int count = 0;
while (running) {
count++;
System.out.println("工作中... 计数:" + count);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
return;
}
}
System.out.println("收到停止信号,退出");
}
}
public static void main(String[] args) throws InterruptedException {
WorkerThread worker = new WorkerThread();
worker.start();
Thread.sleep(1000);
worker.running = false; // 设置标志位终止线程
worker.join(); // 等待线程结束
System.out.println("主线程结束");
}
}
volatile关键字确保了标志位在多线程间的可见性:
- 每次读取都从主内存获取最新值
- 每次修改后立即写入主内存
- 避免线程缓存导致的可见性问题
中断机制的两种响应方式
线程对中断的响应取决于其当前状态,主要有两种情况:
1. 运行中的线程响应中断
当线程处于运行状态时,需要主动检测isInterrupted()标志:
public class RunningInterrupt {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 执行任务逻辑
System.out.println("执行任务...");
}
System.out.println("收到中断信号,退出");
});
t.start();
Thread.sleep(500);
t.interrupt(); // 发送中断请求
t.join(); // 等待线程结束
}
}
这种方式要求线程在循环中定期检查中断状态,适合CPU密集型任务。
2. 等待中的线程响应中断
当线程处于wait()、sleep()、join()等等待状态时,收到中断请求会抛出InterruptedException:
public class WaitingInterrupt {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
try {
System.out.println("线程进入等待状态");
Thread.sleep(5000); // 长时间等待
} catch (InterruptedException e) {
System.out.println("在等待中收到中断信号");
return; // 退出线程
}
System.out.println("线程正常结束");
});
t.start();
Thread.sleep(1000);
t.interrupt(); // 中断等待中的线程
t.join(); // 等待线程处理中断
}
}
在等待状态中响应中断更及时,因为InterruptedException会立即唤醒线程。
中断机制注意事项
及时响应中断:线程应尽快检测并响应中断请求,避免长时间忽略中断
清理资源:收到中断后应先清理资源再退出(要是你采用继承Thread类或者实现Runnable接口的方式创建线程,在线程执行完毕后,系统会自动回收相关资源,无需手动调用.close()方法。)
@Override
public void run() {
Resource resource = new Resource(); // 获取资源
try {
while (!isInterrupted()) {
// 使用资源执行任务
}
} finally {
resource.close(); // 确保资源被释放
System.out.println("资源已清理");
}
}
正确传递中断:捕获
InterruptedException后如需继续执行,应重新设置中断标志try {
Thread.sleep(100);
} catch (InterruptedException e) {
// 重新设置中断标志
Thread.currentThread().interrupt();
// 可以继续处理或退出
}
避免屏蔽中断:不要捕获
InterruptedException后不做任何处理// 不推荐的做法
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// 屏蔽了中断,线程无法响应
}
总结
join()方法可等待线程结束,支持超时设置并能被中断- 中断是协作式机制,需目标线程主动响应
- 运行中的线程通过
isInterrupted()检测中断 - 等待中的线程通过
InterruptedException响应中断 - 自定义标志位需用
volatile保证可见性
Java多线程——3.中断线程的更多相关文章
- Java多线程系列--“JUC线程池”03之 线程池原理(二)
概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...
- Java多线程系列--“JUC线程池”04之 线程池原理(三)
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...
- -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中
本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait( ...
- 转:java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例
java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例 1.CountDownLatch:一个同步工具类,它允许一个或多个线程一 ...
- Java多线程并发02——线程的生命周期与常用方法,你都掌握了吗
在上一章,为大家介绍了线程的一些基础知识,线程的创建与终止.本期将为各位带来线程的生命周期与常用方法.关注我的公众号「Java面典」了解更多 Java 相关知识点. 线程生命周期 一个线程不是被创建了 ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- Java多线程系列--“JUC线程池”02之 线程池原理(一)
概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...
- Java多线程系列--“JUC线程池”05之 线程池原理(四)
概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...
- Java多线程——进程和线程
Java多线程——进程和线程 摘要:本文主要解释在Java这门编程语言中,什么是进程,什么是线程,以及二者之间的关系. 部分内容来自以下博客: https://www.cnblogs.com/dolp ...
- Java多线程之守护线程
Java多线程之守护线程 一.前言 Java线程有两类: 用户线程:运行在前台,执行具体的任务,程序的主线程,连接网络的子线程等都是用户线程 守护线程:运行在后台,为其他前台线程服务 特点:一旦所有用 ...
随机推荐
- 通过 MCP 服务对接 PostgreSQL 问数 (详细实操说明)
一.实操环境 1.1Panel:Linux服务器运维管理面板 2.MaxKB:强大易用的企业AI助手 3.MCP网站:https://mcp.so/ 二.操作说明 2.1.步骤一:1Panel 2.0 ...
- 提升Avalonia UI质感,跨平台图标库选型实践
前言 之前我写了 StarBlog 发布工具更新了版本,优化了一下界面,其中就把 emoji 表情换成了 FontAwesome 图标 emoji 如果只在一个系统上用(如 Windows) 那可能没 ...
- java的错误和异常 error和Exception
error 和 Exception error是由jvm产生的,一般跟我们没有关系, 致命性的 在Exception分支中有一个重要的子类RuntimeException(运行时异常),其他的异常我们 ...
- 深入剖析开源AI阅读器项目Saga Reader基于大模型的文本转换与富文本渲染优化方案
引言 AI阅读器作为一种新型的内容消费工具,正在改变人们获取和处理信息的方式.本文将介绍Saga Reader项目中如何利用大型语言模型(LLM)进行网页内容抓取.智能优化和富文本渲染,特别是如何通过 ...
- [书籍精读]《移动WEB前端高级开发实践》精读笔记分享
写在前面 书籍介绍:<移动WEB前端高级开发实践>这本书的内容涵盖了移动Web前端开发中的各个关键技术环节.分别从HTML5.CSS3.JavaScript的ECMAScript5和ECM ...
- 基于袋鼠云实时开发平台开发 FlinkSQL 任务的实践探索
随着业务的发展,实时场景在各个⾏业中变得越来越重要.⽆论是⾦融.电商还是物流,实时数据处理都成为了其中的关键环节.Flink 凭借其强⼤的流处理特性.窗⼝操作以及对各种数据源的⽀持,成为实时场景下的⾸ ...
- LiteLLM - 统一接口调用100+ LLM模型
:bullet_train: LiteLLM LiteLLM 是一个统一的接口层,支持调用100+种大语言模型(LLM),包括Bedrock.Huggingface.VertexAI.Together ...
- ChatGPT学习之旅 (7) 参数化表达的魔力
大家好,我是Edison. 上一篇:聊聊AI人设 通过人设模版可以有效给AI"洗脑",这体现的是结构化的表达.但想要AI实现精准控制多分支,实现千人千面的功能,就得使用参数化表达了 ...
- linux centos配置环境变量
前言 在centos上配置环境变量,有两种情况:分别是系统级别的,和用户级别的. 用户级别的 只对当前用户生效,切换至其他linux用户则无效. 系统级别的则对所有用户都生效(建议). 用户级别 .b ...
- vue中的一个组件就是一个vue实例吗?
所有的 Vue 组件都是 Vue 实例,并且接受相同的选项对象 先说结论 这句话,这是官方文档说的 不过严谨来说,应该是 一个单页应用就是一个 vue 的实例 每个自定义组件就是一个 vueCompo ...