Reputation: 2459
Isn't it possible that notify() in another thread gets called before the wait() in one thread? It's happening with me.
A client requests a value from a target and waits on a result variable RV. In case the target is the client itself, I update RV with the correct result and call notify() on RV in another thread.
class EMU {
ResultVar RV;
Address my_address;
ResultVar findValue(String key) {
String tgt = findTarget(key);
sendRequest(tgt, key);
synchronized(RV) {
RV.wait();
}
return RV;
}
Runnable Server = new Runnable() {
public void run() {
//code to receive connections. Assume object of type Request is read from the stream.
Request r = (Request) ois.readObject();
if(r.requesterAddr.compareTo(my_address) == 0) {
String val = findVal(key);
RV.putVal(val);
synchronized(RV){
RV.notify();
}
}
}
};
}
The problem is that before the requester has completed all the "networking" (sendReqest in the above example) with itself, the result is updated in the result variable. When the requester thread now calls wait(), the program doesn't continue, since notify has already been called.
How can we prevent it?
Upvotes: 5
Views: 10074
Reputation: 1976
Let me first break down the code to a minimum reproducable:
public static void main(String[] args) throws Exception {
Object RV = new Object();
new Thread() {
@Override
public void run() {
synchronized (RV) {
RV.notify();
}
}
}.start();
Thread.sleep(1_000);
synchronized (RV) {
RV.wait();
}
}
This method will theoretically never end and the program will never quit. It shall be a dispute if this is a deadlock.
My solution is to create a second lock:
public static void main(String[] args) throws Exception {
Object RV = new Object();
Object lock = new Object();
new Thread() {
@Override
public void run() {
synchronized (lock) {
lock.wait();
}
synchronized (RV) {
RV.notify();
}
}
}.start();
Thread.sleep(1_000);
synchronized (RV) {
synchronized (lock) {
lock.notify();
}
RV.wait();
}
}
Lets inspect what the threads are doing while the main-thread is waiting one second:
synchronized(lock)
block.synchronized(lock)
block.The program ends.
Upvotes: 0
Reputation: 616
Use some condition before going to wait() and make sure that condition is thread safe :)
class EMU{
ResultVar RV;
Address my_address;
volatile boolean condition = true;
ResultVar findValue(String key){
String tgt = findTarget(key);
sendRequest(tgt, key);
synchronized(RV){
while(condition == true)
{
RV.wait();
}
}
return RV;
}
Runnable Server = new Runnable(){
public void run(){
//code to receive connections. Assume object of type Request is read from the stream.
Request r = (Request) ois.readObject();
if(r.requesterAddr.compareTo(my_address) == 0){
String val = findVal(key);
RV.putVal(val);
synchronized(RV){
condition = false;
RV.notify();
}
}
}
};
Upvotes: 0
Reputation: 8360
I'd strongly recommend not re-inventing the wheel.
Java's Future interface is designed for results that may only arrive later, and the FutureTask class implements this interface.
Have the first thread obtain access to the Future and get the second thread to run the FutureTask, and all of this stuff gets handled for you. You also get timeout support for free.
Upvotes: 2
Reputation: 111239
Nothing stops you calling notify
on an object that's not being wait
ed by another thread.
It sounds like what you want is a wait only if some condition holds. For example:
synchronized (results) {
while (!results.hasResults()) {
// no results yet; wait for them
try {
results.wait();
} catch (InterruptedException ie) { /* ignore */ }
}
}
Upvotes: 5
Reputation: 2976
You check some flag before waiting (in a loop), see the tutorial: http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
Upvotes: 7