The following program demonstrated how to use a logger thread to log messages. The main thread created a new LogWriter instance and started it. Upon starting, the LogWriter thread started a new logger thread to take message from a blocking queue and write to System.out. Since the blocking queue is empty at the beginning, the LoggerThread thread is blocked on queue.take(). Then main thread created and started another thread which put a new message to the blocking queue every 1 second for 10 times. Main thread itself put a message into the blocking queue then went to sleep for 2 seconds. Once main thread woke up, it interrupted the LoggerThread so that the LoggerThread can stop and exit.
This program has a serious problem, though -- it can not exit. Simply making the logger thread exit left the blocking queue unattended. The producers are still put messages into the blocking queue after the logger thread is no longer alive. Threads blocked in log because the queue is full will never become unblocked, as a result, the program can not exit. Cancelling a producer-consumer activity requires cancelling both the producers and the consumers.
Besides, even we interrupt the logger thread after all Producer finished log messages, interrupt the logger thread will leave a few message in the queue not print to System.out.
CHAP7>cat LogWriter.java
import java.io.PrintWriter;
import java.io.Writer;
import java.util.concurrent.*;
/**
* LogWriter
* <p/>
* Producer-consumer logging service with no shutdown support
*/
public class LogWriter {
private final BlockingQueue<String> queue;
private final LoggerThread logger;
private static final int CAPACITY = 2;
public LogWriter(Writer writer) {
this.queue = new LinkedBlockingQueue<String>(CAPACITY);
this.logger = new LoggerThread(writer);
}
public void start() {
logger.start();
}
public void log(String msg) throws InterruptedException {
queue.put(msg);
}
private class LoggerThread extends Thread {
private final PrintWriter writer;
public LoggerThread(Writer writer) {
this.writer = new PrintWriter(writer, true); // autoflush
}
public void run() {
try {
while (true)
writer.println(queue.take());
} catch (InterruptedException ignored) {
ignored.printStackTrace();
} finally {
writer.close();
}
}
}
public static void main(String...args) {
PrintWriter writer = new PrintWriter(System.out);
LogWriter log = new LogWriter(writer);
log.start();
Thread th = new Thread(() -> {
try {
for(int i = 1; i < 10; i++) {
log.log("msg " + i);
System.out.println("submited msg " + i);
//block if queue is full
Thread.sleep(1000);
}
} catch(Exception e) {}
});
th.start();
try {
log.log("start to log");
Thread.sleep(2000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
log.logger.interrupt();
}
}
CHAP7>javac LogWriter.java
CHAP7>java LogWriter
submited msg 1
start to log
msg 1
submited msg 2
msg 2
submited msg 3
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at LogWriter$LoggerThread.run(LogWriter.java:38)
^CCHAP7>