【问题标题】:scala notify() vs notifyAll()scala notify() 与 notifyAll()
【发布时间】:2013-02-25 18:43:04
【问题描述】:

鉴于以下 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; }
}

有 3 件事情我不太明白:

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

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

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

【问题讨论】:

  • P.S.我有一个生产者和多个消费者。
  • 尝试使用docs.oracle.com/javase/6/docs/api/java/util/concurrent/…作为生产者/消费者,会更简单
  • 我建议你避免使用 wait() 和 notify(),它们是非常低级的细粒度原语,并使用 java.util.concurrent 中的东西

标签: multithreading scala


【解决方案1】:

这确实是一个主要关于 Java 并发性的问题。 Scala 并发建立在 Java 并发模型之上,但语法不同。 In Scala, synchronized is a method of 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; 
  }
}

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

回答您的问题:

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

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

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

你确实有一把锁。这是PC 实例上的隐式锁定,在Java 中,每个对象只有一个监视器,尽管可能有许多入口点。 consume() 中的wait()produce() 中的notify() 通知,因为它们都在等待锁定同一资源——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

【讨论】:

    【解决方案2】:

    为什么,如果我使用 notify 而不是 notifyAll,我最终会陷入死锁?

    您可能已经观察到,如果只有一个生产者和一个消费者,则不会发生您所看到的问题(因为在这种情况下,notify 会完成您期望它完成的工作,即让下一个生产者/消费者采取行动)。

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

    1. 其中一位生产者运行,并致电notify()
    2. 通知另一个生产者而不是消费者
    3. notify() 唤醒的生产者现在无限等待,因为消费者永远不会收到通知

    如果改为调用notifyAll,总会有消费者收到通知,这样就不会出现生产者或消费者无限期等待对方的问题。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-17
      • 2011-02-19
      • 1970-01-01
      • 1970-01-01
      • 2012-08-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多