生产者-消费者日志记录服务以不可靠的方式关闭

Producer-Consumer Logging service with Unreliable way to shutdown

我正在阅读 'Java Concurrency in Practice',其中一个例子让我感到困惑,它是关于生产者-消费者日志记录服务的:

public class LogWriter {
    private final BlockingQueue<String> queue;
    private final LoggerThread logger;
    private boolean shutdownRequested = false;
    public LogWriter(Writer writer) {
        this.queue = new LinkedBlockingQueue<String>(CAPACITY);
        this.logger = new LoggerThread(writer);
    }
    public void start() { logger.start(); }
    public void shutdownlog() { shutdownRequested = true; }
    public void log(String msg) throws InterruptedException {
        if (!shutdownRequested)
            queue.put(msg);
        else
            throw new IllegalStateException("logger is shut down");
    }
    private class LoggerThread extends Thread {
        private final PrintWriter writer;
        ...
        public void run() {
            try {
                while (true)
                   writer.println(queue.take());
            } catch(InterruptedException ignored) {
            } finally {
                writer.close();
            }
        } 
    }
}

从书上看,如果我们关闭它是不可靠的。它写道:

另一种关闭 LogWriter 的方法是设置一个 "shutdown requested" 标志以防止提交更多消息,如清单 7 所示。14.The 消费者然后可以耗尽在收到已请求关闭的通知后排队,写出所有未决消息并解除阻塞在日志中的任何生产者。但是,这种方法具有竞争条件,使其不可靠。 log 的实现是一个 checkthenact 序列:生产者可以观察到服务尚未关闭,但在关闭后仍在排队消息,同样存在生产者可能被阻塞的风险登录并且永远不会畅通无阻。有一些技巧可以降低这种可能性(比如让消费者在宣布队列耗尽之前等待几秒钟),但这些不会改变根本问题,只是会导致失败的可能性。

不太明白。这是否意味着在 shutdownflag 设置为 true 之后另一个线程恰好 运行 进入 queue.put(msg)?

谢谢大家。

漏洞在生产者通过检查shutdownRequested检查标志然后将消息放入队列之间。如果关闭发生并且工作人员在这个微小的时间跨度内停止执行,则会发生这种情况。 尽管这不太可能,但您可能会在已设置标志时将消息排队。

但我看不到生产者被阻止,因为工作人员只是忽略了关闭标志。

如果生产者在队列已满时尝试将消息入队,它会阻塞,但当工作程序从队列中取出元素时会解除阻塞。