这实际上是一个主要关于 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;
}
}
有关 Java 并发模型的更详细介绍,请阅读Java 教程。您可能想要研究Java 并发库。例如,您可以使用容量为 1的阻塞队列来实现相同的功能。
在回答您的问题时:
1. 为什么,如果我使用 notify 而不是 notifyAll,我最终会陷入僵局;我应该在哪里使用 notifyAll,用于生产或消费?
您可能有一个竞争条件(而不是死锁),因为notify()
inconsumer()
是由其他一些消费者线程而不是生产者线程接收的,但这只是一个猜测。至于是否使用notify()
或notifyAll()
以何种方式使用,有人建议notifyAll()
总是使用。而且,在这种情况下,您可以使用notifyAll()
, 因为您在条件 while 循环中等待 - 由于wait()文档中描述的各种原因,您应该始终这样做。但是,您也可以选择在 中notify()
用作优化producer()
,因为我假设您只希望一个消费者使用缓冲区内容。使用当前的实现,您仍然必须使用notifyAll()
inconsume()
或者可能将自己暴露于通知其中一个消费者而不是单个等待的生产者导致生产者永远等待的情况。
2.我不应该有一个对象,例如锁,并调用lock.synchronized、lock.wait和lock.notify吗?为什么它会这样工作,不生产和消费有 2 个不同的监视器关联?为什么生产的“通知”通知消费的“等待”?
你确实有一把锁。这是对PC
实例的隐式锁定,在 Java 中,每个对象只有一个监视器,尽管可能有许多入口点。wait()
in由inconsume()
通知,因为它们都在等待对同一资源(实例)的锁定。如果您想实现更灵活或更细粒度的锁定,则可以使用Java 并发库中的各种策略,例如Locks。notify()
produce()
PC
3. 监视器在 scala 中是如何工作的(在我们的例子中)?它是否使用信号并继续策略?如何在特定条件下将等待队列中的进程移动到可运行队列?每个条件/锁是否有一个队列(例如 lock1.wait、lock2.wait 等)。
有关 JVM 如何执行线程同步的详细说明,请阅读以下内容:Java 虚拟机如何执行线程同步。有关同一作者的一章的更多详细信息,您可以阅读Java 虚拟机内部。