1
Thread thread = new Thread(() -> {
    synchronized (this){
        try {
            this.wait();
            System.out.println("Woke");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

});
thread.start();
TimeUnit.SECONDS.sleep(1);
this.notify();

When calling notify it says

java.lang.IllegalMonitorStateException: current thread is not owner

The typical usage of notify is that you call it and then you release the lock implicitly (by leaving the synchronized block) so that the waiting threads may re-acquire the lock.

But code above calls notify even before it has the lock, so other threads can just try to acquire the lock, why not? I think the holding the lock is not necessary.

Name Null
  • 390
  • 2
  • 11
  • 1
    Related: [why wait/notify/notifyAll methods are not synchronized in java ?](https://stackoverflow.com/questions/7019745/why-wait-notify-notifyall-methods-are-not-synchronized-in-java) – Mark Rotteveel Dec 16 '22 at 15:53
  • 5
    _"I think the holding the lock is not necessary."_ Why do you think that? The whole point of notification is that your thread did something that others waiting on the lock might be interested in. For those actions to be visible (or at least to guarantee they are visible), you must have performed those actions under the lock, requiring the notify to be under the lock is a good idea, because that improves the chance it is used correctly and that your actions are actually visible to the threads you're notifying. – Mark Rotteveel Dec 16 '22 at 15:56
  • 1
    Calling notify doesn't "release the lock", it tells the scheduler to wake some thread waiting on the same lock. Waiting threads have released the lock and have to reacquire it in order to stop waiting – Nathan Hughes Dec 16 '22 at 16:00
  • Actually I know the typical usage of `notify` is to update some state for waiting threads to see. I was just wondering, when I don't have any states for waiting threads to see, why not just `notify` without using `synchronized` for only one line of `notify` code. My viewpoint is that calling `notify` outside a `synchronized` block is not thread-safe. Requiring it to be thread-safe just avoids the special case I put up above, without any benefit (it's equivalent to bracing it with a `synchronized` block). Other comments also show that in this situation `wait/notify` should be avoided at all. – Name Null Dec 16 '22 at 16:46

2 Answers2

3

I think the holding the lock is not necessary.

It is necessary because the javadoc for Object.notify() says it is necessary. It states:

"This method should only be called by a thread that is the owner of this object's monitor. A thread becomes the owner of the object's monitor in one of three ways:

  1. By executing a synchronized instance method of that object.
  2. By executing the body of a synchronized statement that synchronizes on the object.
  3. For objects of type Class, by executing a synchronized static method of that class."

But your real question is why is it necessary? Why did they design it this way?

To answer that, we need to understand that Java's wait / notify mechanism is primarily designed for implementing condition variables. The purpose of a condition variable is to allow one thread to wait for a condition to become true and for another thread to notify it that this has occurred. The basic pattern for implementing condition variables using wait() / notify() is as follows:

// Shared lock that provides mutual exclusion for 'theCondition'.
final Object lock = new Object();

// Thread #1
synchronized (lock) {
    // ...
    while (! theCondition) {  // One reason for this loop will
                              // become later ...
        lock.wait();
    }
    // HERE
}

// Thread # 2
synchronized (lock) {
    // ...
    if (theCondition) {
        lock.notify();
    }
}

This when thread #1 reaches // HERE, it knows that theCondition is now true. Furthermore it is guaranteed the current values variables that make up the condition, and any others controlled by the lock monitor will now be visible to thread #1.

But one of the prerequisites for this actually working is that both thread #1 and thread #2 are synchronized on the same monitor. That will guarantee the visibility of the values according to a happens before analysis based on the Java Memory Model (see JLS 17.4).

A second reason that the above needs synchronization is because thread #1 needs exclusive access to the variables to check the condition and then use them. Without mutual exclusion for the shared state between threads #1 and #2, race conditions are possible that can lead to a missed notification.

Since the above only works reliably when threads #1 and #2 hold the monitor when calling wait and notify, the Java designers decided to enforce this in implementations of the wait and notify methods themselves. Hence the javadoc that I quoted above.


Now ... your use-case for wait() / notify() is simpler. No information is shared between the two threads ... apart from the fact that the notify occurred. But it is still necessary to follow the pattern above.

Consider the consequences of this caveat in the javadoc for the wait() methods:

"A thread can 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 ..."

So one issue is that a spurious wakeup could cause the child thread to be woken before the main thread's sleep(...) completes.

A second issue is that is the child thread is delayed, the main thread may notify the child before the child has reached the wait. The notification then be lost. (This might happen due to system load.)

What these issues mean is that your example is incorrect ... in theory, if not in reality. And in fact, it is not possible to solve your problem using wait / notify without following the pattern above/

A corrected version of your example (i.e. one that is not vulnerable to spurious wakeups, and race conditions) looks like this:

final Object lock = new Object;
boolean wakeUp = false;

Thread thread = new Thread(() -> {
    synchronized (lock){
        try {
            while (!wakeUp) {
                this.wait();
            }
            System.out.println("Woke");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

});
thread.start();
TimeUnit.SECONDS.sleep(1);
synchronized (lock) {
    wakeUp = true;
    this.notify();
}

Note that there are simpler and more obviously correct ways to do this using various java.concurrent.* classes.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
0

The case where using synchronized makes sense is where the thing using the lock has state that needs to be protected. In that case the lock has to be held while notifying because there are going to be state changes that go along with the notification, so that requiring notify to be called with the lock makes sense.

Using wait/notify without state that indicates when the thread should wait is not safe, it allows race conditions that can result in hanging threads, or threads can stop waiting without having been notified. It really isn't safe to use wait and notify without keeping state.

If you have code that doesn't otherwise need that state, then synchronized is an overcomplicated/tricky/buggy solution. In the case of the posted code example you could use a CountdownLatch instead, and have something that is simple and safe.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • 2
    "If you have code that doesn't have that state..." Then you should not try to use wait()/notify(). That's a common newbie mistake, of which you can find hundreds of examples on SO. It's a mistake for any thread to call `o.wait()` unless it can _prove_ that the event in question has not already happened. The normal way to prove it is by examining variables that are shared with the `notify()`ing thread. – Solomon Slow Dec 16 '22 at 16:46
  • @SolomonSlow: true, using wait by itself can leave a thread hanging due to missed notifications. – Nathan Hughes Dec 16 '22 at 16:51
  • In fact, the OP's code would be incorrect even if `notify` could be called without holding the monitor; see my answer for why. Hint: spurious wakeups. – Stephen C Dec 16 '22 at 16:55