线程池子线程超时(僵死)问题
简介
线程池循环执行一些任务,某个线程执行超时,需要将超时的线程任务抛弃。
示例
修改前
当遇到超时的任务就凉凉,得重启程序。
Task.java:
public class Task implements Runnable {
private final int sleepTime;
private final CountDownLatch countDownLatch;
public Task(int sleepTime, CountDownLatch countDownLatch) {
this.sleepTime = sleepTime;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
Thread.sleep(sleepTime == Demo.THREAD_SIZE ? Demo.LONG_RUNNING_THREAD_TIME : Demo.SHORT_RUNNING_THREAD_TIME);
System.out.println("任务 " + sleepTime + " 干完了");
countDownLatch.countDown();
}
}
Demo.java:
public class Demo {
/**
* 线程数量
* 前三个任务执行 {@link Demo#SHORT_RUNNING_THREAD_TIME} ms
* 最后一个线程运行 {@link Demo#LONG_RUNNING_THREAD_TIME} ms
*/
public static final int THREAD_SIZE = 4;
/**
* 线程超时时间(ms)
*/
public static final int THREAD_TIMEOUT = 3000;
/**
* 超时线程运行的时间(ms)
*/
public static final int LONG_RUNNING_THREAD_TIME = 20000;
/**
* 正常线程运行的时间(ms)
*/
public static final int SHORT_RUNNING_THREAD_TIME = 10;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(1);
while (true) {
CountDownLatch countDownLatch = new CountDownLatch(THREAD_SIZE);
System.out.println("开始");
for (int i = 1; i <= THREAD_SIZE; i++) {
executorService.execute(new Task(i, countDownLatch));
}
if (!countDownLatch.await(Demo.THREAD_TIMEOUT, TimeUnit.MILLISECONDS)) {
throws new Exception("凉凉,重启程序");
}
System.out.println("----写数据开始----");
System.out.println("----写数据结束----");
System.out.println("结束");
}
}
}
修改后
如果 countDownLatch.await(timeout, unit) 判断超时未全部执行完,就遍历线程池 submit 返回的所有 future,未执行完就中断,最后再调用 await() 等待中断的线程执行 countDownLatch.countDown() 完成所有任务,代码如下:
Task.java:
public class Task implements Runnable {
private final int sleepTime;
private final CountDownLatch countDownLatch;
public Task(int sleepTime, CountDownLatch countDownLatch) {
this.sleepTime = sleepTime;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
Thread.sleep(sleepTime == Demo.THREAD_SIZE ? Demo.LONG_RUNNING_THREAD_TIME : Demo.SHORT_RUNNING_THREAD_TIME);
System.out.println("任务 " + sleepTime + " 干完了");
} catch (InterruptedException ie) {
System.out.println("任务 " + sleepTime + " 被中断");
} finally {
countDownLatch.countDown();
}
}
}
Demo.java:
public class Demo {
/**
* 线程数量
* 前三个任务执行 {@link Demo#SHORT_RUNNING_THREAD_TIME} ms
* 最后一个线程运行 {@link Demo#LONG_RUNNING_THREAD_TIME} ms
*/
public static final int THREAD_SIZE = 4;
/**
* 线程超时时间(ms)
*/
public static final int THREAD_TIMEOUT = 3000;
/**
* 超时线程运行的时间(ms)
*/
public static final int LONG_RUNNING_THREAD_TIME = 20000;
/**
* 正常线程运行的时间(ms)
*/
public static final int SHORT_RUNNING_THREAD_TIME = 10;
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
while (true) {
List<Future<Boolean>> futures = new ArrayList<>(THREAD_SIZE);
CountDownLatch countDownLatch = new CountDownLatch(THREAD_SIZE);
System.out.println("开始");
for (int i = 1; i <= THREAD_SIZE; i++) {
futures.add(executorService.submit(new Task(i, countDownLatch), true));
}
if (!countDownLatch.await(Demo.THREAD_TIMEOUT, TimeUnit.MILLISECONDS)) {
for (Future<Boolean> future : futures) {
if (!future.isDone()) {
future.cancel(true);
}
}
countDownLatch.await();
}
System.out.println("----写数据开始----");
System.out.println("----写数据结束----");
System.out.println("结束");
}
}
}
输出结果:
开始
任务 3 干完了
任务 1 干完了
任务 2 干完了
任务 4 被中断
----写数据开始----
----写数据结束----
结束
开始
任务 3 干完了
任务 1 干完了
任务 2 干完了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?