java中的生产者和消费者使用notify()和wait()运行4个线程

java中的生产者和消费者使用notify()和wait()运行4个线程,java,multithreading,wait,producer-consumer,Java,Multithreading,Wait,Producer Consumer,最近,我学习了Java线程通信中的notify和wait,并尝试编写消费者和生产者的经典问题,在我的代码中,我实际上有4个线程,2个是消费者,另外2个是生产者 package producer_consumer; class Shared { private volatile boolean writable = true; public Character character = 'A'; public synchronized void produceChar(Ch

最近,我学习了Java线程通信中的notify和wait,并尝试编写消费者和生产者的经典问题,在我的代码中,我实际上有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();

    }
}
包装生产商与消费者;
类共享{
private volatile boolean writable=true;
公共字符='A';
公共同步的void produceChar(字符c){
而(!可写){
试一试{
等待();
}捕捉(中断异常e){
e、 printStackTrace();
}
}
可写=假;
字符=c;
通知();
}
公共同步的void consumerChar(){
while(可写){
试一试{
等待();
}捕捉(中断异常e){
e、 printStackTrace();
}
}
可写=真;
通知();
}
}
公营电脑{
公共静态void main(字符串[]args){
共享=新共享();
类生成器扩展线程{
@凌驾
公共同步的无效运行(){

对于(Character Character='A';Character,当代码调用notify时,它告诉调度程序从waitset中为调用notify的锁选择一个线程,并将其唤醒。调度程序不知道线程正在等待什么特定条件,也不知道它将选择哪个

当您有多个线程时,其中一些线程正在等待不同的条件(这里的条件是可写和不可写的),则可能会通知某个线程它不感兴趣的条件。一旦通知的线程发现它要查找的条件不存在,并且没有其他线程接收到该条件,该线程就会返回等待状态。这意味着没有人会因为该事件而取得进展

例如:

1) 第一个生产者执行,writable为true,让它跳过等待,写入s char,调用notify(无人监听),并将writable标志翻转为false

2) 上下文切换到第二个生产者,它发现可写为false,所以等待

3) 在这一点上,调度器可以运行一个消费者(如果消费者已经启动),或者它可以切换回第一个生产者。 假设它选择了生产者。第一个生产者看到writeable仍然是false,所以它等待

4) 第一个使用者运行。Writable为false,因此无需等待;它将Writable标志翻转为true并调用notify

5) 现在两个制作人正在等待,通知将唤醒其中一个,另一个仍在等待

6) 可以选择第一个消费者再次运行,writable为true,因此它会等待。现在有一个生产者和一个消费者在等待

7) 此时,计划程序可以选择剩余的活动使用者或剩余的活动生产者。如果它选择生产者,则生产者可以采取行动,然后调用notify。可以通知任一等待线程。只有一个线程可以对通知采取行动

一种解决方案是使用notifyAll。这会唤醒waitset中的所有线程,因此如果其中任何线程感兴趣,就会收到通知。这并不是一种适用于所有情况的完美解决方案;在一个有大量线程的程序中,这可能意味着对大多数线程进行大量无效的上下文切换和状态转换,而这些线程最终都会失败回到等待状态,但没有取得任何进展。对于一个小程序来说,这当然不是问题

一个没有notifyAll缺点的现实解决方案是使用ReentrantLock,它允许单独的条件。这样线程就可以等待特定的条件对象,结果通知只发送给正在等待特定条件的线程

api doc for Condition有一个有界固定大小队列的示例,该队列显示线程在不同的条件对象上等待,具体取决于它们是生产者还是消费者。条件既不是空的,也不是满的。向满队列插入内容的线程必须等待非满条件。尝试从emp中取出项目的线程ty队列等待非空条件


顺便说一句,将synchronized放在run方法上并不能完成任何事情。每个线程都会在线程的生命周期内获取自身的锁。锁必须共享才能发挥作用。它所做的一切都会使加入其中任何一个方法的线程很难进入等待状态。

当代码调用notify时,这会告诉调度程序to为调用notify on的锁从waitset中选择一个线程,并将其唤醒。调度程序不知道线程正在等待的具体条件,也不知道它将选择哪一个

当您有多个线程时,其中一些线程正在等待不同的条件(这里的条件是可写和不可写的),则可能会通知某个线程它不感兴趣的条件。一旦通知的线程发现它要查找的条件不存在,并且没有其他线程接收到该条件,该线程就会返回等待状态。这意味着没有人会因为该事件而取得进展

例如:

1) 第一个生产者执行,writable为true,让它跳过等待,写入s char,调用notify(无人监听),并将writable标志翻转为false

2) 上下文切换到第二个生产者,它发现可写为false,所以等待

3) 在这一点上,调度器可以运行一个消费者(如果消费者已经启动),或者它可以切换回第一个生产者。 假设它选择了生产者。第一个生产者看到writeable仍然是false,所以它等待

4) 第一个使用者运行。Writable为false,因此无需等待;它将Writable标志翻转为true并调用notify

5) 现在两个制作人正在等待,通知将唤醒其中一个,另一个仍在等待

(六)