The methods object.wait(), object.notify() and object.notifyAll() are meant to be used in a very specific way. (see http://stackoverflow.com/documentation/java/5409/wait-notify#t=20160811161648303307 )

The “Lost Notification” problem

One common beginner mistake is to unconditionally call object.wait()

private final Object lock = new Object();

public void myConsumer() {
    synchronized (lock) {
        lock.wait();     // DON'T DO THIS!!
    }
    doSomething();
}

The reason this is wrong is that it depends on some other thread to call lock.notify() or lock.notifyAll(), but nothing guarantees that the other thread did not make that call before the consumer thread called lock.wait().

lock.notify() and lock.notifyAll() do not do anything at all if some other thread is not already waiting for the notification. The thread that calls myConsumer() in this example will hang forever if it is too late to catch the notification.

The “Illegal Monitor State” bug

If you call wait() or notify() on an object without holding the lock, then the JVM will throw IllegalMonitorStateException.

public void myConsumer() {
    lock.wait();      // throws exception
    consume();
}

public void myProducer() {
    produce();
    lock.notify();    // throws exception
}

(The design for wait() / notify() requires that the lock is held because this is necessary to avoid systemic race conditions. If it was possible to call wait() or notify() without locking, then it would be impossible to implement the primary use-case for these primitives: waiting for a condition to occur.)

Wait / notify is too low-level

The best way to avoid problems with wait() and notify() is to not use them. Most synchronization problems can be solved by using the higher-level synchronization objects (queues, barriers, semaphores, etc.) that are available in the java.utils.concurrent package.