【问题标题】:Producer and Consumer in java use notify() and wait() run 4 threadsjava中的Producer和Consumer使用notify()和wait()运行4个线程
【发布时间】:2017-05-25 19:26:55
【问题描述】:

最近学习了Java线程通信中的notify和wait,试着写了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 的字母,但它总是卡住。我知道一定有什么问题,但我自己无法弄清楚。实际上,我不知道它有什么问题。那么,有人会帮助我吗?谢谢!

【问题讨论】:

  • 有时它可以正常工作,但有时不能。打印几个字符后它会停止
  • 不要同步你的运行方法。它只被调用一次。
  • 你应该使用notifyAll()而不是notify(),因为有超过1个消费者线程和1个生产者线程

标签: java multithreading wait producer-consumer


【解决方案1】:

当您的代码调用通知时,这会告诉调度程序从等待集中为您调用通知的锁选择一个线程,然后将其唤醒。调度程序不知道线程正在等待什么特定条件,也不知道它将选择哪一个。

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

例子:

1)第一个生产者执行,可写为真,让它跳过等待,写s char,调用notify(没有人在监听),并将可写标志翻转为假。

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

3) 此时调度程序可以运行一个消费者,如果一个消费者正在启动,或者它可以切换回第一个生产者。 说它选择了制作人。第一个生产者看到 writable 仍然是假的,所以它等待。

4) 第一个消费者运行。可写是假的,所以不用等待;它将可写标志翻转为真并调用通知。

5) 现在有 2 个生产者在等待,通知会唤醒其中一个,另一个仍在等待。

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

7) 此时调度程序可以选择剩余的活动消费者或剩余的活动生产者。如果它选择了生产者,那么生产者可以采取行动,然后调用通知。可以通知任一等待线程。只有一个人可以对通知采取行动。

一种解决方案是使用 notifyAll。这会唤醒等待集中的所有线程,因此如果它们中的任何一个感兴趣,那么它们将被通知。这不是适用于所有情况的完美解决方案;在一个有很多线程的程序中,对于大多数线程来说,这可能意味着很多非生产性的上下文切换和状态转换,这些线程最终最终回到他们的等待状态而没有取得任何进展。当然,对于一个不是问题的小程序。

没有 notifyAll 缺点的实际解决方案是使用 ReentrantLock,它允许单独的条件。这样,线程等待特定的 Condition 对象,结果通知只发送到等待该特定条件的线程。

Condition 的 api 文档有一个有界固定大小队列的示例,该队列显示线程等待不同的条件对象,具体取决于它们是生产者还是消费者。条件不空也不满。将东西插入满队列的线程必须等待未满状态。试图从空队列中取出项目的线程等待非空条件。

顺便说一句,将同步放在 run 方法上不会完成任何事情。每个线程都在线程的生命周期内获取自己的锁。锁必须共享才能有用。它所做的只是使加入其中任何一个的线程难以进入等待状态。

【讨论】:

  • @David:是的。我应该在这里添加更多细节
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多