2

I'm trying to understand how Java's wait and notify methods work. As per the documentation, wait() causes thread to wait for subsequent calls to notify() or notifyAll() methods but for some reason notify doesn't interrupt "waiting":

public static void main(String[] args) {

    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("thread1 is started, waiting for notify()");

            synchronized (this) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    System.out.println(e.getLocalizedMessage());
                }
            }

            System.out.println("waiting is over");
        }
    });

    thread1.start();

    // unblock thread1 in 2 seconds

    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    synchronized (thread1) {
        thread1.notify();
    }
}
fraggjkee
  • 3,524
  • 2
  • 32
  • 38
  • 2
    You have to call `wait()` and `notify()` on the same object. You are not doing that in your code - you implicitly call `wait()` on the `Runnable` object, and `notify()` on the `Thread` object. – Jesper Mar 24 '17 at 09:16
  • 1
    `wait()` and `notify()` are low-level methods that are meant to be used in a very specific way to implement higher-level synchronization classes. https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html You are likely to run in to trouble if you use them any differently. Also, in most cases, you can use the higher-level synchronization classes that are provided by the `java.util.concurrent` package, and avoid the risks associated with crafting your own. – Solomon Slow Mar 24 '17 at 11:12
  • Related to http://stackoverflow.com/questions/17840397/concept-behind-putting-wait-notify-methods-in-object-class/17841450#17841450 – Gray Mar 24 '17 at 15:39
  • @jameslarge yep, i'm already familiar with high-level Java stuff related to concurrency and multithreading, just wanted to understand how all of that works at the lowest level. – fraggjkee Mar 24 '17 at 16:07

2 Answers2

5

You need to notify the object that is being waited on, not the thread that is waiting.

In your case the object waited on is an instance of an anonymous inner class, which is problematic because you cannot easily obtain a reference to it in order to notify it. You could solve this by extending Thread directly:

Thread thread1 = new Thread() {
    @Override
    public void run() {
        System.out.println("thread1 is started, waiting for notify()");

        synchronized (this) {
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println(e.getLocalizedMessage());
            }
        }

        System.out.println("waiting is over");
    }
};

Now the this (in synchronized (this)) refers to the thread itself, and the wait is called on the thread object too. In this case your current call to notify should be fine, since it notifies the same object (which happens in this case to be the thread that is waiting - but just to be clear, that need not be the case).

It isn't considered good practice to use an object for synchronisation that may also be used elsewhere; instances of Thread would be an example of this, and in fact the documentation specifically advises against it:

It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

Also, you should correctly handle spurious wakeup; that is, wait may return because notify/notifyAll was called elsewhere or perhaps was not even called at all. As the documentation also says:

A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops [...]

Therefore, your example should really use a separate variable to track whether the wakeup was intentional (due to an explicit notify) or not.

davmac
  • 20,150
  • 1
  • 40
  • 68
  • yeah, that was the case - I totally missed the fact that `this` is referencing anonymous `Runnable` class and not the `thread1` variable. Thank you! – fraggjkee Mar 24 '17 at 09:23
  • Using `thread.wait()` and `thread.notify()` usually is not recommended because the `Thread` class itself uses `wait()` and `notify()` on `Thread` objects. – Solomon Slow Mar 24 '17 at 11:08
1

for some reason notify doesn't interrupt "waiting":

@davmac's answer is correct but for posterity, there are some other ways you can do it because extending Thread and calling wait() and notify() on the Thread object is not recommended.

The best way would be to create a lock object. Making your lock objects final is always a good pattern although here it is also necessary to use it in the inner class.

final Object lock = new Object();
Thread thread1 = new Thread(new Runnable() {
    ...
        synchronized (lock) {
            try {
                lock.wait();
            } catch (InterruptedException e) {
                // always a good pattern
                Thread.currentThread().interrupt();
                System.out.println(e.getLocalizedMessage());
            }
        }
      ...
}
...
synchronized (lock) {
    lock.notify();
}
// might as well wait for it to finish
thread1.join();
Gray
  • 115,027
  • 24
  • 293
  • 354