我有一个包含多个生产者的队列 - 一个消费者。消费者定期运行并完全排空队列(之后没有消息)。 优雅的算法应该运行使用者并使用超时等待它,或者等待消费者已经在运行。
目前我们已经这样了:
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)或双重等待逻辑,时间间隔作为对象字段等。 顺便说一句,毒丸模式出现在我的脑海里......
答案 0 :(得分:1)
您是否在这里谈论如何正常关闭您的应用程序应该执行以下操作?
我可能需要了解你是如何排空队列的,但是如果你能够以可中断的方式做到这一点,你可以在获得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
将尝试在任何当前运行的线程上调用中断)。