java.util.Timer任务全部终止后如何正确释放ProcessQueues资源?
解决方案
核心问题是Timer.schedule()为异步调度逻辑,调用后会立即返回,不会等待任务执行结束,导致try-with-resources代码块执行完成后直接触发close(),提前释放资源。你不需要移除AutoCloseable接口实现,按以下方案改造即可:
方案1:替换Timer为ScheduledExecutorService(推荐)
ScheduledExecutorService是JDK官方推荐的定时任务实现,内置优雅关闭和等待任务终止的API,适配现有逻辑的改造成本很低:
- 改造ProcessQueues类,新增
ScheduledExecutorService成员,替换原有Timer - 在
close()方法中先关闭线程池,等待所有任务终止后再释放MessageQueue资源
示例代码:
import java.util.ArrayList; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ProcessQueues implements AutoCloseable { private ArrayList<MessageQueue> queueObjects = new ArrayList<>(); // 按实际任务量调整核心线程数 private final ScheduledExecutorService monitorExecutor = Executors.newScheduledThreadPool(2); public ProcessQueues() { queueObjects.add(new FFE_DPVALID_TO_SSP_EXCEPTION()); queueObjects.add(new FFE_DPVALID_TO_SSP_ESBEXCEPTION()); // 其余初始化逻辑 } private void scheduleProcessRuns() { try { for (MessageQueue obj : queueObjects) { // 对应原有定时调度逻辑,按实际参数调整初始延迟、执行周期、时间单位 monitorExecutor.scheduleAtFixedRate(obj, 0, 1, TimeUnit.MILLISECONDS); } } catch (Exception ex) { // 原有异常处理逻辑 } } public static void main(String[] args) { try (ProcessQueues pq = new ProcessQueues()) { pq.scheduleProcessRuns(); // 可在此处添加额外业务逻辑,比如监听进程停止信号 } catch (Exception e) { // 原有异常处理逻辑 } } @Override public void close() throws Exception { // 1. 停止接收新任务 monitorExecutor.shutdown(); // 2. 等待所有已提交任务执行完成,自定义超时时间避免永久阻塞 if (!monitorExecutor.awaitTermination(60, TimeUnit.MINUTES)) { // 超时后强制关闭所有未完成任务 monitorExecutor.shutdownNow(); if (!monitorExecutor.awaitTermination(60, TimeUnit.SECONDS)) { System.err.println("定时任务线程池无法正常终止"); } } // 3. 所有任务终止后再释放MessageQueue资源 for (MessageQueue queue : queueObjects) { queue.close(); } } }
方案2:保留Timer的适配方案
如果必须使用Timer实现,可以通过CountDownLatch跟踪所有任务的完成状态:
- 给每个MessageQueue任务注入CountDownLatch实例,任务执行到终止逻辑时调用
countDown() - 在close()方法中调用
latch.await()等待所有任务计数归零后再释放资源
如果是循环执行的定时任务,需要额外设置终止触发条件(比如全局停止标识位),符合条件时再执行countDown逻辑。
注意事项
- 不要重写finalize()方法,该方法执行时机完全不可控,Java 9开始已经被标记为废弃
- 如果你的定时任务是永久循环运行的,需要额外增加终止信号监听逻辑,触发后先停止所有任务调度,再进入资源释放流程
内容的提问来源于stack exchange,提问作者Alan Feldstein
相关产品推荐
相关产品推荐

