Java threads: wait and notify methods
Solution 1
If ThreadB
gets through its synchronized
block before ThreadA
does, then ThreadA
will block indefinitely on the call to wait
. It won't somehow be notified that the other thread has already completed.
The problem is that you're trying to use wait
and notify
in ways that they are not designed to be used. Usually, wait
and notify
are used to have one thread wait until some condition is true, and then to have another thread signal that the condition may have become true. For example, they're often used as follows:
/* Producer */
synchronized (obj) {
/* Make resource available. */
obj.notify();
}
/* Consumer */
synchronized (obj) {
while (/* resource not available */)
obj.wait();
/* Consume the resource. */
}
The reason that the above code works is that it doesn't matter which thread runs first. If the producer thread creates a resource and no one is wait
ing on obj
, then when the consumer runs it will enter the while
loop, notice that the resource has been produced, and then skip the call to wait
. It can then consume the resource. If, on the other hand, the consumer runs first, it will notice in the while
loop that the resource is not yet available and will wait
for some other object to notify it. The other thread can then run, produce the resource, and notify
the consumer thread that the resource is available. Once the original thread is awoken, it will notice that the condition of the loop is no longer true and will consume the resource.
More generally, Java suggests that you always call wait
in a loop because of spurious notifications in which a thread can wake up from a call to wait
without ever being notified of anything. Using the above pattern can prevent this.
In your particular instance, if you want to ensure that ThreadB
has finished running before ThreadA
executes, you may want to use Thread.join()
, which explicitly blocks the calling thread until some other thread executes. More generally, you may want to look into some of the other synchronization primitives provided by Java, as they often are much easier to use than wait
and notify
.
Solution 2
It is possible for ThreadB's run method to complete before you enter the synchronized block in ThreadA.main. In that situation, since the notify call has happened before you started waiting, ThreadA will block forever on the wait call.
A simple workaround would be to grab the lock on b in main before you start the second thread to ensure the wait happens first.
ThreadB b = new ThreadB();
synchronized(b) {
b.start();
...
b.wait();
}
Solution 3
You could loop and wait until the total has been computed :
synchronized(b) {
while (total == 0) {
b.wait();
}
}
You could also use a higher-level abstraction like a CountDownLatch.
satheesh
Updated on June 09, 2022Comments
-
satheesh almost 2 years
I have a thread that calls the
wait
method and can only be awoken when thenotify
method called from some other class:class ThreadA { public static void main(String [] args) { ThreadB b = new ThreadB(); b.start(); synchronized(b) { try { System.out.println("Waiting for b to complete..."); b.wait(); } catch (InterruptedException e) {} System.out.println("Total is: " + b.total); } } } class ThreadB extends Thread { int total; public void run() { synchronized(this) { for(int i=0;i<100;i++) { total += i; } notify(); } } }
In the above code if the
synchronized
block inmain
, if theThreadA
does not execute first and instead the other synchronization block executing and completes to completion, thenThreadA
executes itssynchronized
block and callswait
, what is going to happen and how it will be notified again? -
templatetypedef about 13 yearsThis answer does not provide any justification as to why you should not synchronize on the other thread object, nor does it answer the original question in any detail.
-
bestsss about 13 yearstemplatetypede, b/c join and join() and start() use sync. You don't want to mess w/ them (usually). And the answer is exact: use b.join()
-
bestsss about 13 yearsif you use sync(thread)/notify() you mess up w/ any waiting join on that condition.
-
Jesse Barnum about 13 yearsThat's a good suggestion to ensure that ThreadB doesn't start working until A has the lock. However, you still want to have a loop inside the synchronized block to check that some success condition is true, instead of relying on the notify() call. This can be triggered by a spurious wakeup (books.google.com/…)
-
templatetypedef about 13 years@bestsss- Can you point me to the part of the JLS that says that
join
synchronizes on theThread
object? I've read over this pretty carefully and I don't remember ever seeing anything like that. -
bestsss about 13 years@templatetypedef start/stop/join all sync on the thread object. new thread syncs on the Thread.class. Look at the source, even it's not clearly stated, all the happens-before require sync on something to happen and since jdk 0.9 it was the thread object. De-facto it's a standard.
-
bestsss about 13 years@templatetypedef, The advice is a very valid one, if people feel like skipping it, it's their own call.
-
templatetypedef about 13 years@bestsss- Synchronizing on the
Thread
class object is not the same as synchronizing on individualThread
objects; the two are different entities. Also, I pulled up the source for theThread
class, but thestart
andjoin
methods are markednative
and so I can't see the implementation. I also can't seem to find something on Google that says that threads lock theThread.class
object, and the JLS special-casesThread.join
as a way of enforcing happens-before, so I'm not sure that synchronizing on theThread
class is the reason for happens-before. -
bestsss about 13 years@templatetypedef, dear goodness, do I look so stupid, that I need explanation that Class (object) is a different than an instance of a Thread. The point is only that such sync. exits even at far worse level.
syncrhonized(Thread.class){try{for(;;){Thread.sleep(0)}}catch(Throwable t){}}
halts any attempt to create a new thread on java 5/6. That was the only reason I added Thread.class. The source code about the affected method has been included in the answer and it's part of the java.lang.Thread class and ppl should not look at it unless they agree to Oracles license terms. -
Paŭlo Ebermann about 13 yearsIf used correctly (i.e. with an additional condition variable) there should be no problem with two different uses of the same wait/notify lock, apart from some performance losses. (And as said,
Tread.class
is a different object thanthread
here.) -
satheesh about 13 yearssir i had a doubt how to get to know which threads owns the lock on a particular object.can u clarify me please
-
bestsss about 13 years@Paŭlo, the issue is that join uses System.currentTimeMillis() which may change due to system time sync software or just user innervation. That can wreck Thread.join() if there is an external notify(), morealso any thread waiting on join() is free to consume that damn notify() call and your own thread held on wait() will do nothing but stay the OS scheduler queue.
-
Paŭlo Ebermann about 13 yearsThis would be a bug in
Thread.join()
, I would say, since there always is the possibility of a spurious wakeup - and a "malicious notify" could be seen as that. And for consuming thenotify()
call by another wait, I would hope that the ending Thread usesnotifyAll()
and notnotify()
, as there may be more than one thread joining it, too. -
bestsss about 13 years@Paŭlo, take as you wish, as free advice, and a note that notifyAll in most cases is significantly more expensive call than notify(). Don't try to summon the bugs and such... hence the 1st sentence in my answer. Obviously I did know it and there was/is some reason for that. Thread.join is as bugged as any other code relying on System.currentTimeMillis(); here is a related bug caused by the same issue: bugs.sun.com/view_bug.do?bug_id=4290274
-
Paŭlo Ebermann about 13 yearsMaybe write it like this: Because of a bug in timed join it is not advisable to use the Thread object as a lock object for other uses instead of your categorical do not
synchronized(thread)
, don't do it, do notsynchronized(thread)
.. repat: nosynchronized(thread)
. -
bestsss about 13 years...it's f-community wiki, feel free to edit, whatever you like. The issue is not only timed joins, the issue is not getting your own notify() the most if there is a 'concurrent' join(). Honestly how many of you knew anything about that stuff, it got yer attention.
-
bestsss about 13 yearsthe 2nd snippet must be while(! b .isDone ) b.wait();
-
bestsss about 13 years
new CountDownLatch(1)
is usually my favorite, condition to wait upon. -
bestsss about 13 years@templatetypedef, Thread.join as a way of enforcing happens-before, so I'm not sure that synchronizing on the Thread class is the reason for happens-before. this is done via synchronized, the same is true for Thread.start(). Happens-before stuff requires memory ordering and the only way to be achieved on the hardware level are memory fences (or instructions that behave like fences, for instance CMPXCHG/CPUID on x86). Start/join are part of the spec. but sync(thread) is what implements the spec.
-
Jesse Barnum about 13 yearsI don't think that it needs to be b.isDone. The boolean variable does not need to exist in the b thread, it is OK to have it in the parent class. Nor does it need to be marked as volatile, since all reads and writes to it happen in synchronized blocks with the same lock object. If I'm wrong about that, please let me know the details why. [I just re-read my post, and I think I see the confusion - I'm assuming that isDone is defined in the parent class, not in b]
-
bestsss about 13 yearsthe parent class is java.lang.Thread. Unless you are using some outer class where isDone is defined the code will not compile. Other than that as long as
b
instance andisDone
are in bijection the code is ok (so the best place of isDone is in the class of b). No need for volatile if you to access them under the same lock. -
Jesse Barnum about 13 yearsOk, I see what you mean. I was assuming that ThreadB was an inner class of ThreadA, in which an instance variable in ThreadA would work, but re-reading the code, there's no indication that ThreadB is an inner class.