Search code examples
javamultithreadingconcurrencyproducer-consumer

Producer-Consumer Logging service with Unreliable way to shutdown


I'm reading 'Java Concurrency in Practice', one example sort of makes me confused, it's about producer-consumer logging service:

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();
            }
        } 
    }
}

From the book, it's unreliable if we would shutdown it. It wrote:

Another approach to shutting down LogWriter would be to set a "shutdown requested" flag to prevent further messages from being submitted,as shown in Listing7.14.The consumer could then drain the queue upon being notified that shutdown has been requested, writing out any pending messages and unblocking any producers blocked in log. However, this approach has race conditions that make it unreliable. The implementation of log is a checkthenact sequence: producers could observe that the service has not yet been shut down but still queue messages after the shutdown,again with the risk that the producer might get blocked in log and never become unblocked. There are tricks that reduce the likelihood of this (like having the consumer wait several seconds before declaring the queue drained), but these do not change the fundamental problem, merely the likelihood that it will cause a failure.

I don't quite understand it. does it mean that another thread happens to run into queue.put(msg) right after the shutdownflag set to true?

Thanks, guys.


Solution

  • The loophole is between the producer checking the flag by checking shutdownRequested and then putting the message in the queue. If shutdown happens and the worker stops performing in this tiny timespan, this happens. Although its unlikely, it might happen that you enqueue messages when flag is already set.

    But I cant see that the producer getting blocked, because the worker simply ignores the shutdown flag.

    If the producer tries to enqueue a message while the queue is full it blocks, but becomes unblocked when the worker takes elements from the queue.