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

[2919 byte] By [aria_kokoschkaa] at [2007-11-27 8:56:26]
# 1

notifyAll() wakes up all waiting threads. That's why the loop is there. The threads then reacquire the lock named in the synchronized() block, in an undermined order. The first thread to acquire it will take whatever action it wants to take, which will generally clear the loop condition, before it exits the synchronized block. The other threads will therefore keep looping.

Consider the case of waiting while a queue is empty, then removing the first element. Only the first thread to acquire the lock will do that.

ejpa at 2007-7-12 21:19:26 > top of Java-index,Core,Core APIs...
# 2

So according to your statement, every time blocking threads are signaled, ALL of them gets out of obj.wait(), "try" to acquire the lock on the object, and only one would prevail. Which brings us to the question I asked, they ALL get to their line (1) after each notification and only put on the waiting list due to the while (condition not being met). Is that a correct interpretation?

If that is the case, this would be a bit of waste of effort on JVM part because this concludes that after each notification, JVM needs to move all the blocking threads out of the waiting list, choose one as the winner, let the rest go through their while loop and put on the waiting list again. couldn't the scheduler select one thread, give it a lock and "keep" the rest on the waiting list without getting them ALL out of wait()?

So my question pretty much boils down to this: When a notification occurs, do ALL waiting threads execute line (1) regardless of whether winning the contention and only to be put back on the waiting list after failing the while (condition not being met)?

aria_kokoschkaa at 2007-7-12 21:19:26 > top of Java-index,Core,Core APIs...
# 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...

aria_kokoschkaa at 2007-7-12 21:19:26 > top of Java-index,Core,Core APIs...
# 4

notify and notifyAll will wake up threads that are waiting because of a wait() call, they don't wake up threads that are waiting to enter a synchronized block. I think that's where your confusion is coming from. wait() causes the current thread to release its lock on the monitor, so then other threads can attempt to acquire the lock (and possibly wait as well, if the condition is still not met).

The purpose of the loop is not because notifyAll wakes up all waiting threads. The loop is because there is a chance of spurious wakeups. From the JLS:

"Implementations are permitted, although not encouraged, to perform "spurious wake-ups" -- to remove threads from wait sets and thus enable resumption without explicit instructions to do so. Notice that this provision necessitates the Java coding practice of using wait only within loops that terminate only when some logical condition that the thread is waiting for holds."

http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.8.1

If a waiting thread is spuriously awoken, the loop ensures that it will go back to waiting if the condition still isn't met.

When a notification occurs, all waiting threads wake up and compete to acquire the lock. When one does acquire the lock, it will execute line (1) while the others will be blocked waiting on the lock.

BinaryDigita at 2007-7-12 21:19:26 > top of Java-index,Core,Core APIs...
# 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.

aria_kokoschkaa at 2007-7-12 21:19:26 > top of Java-index,Core,Core APIs...
# 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.

BinaryDigita at 2007-7-12 21:19:26 > top of Java-index,Core,Core APIs...
# 7
"each will acquire it in succession once the preceding thread goes back to waiting, because remember that wait() releases the lock."Excellent, now everything makes sense. I guess now the only thing that remains to explore is JLS' Java memory model.Thank you for your help.
aria_kokoschkaa at 2007-7-12 21:19:26 > top of Java-index,Core,Core APIs...
# 8
> Excellent, now everything makes sense.Wonderful!> Thank you for your help.You're welcome!
BinaryDigita at 2007-7-12 21:19:26 > top of Java-index,Core,Core APIs...