我正在使用多线程处理经典的生产者-消费者问题。我在代码中使用wait()和notifyAll()。我的问题是,当notifyAll通知另一个等待线程恢复时,它不是立即恢复。为什么会这样呢?代码如下
public class ConsumerProducer {
private int count;
public synchronized void consume() {
while (count == 0) { // keep waiting if nothing is produced to consume
try {
wait(); // give up lock and wait
} catch (InterruptedException e) {
// keep trying
}
}
count--; // consume
System.out.println(Thread.currentThread().getName() + " after consuming " + count);
}
public synchronized void produce() {
count++; //produce
System.out.println(Thread.currentThread().getName() + " after producing " + count);
notifyAll(); // notify waiting threads to resume
}
}客户代码:
public class ConsumerProducerTest implements Runnable {
boolean isConsumer;
ConsumerProducer cp;
public ConsumerProducerTest(boolean isConsumer, ConsumerProducer cp) {
this.isConsumer = isConsumer;
this.cp = cp;
}
public static void main(String[] args) {
ConsumerProducer cp = new ConsumerProducer(); //shared by both threads to communicate
Thread producer = new Thread(new ConsumerProducerTest(false, cp));
Thread consumer = new Thread(new ConsumerProducerTest(true, cp));
producer.start();
consumer.start();
//producer.start();
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
if (!isConsumer) {
cp.produce();
} else {
cp.consume();
}
}产出如下:
Thread-1 after producing 1
Thread-1 after producing 2
Thread-1 after producing 3
Thread-1 after producing 4
Thread-1 after producing 5
Thread-1 after producing 6
Thread-1 after producing 7
Thread-1 after producing 8
Thread-1 after producing 9
Thread-1 after producing 10
Thread-2 after consuming 9
Thread-2 after consuming 8
Thread-2 after consuming 7
Thread-2 after consuming 6
Thread-2 after consuming 5
Thread-2 after consuming 4
Thread-2 after consuming 3
Thread-2 after consuming 2
Thread-2 after consuming 1
Thread-2 after consuming 0打印上面的第一行后,调用notifyAll,等待线程恢复,打印Thread-2 after consuming 0。我看到的问题是在Thread-1 (Thread-1 after producing)完成之后,线程-2被恢复.但两者都应该同时发生吗?请帮帮我。感谢编辑:在main方法中有join(),没有更改输出:
producer.start();
producer.join();
consumer.start(); 发布于 2013-08-29 22:04:36
但两者都应该同时发生吗?
这里的问题是生产(和消费)所需的时间非常少。您看到的是所谓的(非关键的) 种族条件,生产者甚至能够在消费者开始之前生产所有的10个产品。在竞争条件下,我的意思是您的两个线程正在相互竞争以生成和消费,而不是在本例中的bug。
如果您将测试增加到(比方说) 100000项,您将看到生成和消费消息混合在一起,尽管即使这样,您也可能看到10条或更长的producing和consuming消息块。
另一件要尝试的事情是先启动消费者,然后在启动它之后放置一个Thread.sleep(10);,这样它就可以等待生产者了。然后,消费者将有时间调用wait(),并将在第一个notifyAll()被调用时从WAIT移动到BLOCKED。但是即使这样,争用条件也可能在producing之前显示所有的consuming消息。这就是多线程应用程序的异步特性。
附注:正确处理InterruptedException总是一种很好的模式。你应该做这样的事情:
try {
wait(); // give up lock and wait
} catch (InterruptedException e) {
// reset the thread interrupt flag
Thread.currentThread().interrupt();
// probably stopping the thread is best
return;
}发布于 2013-08-29 22:33:16
正如这里的大多数人告诉您的,程序输出并不是不正确的。理解原因是你的挑战。
调用.notifyAll()不能保证通知线程(生产者)立即停止执行,也不能保证等待通知的线程(使用者)立即开始执行。这两个线程可以“并发”执行,也可以不执行。线程受操作系统线程调度器的支配。如果要增加for-循环中的迭代次数,您可能会看到消息开始交叉,因为操作系统交换了CPU上和下的两个线程,但是它们不一定会交织--没有什么必须的--它们只是可能的。此外,交织的粒度(如果发生的话)也没有得到保证。
发送通知只是保证如果另一个线程在“等待”中被阻塞,该线程最终将被唤醒。此外,消费者也不一定会总共醒来10次。通知只对在“等待”调用中被阻塞的线程有意义。可能发生的情况是,您发送了10个通知,但只输入和退出对.wait的调用一次(或从未)。如果在.wait中没有线程时发送通知,则该通知将“丢失”。这就是为什么其他一些信息通常与等待/通知结合使用,如计数、布尔值或队列。
发布于 2013-08-29 22:08:21
来自notifyAll(和notifyAll)文档
在当前线程放弃此对象的锁之前,唤醒的线程将无法继续执行。唤醒的线程将按照通常的方式与可能在此对象上积极竞争以同步的任何其他线程竞争;例如,唤醒的线程在作为下一个锁定该对象的线程时没有任何可靠的特权或缺点。
您所看到的只是这两个线程是如何被调度的。
https://stackoverflow.com/questions/18521674
复制相似问题