Multithreading scala notify()与notifyAll()的比较

Multithreading scala notify()与notifyAll()的比较,multithreading,scala,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 { wh

给出了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; }
}
有三件事我不太明白:

  • 为什么,如果我使用notify而不是notifyAll,我最终会陷入死锁;我应该在哪里使用notifyAll,用于生产或消费

  • 我不应该有一个对象,例如lock,并调用lock.synchronized、lock.wait和lock.notify吗?为什么它是这样工作的,不生产和消费有两个不同的显示器关联?为什么来自产品的“通知”通知来自消费的“等待”

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


  • 这实际上是一个关于Java并发性的问题。Scala并发是建立在Java并发模型之上的,但语法不同,上述语法相当于使用关键字
    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并发模型的更多详细内容,请阅读。你可能想调查这个问题。例如,您可以使用容量为1的实现相同的功能

    回答你的问题:

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

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

    2。我不应该有一个对象,例如lock,并调用lock.synchronized、lock.wait和lock.notify吗?为什么它是这样工作的,不生产和消费有两个不同的显示器关联?为什么来自生产的“通知”通知来自消费的“等待”?

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

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

    有关JVM如何执行线程同步的详细描述,请阅读以下内容:。有关同一作者的章节的更多细节,您可以阅读。

    为什么,如果我使用notify而不是notifyAll,我最终会陷入死锁? 正如您所观察到的,如果只有一个生产者和一个消费者,您所看到的问题就不会发生(因为在这种情况下,
    notify
    完成了您期望它完成的工作,即让下一个生产者/消费者行动)

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

  • 其中一个生产者运行并调用
    notify()
  • 通知的不是消费者,而是其他生产商
  • notify()
    唤醒的生产者现在无限等待,因为消费者永远不会得到通知
  • 如果改为调用
    notifyAll
    ,则始终会有一个消费者收到通知,因此生产者或消费者会无限期地等待另一方

    我不应该有一个对象,例如lock,并调用lock.synchronized、lock.wait和lock.notify吗?
    锁定对象是
    PC
    对象。Scala
    对象
    只是编译器为您生成的类的单例实例。由于您的
    对象实际上是类
    对象的类实例,因此它也继承了它的
    notify
    notifyAll
    wait
    方法。

    p.S.我有一个生产者和多个消费者。尝试使用producer/comsumer,它会更简单。我建议您避免使用wait()和notify(),它们是非常低级的细粒度原语,并使用java.util.concurrent中的内容