java - java.util.Timer 之后释放资源的最佳方式是什么?
问题描述
我有一个 AutoCloseable,它的 close() 方法被过早地调用。AutoCloseable 是下面的 ProcessQueues。我不希望在当前调用 close() 方法时调用它。我正在考虑删除“实现 AutoCloseable”来实现这一点。但是我怎么知道何时调用 ProcessQueues.close()?
public class ProcessQueues implements AutoCloseable {
private ArrayList<MessageQueue> queueObjects = new ArrayList<MessageQueue>();
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) {
monitorTimer.schedule(obj, new Date(), 1); // NOT THE ACTUAL ARGUMENTS
}
}
catch (Exception ex) {
// NOT THE ACTUAL EXCEPTION HANDLER
}
}
public static void main(String[] args) {
try (ProcessQueues pq = new ProcessQueues()) {
pq.scheduleProcessRuns();
} catch (Exception e) {
// NOT THE ACTUAL EXCEPTION HANDLER
}
}
@Override
public void close() throws Exception {
for (MessageQueue queue : queueObjects) {
queue.close();
}
}
}
我希望调用 ProcessQueues.close(),但直到所有 Timer 对象的任务执行线程终止。如所写, ProcessQueues.close() 将在任务安排后立即调用。我可以通过从 ProcessQueues 类中删除“实现 AutoCloseable”(并删除 @Override 注释)轻松解决这个问题。但是我必须自己调用 ProcessQueues.close() 。如何知道所有 Timer 对象的任务执行线程何时终止?那时我想调用 ProcessQueues.close()。
请注意,MessageQueue 并未在 try-with-resources 块的资源规范标头中实例化,因此尽管 MessageQueue 也实现了 AutoCloseable,但此处并未使用该功能。我明确地调用 MessageQueue.close()。我在 MessageQueue.close() 中释放资源。过早释放这些资源会导致任务执行线程无法完成其任务。
在重写代码以防止自动资源释放之后,我正在考虑对 ProcessQueues.close() 进行显式调用,但是我不知道如何为该显式调用找到合适的时间。
我考虑过覆盖 ProcessQueues.finalize(),但是第十一版中的“Java:如何编程”不建议这样做。“你永远不应该使用 finalize 方法,因为它会导致很多问题,并且不确定它是否会在程序终止之前被调用......现在对于任何使用系统资源的类来说,它被认为是更好的做法......提供一个当程序不再需要资源时,程序员可以调用该方法来释放资源。” 我有这样的方法。它是 ProcessQueues.close()。但是我应该什么时候调用它?
解决方案
您在这里遇到了相互冲突的生命周期问题。
您可以Timer
100% 控制其生命周期。你开始它,你停止它,就是这样。但是您无法直接自省 Timer 管理的线程的状态。因此,例如,您不能询问它当前是否正在运行任何东西。
然后你有你的MessageQueue
,由Timer
. 这是您感兴趣的生命周期。您希望等待所有MessageQueues
“完成”,等待完成的各种值。但是,由于队列不断被重新安排(考虑Timer.schedule
到您使用的方法),它们永远不会“完成”。他们处理他们的内容,然后再次运行。
那么,如何知道“完成”何时意味着“完成”?
这取决于MessageQueue
?还是取决于ProcessQueues
?谁在这里指挥?
请注意,没有什么会取消Timer
. 它只是不停地运行。
那么,怎么知道什么时候MessageQueue
可以关闭呢?
如果MessageQueue
这里是真正的驱动程序,那么您应该将生命周期方法添加到MessageQueue
可以ProcessQueues
监视以了解何时关闭事物的方法。例如,您可以CountDownLatch
为列表中的任意数量创建一个集合MessageQueues
,然后订阅MessageQueue
它在完成时调用的新生命周期方法。然后回调方法可以递减CountDownLatch
,并且该ProcessQueues.close
方法只是在关闭所有内容之前等待闩锁倒计时。
public class ProcessQueues implements AutoCloseable, MessageQueueListener {
private ArrayList<MessageQueue> queueObjects = new ArrayList<MessageQueue>();
CountDownLatch latch;
public ProcessQueues() {
queueObjects.add(new FFE_DPVALID_TO_SSP_EXCEPTION());
queueObjects.add(new FFE_DPVALID_TO_SSP_ESBEXCEPTION());
...
queueObjects.forEach((mq) -> {
mq.setListener(this);
});
latch = new CountDownLatch(queueObjects.size());
}
private void scheduleProcessRuns() {
try {
for (MessageQueue obj : queueObjects) {
monitorTimer.schedule(obj, new Date(), 1); // NOT THE ACTUAL ARGUMENTS
}
} catch (Exception ex) {
// NOT THE ACTUAL EXCEPTION HANDLER
}
}
public static void main(String[] args) {
try (ProcessQueues pq = new ProcessQueues()) {
pq.scheduleProcessRuns();
} catch (Exception e) {
// NOT THE ACTUAL EXCEPTION HANDLER
}
}
@Override
public void close() throws Exception {
latch.await();
for (MessageQueue queue : queueObjects) {
queue.close();
}
monitorTimer.cancel();
}
@Override
public void messageQueueDone() {
latch.countDown();
}
}
public interface MessageQueueListener {
public void messageQueueDone();
}
public class MessageQueue extends TimerTask {
MessageQueueListener listener;
public void setListener(MessageQueueListener listener) {
this.listener = listener;
}
private boolean isMessageQueueReallyDone {
...
}
public void run() {
...
if (isMessageQueueReallyDone() && listener != null) {
listener.messageQueueDone();
}
}
}
请注意,这意味着您的 try-with-resource 块将阻止等待所有MessageQueues
,如果这是您想要的,那么您就可以开始了。
它还愚蠢地假设您MessageQueue.run()
知道何时关闭,这可以追溯到“谁在这里控制”的事情。
推荐阅读
- java - Azure的deleteByNameAsync是否阻塞了程序中的后续语句?
- node.js - Node.js 表达如何从路由中退出
- hive - 与 Talend 的 Hive 连接问题
- sql - 我如何在sql中的数据透视中使用案例
- javascript - 在分层布局中包含子边缘而不将它们从父级内部撤回
- php - 多个 vhost 可以使用同一个 php-fpm 池吗
- azure-devops - Azure Function App 结果并不总是显示在 Monitor 中
- javascript - 按属性值(数字键)对 JavaScript 对象进行排序
- javascript - 单击滑块按钮后,js滑块调用堆栈加倍
- c# - 将可空引用类型转换为不可空引用类型,不那么冗长