I am trying to understand inter-thread communication in Java, and read that the support comes by using: wait()
, notify()
, notifyAll()
methods.
In order thread to execute any of these methods, the thread needs to be owner of object's lock
for which thread is invoking (any of these) methods. In addition to this, all these methods needs to be in a synchronized
block/method. So far good.
I tried to implement a program in which one thread prints odd numbers, and other thread prints even numbers.
The program works correctly, however, at the same time, it raised few more doubts.
Below is the complete source code of the program which I implemented.
package com.example.multithr.implrun;
import com.example.common.ObjectToWaitOn;
public class PrintEvenNumThread implements Runnable {
private ObjectToWaitOn objectToWaitOn;
public PrintEvenNumThread(ObjectToWaitOn objectToWaitOn) {
this.objectToWaitOn = objectToWaitOn;
}
@Override
public void run() {
int numToPrint = 2;
for (;;) {
synchronized (objectToWaitOn) {
while(objectToWaitOn.getPrintEvenOrOdd() != 2) {
try {
objectToWaitOn.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
objectToWaitOn.print("EvenThread",numToPrint);
numToPrint += 2; // Generate next even number
objectToWaitOn.setPrintEvenOrOdd(1);
objectToWaitOn.notifyAll();
}
}
}
}
package com.example.multithr.implrun;
import com.example.common.ObjectToWaitOn;
public class PrintOddNumsThread implements Runnable {
private ObjectToWaitOn objectToWaitOn;
public PrintOddNumsThread(ObjectToWaitOn objectToWaitOn) {
this.objectToWaitOn = objectToWaitOn;
}
@Override
public void run() {
int numToPrint = 1;
for(;;) {
synchronized(objectToWaitOn) {
while(objectToWaitOn.getPrintEvenOrOdd() != 1) {
try {
objectToWaitOn.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
objectToWaitOn.print("OddThread", numToPrint);
numToPrint += 2; // Generate next odd number
objectToWaitOn.setPrintEvenOrOdd(2);
objectToWaitOn.notifyAll();
}
}
}
}
package com.vipin.common;
public class ObjectToWaitOn {
private int printEvenOrOdd;
public ObjectToWaitOn(int printEvenOrOdd) {
this.printEvenOrOdd = printEvenOrOdd;
}
public int getPrintEvenOrOdd() {
return printEvenOrOdd;
}
public void setPrintEvenOrOdd(int printEvenOrOdd) {
this.printEvenOrOdd = printEvenOrOdd;
}
public void print(String byThread, int numToPrint) {
System.out.println(byThread + ": " +numToPrint);
}
}
package com.example.multithr.main.app1;
import com.example.common.ObjectToWaitOn;
import com.example.multithr.implrun.PrintEvenNumThread;
import com.example.multithr.implrun.PrintOddNumsThread;
public class PrintEvenOddNumsMainApp {
public static void main(String[] args) {
ObjectToWaitOn obj = new ObjectToWaitOn(1); // 1 == odd; 2 == even
PrintEvenNumThread printEvenNumThread = new PrintEvenNumThread(obj);
PrintOddNumsThread printOddNumsThread = new PrintOddNumsThread(obj);
Thread evenNum = new Thread(printEvenNumThread);
Thread oddNum = new Thread(printOddNumsThread);
evenNum.start();
oddNum.start();
}
}
1) When any of these threads releases lock by calling notifyAll()
on object objectToWaitOn
(which is shared between these threads), does it release the lock immediately? I have this doubt because these threads are in synchronized
block based on objectToWaitOn
object; so even if a thread calls the notifyAll(), shouldn't it still hold the lock because it is in synchronized block?
2) When a thread is in waiting condition by calling wait()
on objectToWaitOn
, and if other thread released the lock by invoking notifyAll()
, does the waiting thread waits for lock to release or something else? Doesn't a thread coming out of the synchronized
block anyway release the lock on the object it holds; so in above example if a thread is holding lock on objectToWaitOn
and comes out of the synchronized
block, doesn't it anyway release the lock for objectToWaitOn
, and shouldn't based on this the other thread wake up?
Can anyone help me clarify these doubts?
Does it release the lock immediately?
No, it doesn't. The thread continues executing next statements within the synchronisation block.
Shouldn't it still hold the lock because it is in a synchronized block?
Yes, it should. A thread that calls the notify
/notifyAll
methods must hold the lock and will continue holding it until it leaves the synchronisation block normally or an exception happens:
- If execution of the Block completes normally, then the monitor is unlocked and the synchronized statement completes normally.
- If execution of the Block completes abruptly for any reason, then the monitor is unlocked and the synchronized statement completes abruptly for the same reason.
JLS-14.19
The notify
/notifyAll
methods change the state of the threads1 that are waiting on this monitor from State.WAITING
to State.RUNNABLE
. When the threads are woken up, they can participate in acquiring the lock.
Coming up to the monitor, some of them2 might get the STATE.BLOCKED
state and wait until the other thread releases the lock. Note that it doesn't require any notifications from the thread which holds the lock.
The awakened threads will not be able to proceed until the current thread relinquishes the lock on this object. The awakened threads will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened threads enjoy no reliable privilege or disadvantage in being the next thread to lock this object.
docs
1. In case of notify
, it's a single arbitrary chosen thread.
2. Or all of them - if the thread that notified keeps holding the monitor.
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