scala notify()vs notifyAll()

时间:2013-02-25 18:43:05

标签: multithreading scala

鉴于scala中生产者 - 消费者问题的以下实现

class PC {
  var buffer = null.asInstanceOf[Int]
  var set = false

  def produce(value: Int) = synchronized {
    while (set) wait()
    buffer = value
    set = true
    notify() }

  def consume: Int = synchronized {
    while (!set) wait()
    val result = buffer
    set = false
    notify()
    return result; }
}

有三件事我不太明白:

  1. 为什么,如果我使用notify而不是notifyAll,我最终陷入僵局;我应该在哪里使用notifyAll,用于生产或消费?

  2. 我不应该有一个对象,例如锁定,并调用lock.synchronized,lock.wait和lock.notify?为什么它会像这样工作,不生产和消费有2个不同的监视器关联?为什么来自“生产”的“通知”会通知消费“等待”?

  3. 监视器如何在scala中完全正常工作(在我们的例子中)?它是否使用信号继续策略?如何将某个条件下等待队列的进程移动到可运行队列?每个条件/锁都有一个队列(例如lock1.wait,lock2.wait等)。

2 个答案:

答案 0 :(得分:5)

这实际上是一个关于Java并发的问题。 Scala并发性建立在Java并发模型之上,但语法不同。 In Scala, synchronized is a method of AnyRef,上述语法等同于使用关键字synchronized编写 synchronized方法,如以下Java代码所示:

public class PC {
  public int buffer;
  public boolean set;
  public synchronized void produce(int value) { 
    while(set) wait();
    buffer = value;
    set = true;
    notify(); 
  }
  public synchronized int def consume { 
    while(!set) wait();
    int result = buffer;
    notify();
    return result; 
  }
}

有关Java并发模型的更详细介绍,请仔细阅读Java Tutorials。您可能需要调查Java Concurrency Library。例如,您可以使用容量为1的Blocking Queue实现相同的功能。

回答你的问题:

<强> 1。为什么,如果我使用notify而不是notifyAll,我最终陷入僵局;我应该在哪里使用notifyAll,用于生产或消费?

你可能有一个竞争条件(而不是死锁),因为notify()中的consumer()是由其他一些消费者线程而不是生产者线程接收的,但这只是猜测。至于是否使用notify()notifyAll()以及使用哪种方法,有些人建议始终使用notifyAll()。并且,在这种情况下,您可以使用notifyAll(),因为您正在等待有条件的while循环 - 您应该始终出于wait()文档中描述的各种原因。但是,您也可以选择notify()作为producer()中的优化,因为我假设您只希望一个消费者使用缓冲区内容。对于当前实现,您仍然必须在notifyAll()中使用consume(),或者可能使自己暴露于其中一个消费者被通知而不是单个等待生成者导致生产者永远等待的情况。

<强> 2。我不应该有一个对象,例如锁定,并调用lock.synchronized,lock.wait和lock.notify?为什么它会像这样工作,不生产和消费有2个不同的监视器关联?为什么“生产”中的“通知”会通知消费“等待”?

你确实有锁。它是对PC实例的隐式锁定,而在Java中,每个对象只有一个监视器,尽管可能有许多入口点。 wait()中的consume()会通知notify()中的produce(),因为他们都在等待锁定同一资源 - PC实例。如果您想实现更灵活或更细粒度的锁定,那么您可以使用Java Concurrency Library中的各种策略,例如Locks

第3。监视器如何在scala中完全工作(在我们的例子中)?它是否使用信号继续策略?如何将某个条件下等待队列的进程移动到可运行队列?每个条件/锁都有一个队列(例如lock1.wait,lock2.wait等)。

有关JVM如何执行线程同步的详细说明,请阅读:How the Java virtual machine performs thread synchronization。有关同一作者章节的详细信息,请阅读Inside the Java Virtual Machine

答案 1 :(得分:1)

为什么,如果我使用notify而不是notifyAll,我最终陷入僵局?

正如您可能已经观察到的那样,如果只有一个生产者和一个消费者,那么您所见证的问题就不会发生(因为在这种情况下,notify完成了您希望它做的工作,这就是让下一个生产者/消费者采取行动。

但是,如果您有多个生产者或消费者,则会出现以下问题:假设有2个生产者和一个消费者。在这种情况下,如果您使用notify()

,则必然会发生以下情况
  1. 其中一个制作人运行,并调用notify()
  2. 通知其他制作人
  3. 而不是消费者
  4. notify()唤醒的制作人现在无限期等待,因为消费者永远不会得到通知
  5. 如果调用notifyAll,将始终有一个消费者得到通知,因此生产者或消费者无限期地等待另一方的问题永远不会出现。

    我不应该有一个对象,例如锁定,并调用lock.synchronized,lock.wait和lock.notify?

    您的锁定对象是PC对象。 Scala object只是编译器为您生成的类的单例实例。由于您的object实际上是类Object的类实例,因此它还会继承其notifynotifyAllwait方法。