scala notify()vs notifyAll()

use*_*000 6 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; }
}
Run Code Online (Sandbox Code Playgroud)

有三件事我不太明白:

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

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

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

yak*_*ver 5

这实际上是一个关于Java并发的问题.Scala并发性建立在Java并发模型之上,但语法不同.在Scala中,synchronized是一种方法AnyRef,上面的语法相当于使用关键字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; 
  }
}
Run Code Online (Sandbox Code Playgroud)

有关Java并发模型的更详细介绍,请仔细阅读Java教程.您可能想要研究Java并发库.例如,您可以使用容量为1 的阻塞队列实现相同的功能.

在回答你的问题时:

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

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

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

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

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

有关JVM如何执行线程同步的详细说明,请阅读:Java虚拟机如何执行线程同步.有关同一作者章节中的更多详细信息,您可以阅读Java虚拟机内部.