This is really a question mostly about Java concurrency. Scala concurrency is built on top of the Java concurrency model, but the syntax differs. In Scala, synchronized
is a method of AnyRef
, and the above syntax is equivalent to using the keyword synchronized
to write synchronized methods as in the following Java code:
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;
}
}
For more detailed coverage of the Java concurrency model, peruse the Java Tutorials. You may want to investigate the Java Concurrency Library. For instance, you could implement the same thing using a Blocking Queue with a capacity of 1.
In answer to your questions:
1. Why, if I'm using notify instead of notifyAll, I end up in deadlock; Where should I use notifyAll, for in produce or consume?
You probably have a race condition (rather than a deadlock) because the notify()
in the consumer()
is received by some other consumer thread, rather than the producer thread, but that's just a guess. As far as whether to use notify()
or notifyAll()
and in which methods, some recommend that notifyAll()
always be used. And, in this case, you can use notifyAll()
, because you are waiting in a conditional while loop -- which you should always do for various reasons described in the documentation of wait(). However, you could also choose to use notify()
as an optimization in the producer()
, because I'm assuming you only want one consumer to consume the buffer contents. With the current implementation, you must still use notifyAll()
in consume()
or potentially expose yourself to a situation where one of the consumers is notified rather than the single waiting producer resulting in the producer waiting forever.
2. Shouldn't I have an object e.g. lock, and call lock.synchronized, lock.wait and lock.notify? Why does it work like this, don't produce and consume have 2 different monitors associated? Why does 'notify' from produce notifies 'wait' from consume?
You do have a lock. It's an implicit lock on the PC
instance and in Java there's one and only one monitor per object although there may be many entry points. The wait()
in consume()
is notified by the notify()
in produce()
, because they are both waiting for a lock on the same resource -- the PC
instance. If you want to implement more flexible or finer-grained locking, then you can use various strategies from the the Java Concurrency Library such as Locks.
3. How does a monitor work exactly in scala (in our case)? Does it use a signal-and-continue policy? how do processes from a waiting queue on a certain condition get moved to a runnable queue? Is there a queue for each condition/lock (e.g. lock1.wait, lock2.wait, etc).
For a good description of how the JVM performs thread synchronization, read this: How the Java virtual machine performs thread synchronization. For bit more detail from a chapter from the same author, you can read Inside the Java Virtual Machine.