最近,我在Java Thread Communication中了解了通知和等待,并且我试着编写Consumer& Producer的经典问题,在我的代码中,我实际上有4个线程,2个是消费者,另外2个是生产者。
package producer_consumer;
class Shared {
private volatile boolean writable = true;
public Character character = 'A';
public synchronized void produceChar(Character c) {
while (!writable) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
writable = false;
character = c;
notify();
}
public synchronized void consumerChar() {
while (writable) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
writable = true;
notify();
}
}
public class PC {
public static void main(String[] args) {
Shared shared = new Shared();
class Producer extends Thread {
@Override
public synchronized void run() {
for(Character character = 'A';character<'Z';character++) {
shared.produceChar(character);
System.out.println(shared.character + " is produced");
}
}
}
class Consumer extends Thread {
@Override
public synchronized void run() {
do {
shared.consumerChar();
System.out.println(shared.character + " is consumed");
}while (shared.character!='Z');
}
}
Producer p1 = new Producer();
Producer p2 = new Producer();
Consumer c1 = new Consumer();
Consumer c2 = new Consumer();
p1.start();
p2.start();
c1.start();
c2.start();
}
}
但是,当我尝试运行代码时,它没有成功。我想它会打印从A到Z的字母,但它总是被卡住了。我知道肯定有问题,但我不能自己解决。实际上,我并不知道它有什么问题。那么,有人会帮助我吗?谢谢!
答案 0 :(得分:4)
当你的代码调用notify时,它告诉调度程序从waitset中选择一个你调用notify on的锁的线程,并将其唤醒。调度程序不知道线程正在等待什么特定条件,并且不知道它将选择哪一个。
当你有多个线程时,其中一些线程在不同条件下等待(这里的条件是可写的而且不可写),那么线程可能会被通知一个它不感兴趣的条件。一旦它找出它所寻找的条件不存在,就会回到等待,没有其他线程接收它。这意味着没有人因此事件而取得进展。
示例:
1)第一个生产者执行,writeable为true,让它跳过等待,写s char,调用notify(无人监听),并将可写标志翻转为false。
2)上下文切换到第二个生成器,它发现writeable是false所以它等待。
3)此时,如果一个人通过启动,调度程序可以运行一个消费者,或者它可以切换回第一个生产者。 说它选择了制作人。第一个生产者认为可写仍然是假的,所以它等待。
4)第一个消费者运行。可写是假的,所以没有等待;它将可写标志翻转为true并调用notify。
5)现在2个生产者正在等待,通知将唤醒其中一个,另一个仍在等待。
6)第一个消费者可以被选中再次运行,可写是真的所以它等待。现在有一个生产者等待,一个消费者在等待。
7)此时,调度程序可以选择剩余的活动使用者或剩余的活动生成者。如果它选择生产者,那么生产者可以采取行动,然后调用通知。可以通知等待线程。只有一个人可以对通知采取行动。
一种解决方案是使用notifyAll。这会唤醒waitset中的所有线程,因此如果他们中的任何一个感兴趣,那么他们将会收到通知。对于所有情况,这都不是完美的解决方案;在具有大量线程的程序中,它可能意味着大多数线程的大量非生产性上下文切换和状态转换,最终最终回到等待状态而没有取得任何进展。当然,对于一个不是问题的小程序。
没有notifyAll缺点的现实世界解决方案是使用ReentrantLock,它允许单独的条件。这样线程在特定的Condition对象上等待,结果是通知只发送到等待该特定条件的线程。
Condition的api doc有一个有界固定大小队列的例子,它显示了在不同条件对象上等待的线程,具体取决于它们是生产者还是消费者。条件不是空的而不是满的。将内容插入完整队列的线程必须等待未满的情况。尝试从空队列中取出项目的线程等待非空状态。
在run方法上执行同步,并没有完成任何事情。每个线程都在线程的生命周期内获取一个锁。必须共享锁才能使用。它所做的只是让任何一个线程加入等待状态变得困难。