Understanding wait()
Hi,
I am a bit confused as how wait() actually works. I know the purpose of this method but what I wanted to know is when a few threads waiting on a certain object lock and they are signaled via notifyAll(), how does one of the threads on the waiting list for that object lock gets the lock and what would happen to the other threads? Let me elaborate my confusion in the following simple example:
Thread1, Thread2, Thread3 they all waiting on "obj" that is being processed by Thread4. Thread4 runs as following:
publicvoid run(){
synchronized (obj){
try{
// do some processing on obj status
obj.notifyAll()
}catch (InterruptedException ie){}
}
}
Now, I understand that notifyAll() ONLY notifies other threads blocking on this object that the current thread is done with the object is "ready" to release the lock. That is the actual release of the lock occurs AFTER the code is out of synchronized block.
Having said that, what exactly happens when the waiting threads receive such notification? That is:
// Other threads running block
publicvoid run(){
synchronized (obj){
while (some condition not being met){
try{
obj.wait();
// (1)
}catch (InterruptedException ie){}
// Some code to process
}
}
}
When "ALL THE THREADS" receive such signal, do they all "get out" of wait() method and then whoever wins the contention would gets the lock and the rest are put on the waiting list once they "failed" the condition? Or ONLY a thread that gets the lock is allowed to finish its wait() and continue on with execution? What I want to know is does JVM even allow the threads to get out of wait() before actually acquiring the lock?
Unfortunately the wait() method is implemented in a native code so I can't figure out the inner work of this method from the source code.
Thank You
# 3
So I wrote a small program that verifies my last statement:
package thread;
public class WaitingWorker implements Runnable {
private byte[] lock;
public WaitingWorker(byte[] b) {
lock = b;
}
public byte[] getLock() {
return lock;
}
public void setLock(byte b) {
lock[0] = b;
}
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() +
" tries to acquire the lock...");
System.out.flush();
synchronized (this.getLock()) {
System.out.println(Thread.currentThread().getName() +
" succeeded in acquiring the lock.");
System.out.flush();
while (this.getLock()[0] == 0) {
System.out.println(Thread.currentThread().getName() +
" - the condition is not met... about to block...");
System.out.flush();
try {
this.getLock().wait();
System.out.println(Thread.currentThread().getName() +
" just got out of wait().");
System.out.flush();
} catch (InterruptedException ie) { }
} // end of while()
this.setLock((byte) 0);
System.out.println(Thread.currentThread().getName() +
" - condition is met... about to terminate...");
} // end of synchronized ()
break;
} // end of while (true)
}
} // end of WaitingWorker
package thread;
public class NotifyingThread {
private Thread[] threadArray;
private byte[] threadLock = new byte[1];
public byte[] getThreadLock() {
return threadLock;
}
public void setThreadLock(byte b) {
threadLock[0] = b;
}
public void threadSetup(int numOfThreads, Runnable r) {
threadArray = new Thread[numOfThreads];
for (int i = 0; i < threadArray.length; i++) {
threadArray[i] = new Thread(r, "id(" + i + ")");
}
threadLock[0] = (byte) 0;
}
public void startThreads() {
for (int i = 0; i < threadArray.length; i++) {
threadArray[i].start();
}
}
public static void main(String[] args) {
int numOfThreads = 0;
try {
numOfThreads = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) { }
System.out.println("Main thread starting...");
NotifyingThread nt = new NotifyingThread();
nt.threadSetup(numOfThreads, new WaitingWorker(nt.getThreadLock()));
nt.startThreads();
for (int i = 0; i < numOfThreads; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) { }
synchronized (nt.getThreadLock()) {
nt.setThreadLock((byte) 1);
System.out.println("\n (((Notifying all waiting threads...)))\n");
System.out.flush();
nt.getThreadLock().notifyAll();
} // end of synchronized()
}
} // end of main()
} // end of class NotifyingThread
Running with 5 threads, the output would be something as following:
$> java -cp . thread.NotifyingThread 5
Main thread starting...
id(0) tries to acquire the lock...
id(0) succeeded in acquiring the lock.
id(0) - the condition is not met... about to block...
id(1) tries to acquire the lock...
id(1) succeeded in acquiring the lock.
id(1) - the condition is not met... about to block...
id(2) tries to acquire the lock...
id(2) succeeded in acquiring the lock.
id(2) - the condition is not met... about to block...
id(3) tries to acquire the lock...
id(3) succeeded in acquiring the lock.
id(3) - the condition is not met... about to block...
id(4) tries to acquire the lock...
id(4) succeeded in acquiring the lock.
id(4) - the condition is not met... about to block...
(((Notifying all waiting threads...)))
id(0) just got out of wait().
id(0) - condition is met... about to terminate...
id(1) just got out of wait().
id(1) - the condition is not met... about to block...
id(2) just got out of wait().
id(2) - the condition is not met... about to block...
id(3) just got out of wait().
id(3) - the condition is not met... about to block...
id(4) just got out of wait().
id(4) - the condition is not met... about to block...
(((Notifying all waiting threads...)))
id(1) just got out of wait().
id(1) - condition is met... about to terminate...
id(2) just got out of wait().
id(2) - the condition is not met... about to block...
id(4) just got out of wait().
id(4) - the condition is not met... about to block...
id(3) just got out of wait().
id(3) - the condition is not met... about to block...
(((Notifying all waiting threads...)))
id(2) just got out of wait().
id(2) - condition is met... about to terminate...
id(4) just got out of wait().
id(4) - the condition is not met... about to block...
id(3) just got out of wait().
id(3) - the condition is not met... about to block...
(((Notifying all waiting threads...)))
id(4) just got out of wait().
id(4) - condition is met... about to terminate...
id(3) just got out of wait().
id(3) - the condition is not met... about to block...
(((Notifying all waiting threads...)))
id(3) just got out of wait().
id(3) - condition is met... about to terminate...
# 5
BinaryDigit,
Thank you for your response. You mentioned that the loop also is for the spurious wake-ups and also the fact that "all waiting threads wake up" and compete for the lock. So far so good...
But at the end you stated whichever thread that succeeds in acquiring the lock, it will execute line (1). Well, according to the output of my program, ALL threads get out of wait(), go back up to the while loop to check the condition. Always the first thread that gets out wait() would check the condition first, which must be true, gets out of the loop, reset the condition to false and terminates. This would bring me to the following conclusion:
1) All the waiting threads are awaken.
2) All the threads would get out of the wait() method, execute line (1), check the while condition for validity. I make this claim because " - condition is not met..." is printed for every thread.
Now, the second point is what scares me. Let's take a look at the code again:
synchronized (this.getLock()) {
System.out.println(Thread.currentThread().getName() +
" succeeded in acquiring the lock.");
System.out.flush();
(1)while (this.getLock()[0] == 0) {
(2)System.out.println(Thread.currentThread().getName() +
" - the condition is not met... about to block...");
System.out.flush();
try {
(3) this.getLock().wait();
(4) System.out.println(Thread.currentThread().getName() +
" just got out of wait().");
System.out.flush();
} catch (InterruptedException ie) { }
} // end of while()
(5)
(6)this.setLock((byte) 0);
System.out.println(Thread.currentThread().getName() +
" - condition is met... about to terminate...");
} // end of synchronized ()
Let's walk through the code; let's assume that all threads are on block at line (3). They all get a notification to wake up and compete for the lock. According to the output of the program, apparently they all print out line (4) BUT one by one. Which means, they all acquire the lock, go to the top of while loop, line (1).
Now, the first thread that gets to the top of the copy of its while loop instruction would manage to get out of the loop. The rest of them print out (2) and block.
Imagine a scenario where the first thread who gets to line (5) is thrown out by the scheduler and another thread comes to play. Since the condition has not been reset (at line (6)) YET, it checks the while condition and it too gets out of the loop. Now we have two threads out of loop which is not what we desire.
What I am suspecting is that when threads are waken up, one and only one acquires the lock which means NO OTHER thread can execute a damn thing in its synchronized block. And what I see as an output, is the result of the first thread acquiring the lock, doing its thing, resetting the condition, and terminating. Then the next chosen thread acquires the lock and does its printing and waits due to the failed condition and so on. Am I right on this? If not, then a scenario aforementioned would be a possibility and that gives me blues.
# 6
"Well, according to the output of my program, ALL threads get out of wait()"
That's probably because you're calling notifyAll() instead of notify(). So yes, all threads will wake up and each will compete for the lock (and each will acquire it in succession once the preceding thread goes back to waiting, because remember that wait() releases the lock) If you change it to notify() you should see only one thread waking up.
"Always the first thread that gets out wait() would check the condition first, which must be true, gets out of the loop, reset the condition to false and terminates."
That should not be the case. Waiting threads that are notified do not go back up to check the loop condition first-thing, they continue execution after the wait() call. You can see that in the output you posted in reply #3. You woke up all waiting threads with notifyAll(), and one at a time they report getting out of wait. The first awoken thread sees false for the condition so it bails out of the loop and sets the condition back to true. After it prints "condition is met..." it exits the synchronized block, at which time the next awoken thread will acquire the lock and see the condition is true, so it will wait, which releases the lock, which allows the next awoken thread to get the lock and see the condition is true, etc, etc.
"Imagine a scenario where the first thread who gets to line (5) is thrown out by the scheduler and another thread comes to play."
That should not happen because line (5) is inside the synchronized block. Any thread sitting at line (5) will have the lock, so no other thread is allowed in there at the same time.
"What I am suspecting is that when threads are waken up, one and only one acquires the lock which means NO OTHER thread can execute a damn thing in its synchronized block. And what I see as an output, is the result of the first thread acquiring the lock, doing its thing, resetting the condition, and terminating. Then the next chosen thread acquires the lock and does its printing and waits due to the failed condition and so on. Am I right on this?"
That's exactly right.