Java threads: wait and notify methods

16,291

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 waiting 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.

Share:
16,291
satheesh
Author by

satheesh

Updated on June 09, 2022

Comments

  • satheesh
    satheesh almost 2 years

    I have a thread that calls the wait method and can only be awoken when the notify 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 in main, if the ThreadA does not execute first and instead the other synchronization block executing and completes to completion, then ThreadA executes its synchronized block and calls wait, what is going to happen and how it will be notified again?

  • templatetypedef
    templatetypedef about 13 years
    This 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
    bestsss about 13 years
    templatetypede, 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
    bestsss about 13 years
    if you use sync(thread)/notify() you mess up w/ any waiting join on that condition.
  • Jesse Barnum
    Jesse Barnum about 13 years
    That'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
    templatetypedef about 13 years
    @bestsss- Can you point me to the part of the JLS that says that join synchronizes on the Thread object? I've read over this pretty carefully and I don't remember ever seeing anything like that.
  • bestsss
    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
    bestsss about 13 years
    @templatetypedef, The advice is a very valid one, if people feel like skipping it, it's their own call.
  • templatetypedef
    templatetypedef about 13 years
    @bestsss- Synchronizing on the Thread class object is not the same as synchronizing on individual Thread objects; the two are different entities. Also, I pulled up the source for the Thread class, but the start and join methods are marked native and so I can't see the implementation. I also can't seem to find something on Google that says that threads lock the Thread.class object, and the JLS special-cases 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.
  • bestsss
    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)}}catc‌​h(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
    Paŭlo Ebermann about 13 years
    If 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 than thread here.)
  • satheesh
    satheesh about 13 years
    sir i had a doubt how to get to know which threads owns the lock on a particular object.can u clarify me please
  • bestsss
    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
    Paŭlo Ebermann about 13 years
    This 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 the notify() call by another wait, I would hope that the ending Thread uses notifyAll() and not notify(), as there may be more than one thread joining it, too.
  • bestsss
    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
    Paŭlo Ebermann about 13 years
    Maybe 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 not synchronized(thread).. repat: no synchronized(thread).
  • bestsss
    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
    bestsss about 13 years
    the 2nd snippet must be while(! b .isDone ) b.wait();
  • bestsss
    bestsss about 13 years
    new CountDownLatch(1) is usually my favorite, condition to wait upon.
  • bestsss
    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
    Jesse Barnum about 13 years
    I 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
    bestsss about 13 years
    the 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 and isDone 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
    Jesse Barnum about 13 years
    Ok, 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.