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 the wait()/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 called notify() 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 of n 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 would wait() on the lock for new work and the next notify() 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. All notify() 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 call wait() and just drops its value off, sets the boolean flag and calls notify().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With