java中的Producer和Consumer使用notify()和wait()运行4个线程

问题描述 投票:1回答:1

最近,我在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的字母,但它总是被卡住了。我知道一定有什么不对劲,但我自己也搞不清楚。实际上,我不知道它有什么问题。那么,有人会帮助我吗?谢谢!

java multithreading wait producer-consumer
1个回答
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方法上执行同步并不能完成任何事情。每个线程都在线程的生命周期内获取一个锁。必须共享锁才能使用。它所做的只是让任何一个线程加入等待状态变得困难。

© www.soinside.com 2019 - 2024. All rights reserved.