我正在阅读B. Goetz JCIP并在第7.2节中遇到一些关于取消基于线程的服务的误解。这是代码:
public class LogWriter{
private final BlockingQueue<String> queue;
private final LoggerThread logger;
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 void run() {
try{
while(true)
writer.println(queue.take());
} catch(InterruptedException ignored){
} finally {
writer.close();
}
}
}
他说这种服务没有提供终止它的方法。他给出了另一种选择:
public void log(String msg) throws InterruptedException {
if(!shutdownRequested)
queue.put(msg);
else
throw new IllegalArgumentException("logger is shut down");
}
现在他说那个
日志的实施是chek-than-act sequnce:生产者可以 观察该服务尚未关闭但仍在排队 关机后的消息,再次冒着生产者的风险 可能会在日志中被阻止,永远不会被解锁。
强调我并不清楚。
如果消费者将队列排到某个集合,它将使log()
中的任何生产者被锁定。即使某些生产者尝试将日志消息放入队列,也不会被阻止。我看到的唯一一件事是,由于队列已耗尽,因此不会记录此消息。
问题: 为什么他说生产商很难被阻止而且从未解锁过。我错过了什么?
答案 0 :(得分:1)
如果查看BlockingQueue
doc,您可以看到:
另外支持等待队列的操作的队列 在检索元素并等待空间时变为非空 存储元素时在队列中可用。
即。如果队列中没有剩余空间,则可能会阻止生产者:如果服务已关闭,则队列不再耗尽。
答案 1 :(得分:1)
由于代码不完整,很难说出作者的意图。据推测,记录器线程还会检查shutdownRequested
以停止记录:
public void run() {
try{
while(shutdownRequested)
writer.println(queue.take());
} catch(InterruptedException ignored){
} finally {
writer.close();
}
}
如果你写这个,考虑一个场景,当队列已满并且queue.put
上有线程被阻塞时请求关闭:记录器线程停止调用queue.take
并且这些线程永远被阻止。
即使不是这种情况,也存在变量shutdownRequested
的竞争条件。线程可以请求在读取变量和消息在log
方法中排队之间关闭,以便在请求关闭后消息仍然排队。