请在下面找到消费者生产商代码:
//制片人
public boolean busy = false;
while (rst != null && rst.next()) {
while (queue.size() == 10) {
synchronized (queue) {
try {
log.debug("Queue is full, waiting");
queue.wait();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
synchronized (queue) {
queue.add(/* add item to queue */);
busy = true;
queue.notifyAll();
}
}
//消费者
try {
while (!busy) {
while (queue.isEmpty()) {
synchronized (queue) {
try {
log.debug("Queue is empty, waiting");
queue.wait();
} catch (InterruptedException ex) {
ex.getMessage();
}
}
}
synchronized (queue) {
item = queue.remove();
log.debug("Consumed item" + ++count + " :" + item);
busy = false;
queue.notifyAll();
}
}
在我的生产者代码片段中,我已经同步了队列(链接阻塞队列),我用它来添加元素并使全局布尔变量busy为true并通知消费者。一旦队列大小为10,生产者就会释放对象锁并进入等待状态。 对于我的消费者,一旦全局标志忙碌,消费者就会消耗元素并将标志变为假。 这些元素是正确生成和使用的。但是我的代码并没有终止于生产者消费者循环之外,要执行的最终语句是“队列是空的,等待”。 请让我知道如何修改我的代码和终止条件以退出循环。
答案 0 :(得分:4)
您的代码被破坏的方式很多,很难决定,从哪里开始。首先,您的消费者完全包含在while(!busy){
循环中,因此一旦busy
变为true
,它将退出循环,而不是开始使用项目。但是,由于您在任何同步机制之外访问busy
变量,因此无法保证此线程将看到另一个线程写入的true
值。此时,它“帮助”消费者在wait
返回时没有真正检查队列的状态,而只是假设有些项目(不保证,读取“spurios wakeups”等)。{{ {3}}解释了如何正确等待。
但是还有更多错误无法正确同步。在你说的制作人
while(queue.size() == 10){
synchronized(queue){
…
换句话说,您正在通过调用size()
而没有正确同步来访问队列。
另一个问题是您的异常处理:
catch(InterruptedException ex)
{
ex.getMessage();
}
调用getMessage
但忽略结果就像根本不调用它一样。你应该在这里添加一个真实的动作。即使是简单的print
语句也比这更好。
此外,将等待部分和生产/消费部分拆分为两个不同的synchronized
块可能会有效,只要您有一个生产者和一个消费者,但只要您有更多的线程,它就会被打破,因为在在这两个synchronized
块之间,队列的状态可能会再次发生变化。
正确地做到这一点并不困难,只需简化您的代码:
//Producer
public volatile boolean producerDone = false;
while( /* can produce more items */ ) {
Item item=produceItem();
synchronized(queue) {
while(queue.size() == 10) try {
log.debug("Queue is full, waiting");
queue.wait();
} catch(InterruptedException ex) {
ex.printStackTrace();
}
queue.add(item);
queue.notifyAll();
}
}
producerDone=true;
//Consumer
while(!producerDone) {
Item item;
synchronized(queue) {
while(queue.isEmpty()) try {
log.debug("Queue is empty, waiting");
queue.wait();
} catch(InterruptedException ex) {
ex.printStackTrace();
}
item = queue.remove();
queue.notifyAll();
}
processItem(item);
}