首页 > 解决方案 > 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()。但是我应该什么时候调用它?

标签: javaconcurrencytimertasktry-with-resources

解决方案


您在这里遇到了相互冲突的生命周期问题。

您可以Timer100% 控制其生命周期。你开始它,你停止它,就是这样。但是您无法直接自省 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()知道何时关闭,这可以追溯到“谁在这里控制”的事情。


推荐阅读