class Q {
volatile boolean valueSet = false;
volatile int n;
synchronized int get () {
if ( !valueSet ) {
try {
wait();
} catch ( InterruptedException e ) {
System.out.println( "InterruptedException caught" );
}
}
System.out.println( "Got: " + n );
valueSet = false;
notify();
return n;
}
synchronized void put ( int n ) {
if ( valueSet ) {
try {
wait();
} catch ( InterruptedException e ) {
System.out.println( "InterruptedException caught" );
}
}
this.n = n;
valueSet = true;
System.out.println( "Put: " + n );
notify();
}
}
class Producer
implements Runnable {
Q q;
Producer ( Q q ) {
this.q = q;
new Thread( this, "Producer" ).start();
}
public void run () {
int i = 0;
while ( true ) {
q.put( i++ );
}
}
}
class Consumer
implements Runnable {
Q q;
Consumer ( Q q ) {
this.q = q;
new Thread( this, "Consumer" ).start();
}
public void run () {
while ( true ) {
q.get();
}
}
}
class PCFixed {
public static void main ( String args[] ) {
Q q = new Q();
new Producer( q );
new Consumer( q );
System.out.println( "Press Control-C to stop." );
}
}
I'm unable to understand how this works. Take this flow for instance. Producer enters put method and calls notify(). What if wait() has not been called by the consumer yet? Also, once the producer calls notify(), how can the consumer enter the method get() when the producer has not let go of the monitor yet? Please help me out here.
I'm assuming that the class at the top is Q, it's missing some code. Anyway, the general idea is that the boolean
valueSet
and thewait()
/notify()
calls work in tandem.If the consumer has already begun to wait, he has acquired the lock to the Q instance through the synchronized
get()
method and then releases it while he waits.If the consumer has not begun to wait yet, the producer may have the lock to the Q instance because the
put()
method is synchronized on that same lock. Once the producer exits the lock, he will have callednotify()
as well as set the valueSet boolean to true.The consumer's next call to
get()
will read the boolean value before attempting to wait, notice that something's there, take the contents ofn
and do whatever work is needed. If the value had not been set, meaning that nothing had come in in the consumer's absence, he wouldwait()
on the lock for new work and the nextnotify()
will wake him up.Update
In your scenario in the comments, you're essentially asking about the exact same situation but in reverse. The same logic applies.
The consumer is currently waiting and the producer calls
notify()
. The producer currently has the lock and will continue to hold the lock for the duration of the method. Allnotify()
does is let one other thread currently waiting on the lock know that, when the lock is released, it can try to take the lock and resume execution. In this case, there is only one other thread, but if there were multiple threads then it would select only one (to wake everyone,notifyAll()
should be called).At this point, it's ambiguous as to whether the producer has already come around and is currently waiting on the lock or if it has not entered the method yet. The same tandem of the boolean flag and
wait()
/notify()
applies to this as well.Before the consumer releases the lock by exiting the method, it will both set the boolean flag to false and call
notify()
.If the producer is currently already waiting on the lock, calling
notify()
will let it know that it can wake up and continue when the lock is released.If the producer is not waiting through a
wait()
call, it must be outside the method (possibly waiting to enter the method and acquire the lock that way). When the consumer exits the method and releases the lock, the producer acquires it and checks the boolean flag. It's been set to false, therefore the producer does not attempt to callwait()
and just drops its value off, sets the boolean flag and callsnotify()
.I'll try to understand how this works. For example, the
Producer
has entered theput()
method and saw that the monitor is free and he can execute the putting action. At the time he's doing his job here comes theConsumer
and tries to execute theget()
action. But he fails since the monitor is already taken by theProducer
, so he callswait()
meaning he is blocked at that line of code waiting for some other thread to callnotify()
. The trick here is that he never again callsget()
before he's been notified that the monitor is free again, means he won't execute any more empty cycles. So, when theProducer
has finished his work, he callsnotify()
and theConsumer
resumes where he left off. I know it's a bit hard to understand at first and a bit hard to explain too, but when you do you'll see that these are really simple and powerful things. Good luck!