最近学习了Java线程通信中的notify和wait,尝试着写出Consumer&Producer的经典问题,在我的代码中,其实有4个线程,2个是consumer,另外2个是producer。
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 的字母,但它总是卡住。我知道肯定有问题,但我无法自己弄清楚。实际上,我不知道它有什么问题。那么,有人会帮助我吗?谢谢!
最佳答案
当您的代码调用 notify 时,它会告诉调度程序从等待集中为您调用 notify 的锁选择一个线程,并将其唤醒。调度程序不知道线程正在等待什么特定条件,也不知道它会选择哪一个。
当你有多个线程时,其中一些线程在不同的条件下等待(这里的条件是可写的和不可写的),那么一个线程可能会收到它不感兴趣的条件的通知。被通知的线程一旦它发现它正在寻找的条件不存在,并且没有其他线程收到它,就会返回等待。这意味着没有人会因为该事件而取得进展。
例子:
1)首先producer执行,writable为真,让它跳过等待,写入s char,调用notify(无人监听),将writable标志翻转为false。
2) 上下文切换到第二个生产者,它发现 writable 是 false 所以它等待。
3) 此时调度器可以运行一个消费者,如果一个消费者通过启动,或者它可以切换回第一个生产者。 假设它选择生产者。第一个生产者看到 writable 仍然是假的,所以它等待。
4) 第一个消费者运行。可写是假的,所以不用等待;它将可写标志翻转为真并调用通知。
5) 现在有 2 个生产者在等待,通知会唤醒其中一个,另一个还在等待。
6) 可以选择第一个消费者再次运行,writable 为 true 所以它等待。现在有一个生产者在等待,一个消费者在等待。
7) 此时,调度程序可以选择剩余的活跃消费者或剩余的活跃生产者。如果它选择了生产者,那么生产者可以采取行动,然后调用通知。可以通知任何一个等待线程。只有一个人可以根据通知采取行动。
一种解决方案是使用 notifyAll。这会唤醒 waitset 中的所有线程,因此如果其中任何一个线程感兴趣,就会通知他们。这不是适用于所有情况的完美解决方案;在一个有很多线程的程序中,对于大多数线程来说,这可能意味着大量非生产性的上下文切换和状态转换,这些线程最终会在没有取得任何进展的情况下最终返回到它们的等待状态。对于一个小程序来说,这当然不是问题。
没有 notifyAll 缺点的现实世界解决方案是使用 ReentrantLock,它允许单独的条件。这样线程就等待特定的 Condition 对象,结果通知只发送给正在等待该特定条件的线程。
Condition 的 api 文档有一个有界固定大小队列的示例,它显示了线程在不同的条件对象上等待,具体取决于它们是生产者还是消费者。条件不空不满。将事物插入已满队列的线程必须等待非满状态。试图从空队列中取出项目的线程等待非空条件。
顺便说一句,将同步放在运行方法上并没有完成任何事情。在线程的生命周期中,每个线程都在获取自身的锁。锁必须共享才能有用。它所做的只是让加入其中任何一个的线程难以进入等待状态。
关于java - java中的Producer和Consumer使用notify()和wait()运行4个线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44188387/