通过超时定期消费者优雅停止队列的优雅方式

时间:2011-04-25 07:37:05

标签: java multithreading concurrency message-queue

我有一个包含多个生产者的队列 - 一个消费者。消费者定期运行并完全排空队列(之后没有消息)。 优雅的算法应该运行使用者并使用超时等待它,或者等待消费者已经在运行。

目前我们已经这样了:

void stop(boolean graceful) {
        if (graceful && !checkAndStopDirectly()) {
            executor.shutdown();
            try {
                if (!executor.awaitTermination(shutdownWaitInterval, shutdownWaitIntervalUnit)) {
                    log.warn("...");
                }
            } catch (InterruptedException e) {
                log.error("...", e);
            }
        } else {
            executor.shutdownNow();
        }

private boolean checkAndStopDirectly() {
    ExecutorService shutdownExecutor = Executors.newSingleThreadExecutor();
    try {
        return shutdownExecutor.submit(new Callable<Boolean>(){
            @Override
            public Boolean call() throws Exception {
                if (isAlreadyRan.compareAndSet(false, true)) {
                    try {
                        runnableDrainTask.run();
                    } finally {
                        isAlreadyRan.set(false);
                    }
                    return true;
                } else {
                    return false;
                }
            }
        }).get(shutdownWaitInterval, shutdownWaitIntervalUnit);

有人看到更优雅的方式吗? 例如我正在寻找一种方式,不使用额外的AtomicBoolean(isAlreadyRan)或双重等待逻辑,时间间隔作为对象字段等。 顺便说一句,毒丸模式出现在我的脑海里......

1 个答案:

答案 0 :(得分:1)

您是否在这里谈论如何正常关闭您的应用程序应该执行以下操作?

  1. 等待排队,或
  2. 如果排水耗时太长,
  3. 超时
  4. 我可能需要了解你是如何排空队列的,但是如果你能够以可中断的方式做到这一点,你可以在获得Future时超时并尝试{{ 1}}(如果没有完全耗尽则中断)无论如何;这看起来怎么样?

    shutdownNow

    ExecutorService pool = Executors.newSingleThreadExecutor(); public void stop() { try { pool.submit(new DrainTask()).get(100, MILLISECONDS); } catch (TimeoutException e) { // nada, the timeout indicates the queue hasn't drained yet } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (Exception e) { // nada, defer to finally } finally { pool.shutdownNow(); } } private class DrainTask implements Callable<Void> { @Override public Void call() throws Exception { while (!Thread.currentThread().isInterrupted()) ; // drain away return null; } } 部分是否用于防范对compareAndSet的多个并发呼叫?我想我喜欢普通锁定或使用stop代替。但是,如果发生冲突,系统会抛出synchronized并重复拨打ExecutionException

    它依赖于shutdownNow能够阻止其响应中断调用所做的事情(因为DrainTask将尝试在任何当前运行的线程上调用中断)。