2

From the JAVA docs for Object notify()

The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object.

This means that unless the Thread which notifes, its synchronized block is complete and it releases the lock, the waiting thread cannot proceed. If that's the case then whats the point of having notify() if the sync block is going to be executed anyway? What's the actual use of notify() if it doesn't wake up the waiting thread and let it do its job?

ghostrider
  • 2,046
  • 3
  • 23
  • 46
  • The `sync` block doesn't relinquish the lock, it just ensures that only one thread at a time can acquire it – MadProgrammer Jan 28 '20 at 03:07
  • @MadProgrammer, what's the use of notify() if it does not let the other waiting thread to start doing its job? Does it only signal it? What's the use of that signal? – ghostrider Jan 28 '20 at 03:11
  • "Signal" is just that, it "notifies" the pool of "waiting" objects that the lock has been relinquished and a new object may acquire it. It's conceivable that a lock might be acquired in one thread, but relinquished in another, meaning that a `synchronised` block won't work (as a automatic gate keeper) – MadProgrammer Jan 28 '20 at 03:18
  • @MadProgrammer "a new object may acquire it", this means the new thread cannot acquire it immediately right? It has to wait till the sync block is over? That would be the case even when the notify() was not there in the first place isnt it? Then what's point of having notify( ) ? – ghostrider Jan 28 '20 at 03:21
  • As I said, `sync` is not a good gate keeper for releasing the lock. You may need that lock to be marinated while performing extended work, which goes beyond the scope the sync block - therefore you need a mechanism to "notify" other interested parties that the lock is ready to be acquired. There are different mechanisms at play - a `synchronised` block is good of managing short blocks of code, where monitor locks are good for dealing with more complex problems. It's also a nice way to wait till some other operation has been completed, but that's another story – MadProgrammer Jan 28 '20 at 03:24
  • You're question is about a broader subject (which goes beyond Java) and focuses on "concurrent programming theory". All multi threaded programming languages will use similar concepts to solve the same problem – MadProgrammer Jan 28 '20 at 03:26

5 Answers5

2

Good question. Will point you to take a look at the Thread State Class.

A thread that calls the Object.notify method enables a thread that previously called Object.wait is now enabled to be scheduled by the thread scheduler. In parlance, the thread that was waiting is now "runnable". Although it is "runnable", it is not "running".

It can only continue running when the thread invoking notify releases the lock - one way is when it exits out of the synchronized block.

There are a lot of schematics on the web on the Thread States. Some of them are completely incorrect or confusing since they introduce terminology not in the official docs. Here is one that makes sense to me.

Khanna111
  • 3,627
  • 1
  • 23
  • 25
1

Strictly speaking, we don't: we could have the waiting thread run a loop where it re-acquires the lock, checks the condition, and sleeps for a short amount of time. But using wait() and notify() is much more efficient, because then the waiting thread doesn't keep waking up and tying up CPU (and tying up the lock).

ruakh
  • 175,680
  • 26
  • 273
  • 307
  • "because then the waiting thread doesn't keep waking up and tying up CPU (and tying up the lock)." wouldn't the same thing happen when the sync block is complete? My question is what's the use of notify() if it doesn't let the waiting thread get its lock back and get back to work. Do you think @Khanna111 answer above is correct? – ghostrider Jan 28 '20 at 03:13
  • @ghostrider: Re: "My question is what's the use of notify() if it doesn't let the waiting thread get its lock back and get back to work": Right, and my answer answers that question. ;-) – ruakh Jan 28 '20 at 04:06
1

Notifying is what wakes up a thread that is waiting. If you remove the notify then waiting threads stay waiting (barring spurious wakeups but let’s not go there for now).

(Interrupting wakes up the thread but the guidance is to use it for cancellation only. Interruption targets a specific thread, where notifying lets the scheduler decide which threads are affected.)

When a thread calls wait it has to have the lock, then the wait method lets go of the lock.

When a thread calls notify it has to have the lock.

As a practical matter the notify can’t take effect on any waiting thread until the notifying thread relinquishes the lock. The first thing the notified thread is going to need to do anyway is to try to acquire the lock. All the passage you're quoting is trying to say is that the wakeup doesn't occur instantaneously when a thread calls notify.

So what happens here is that the notifying thread lets go of the lock and sends the notify to the scheduler, the scheduler decides which thread to notify, then the notified thread wakes up and contends for the lock in order to leave the wait method.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
1

notify() and notifyAll() are used to wake up thread(s) that called wait() on the same object on which notify() or notifyAll() is called. Without call to notify() those "waiting" threads will wait forever (although JVM spec says that threads may sometime wake up without call to notify). Also because call to notify() doesn't releases the lock associated with the object itself that call usually is the last statement in a synchronized block.

So notify() is used together with wait() and not by itself. Usually the use case is like the following (blocking queue with limited size).

Method that adds element to queue (some pseudo code)

synchronized(lockObject) {
    if (size < LIMIT) {
        addElement();
        lockObject.notifyAll(); //notifying threads that are waiting to get element from empty queue
    } else {
        lockObject.wait(); // waiting for other thread to get element from queue and make room for new element
    }
}

Method that gets element

synchronized(lockObject) {
    if (size > 0) {
        getElement();
        lockObject.notifyAll(); // notify threads that there is a room for new element
    } else {
        lockObject.wait(); // waiting for other thread to put element into the queue
    }
} 

Also calling lockObject.wait() releases lock on lockObject. More details regarding that could be found here: Java : Does wait() release lock from synchronized block

Ivan
  • 8,508
  • 2
  • 19
  • 30
0

Imagine if you need a thread to wait for another thread to do something that it may or may not even currently be actively working on. For example, a thread that's waiting for a job to do may need to wait until another thread has put a job on the list of jobs it should do if that list is empty. How would you do this?

You can't just use some form of mutual exclusion. There may be long periods of time when there's no work to do and not thread holds any lock on the queue. There may just not be any work to do right now. The thread that does work needs to wait, without holding any lock, until another thread has given it some work to do.

So somewhere, there's a thread that does something like this:

  1. Acquire the lock that protects some shared state that another thread might be waiting for a change to. (In this case, the job queue.)
  2. Change the shared state to reflect the fact that the thing a thread might need to wait for has happened. (That is, put a job on the queue.)
  3. Release the lock and let any waiting thread(s) know that the thing has happened.

So what could our code to wait look like? Perhaps:

  1. Acquire the lock that protects the shared state.
  2. Check whether we need to wait or not. (Is there a job on the queue?)
  3. If we need to wait, wait. (If not, wait for a job to be placed on the queue.) ...

Oops, we have a problem. The thing we're waiting for can't happen because we hold the lock. No other thread can change the shared state. (Our thread to put a job on the queue can't touch the queue until we release the lock we acquired in step 1.)

Let's try it again:

  1. Acquire the lock that protects the shared state.
  2. Check whether we need to wait or not. (Is there a job on the queue?)
  3. If we don't need to wait, exit this algorithm. (If there's a job, take it off the queue, release the lock, and do it.)
  4. Release the lock. (So another thread can put a job on the queue.)
  5. Wait for the thing to happen.
    ...

Oops, we have another problem. What if the thing we're waiting for happens after step 4 but before step 5. Since the lock has been released, the thing we're waiting for can happen. We can't check again because we don't hold the lock. How can we ensure we don't wait for something that has already happened, which may mean waiting forever?

To solve this, we need an atomic "unlock and wait" operation. That's what wait does. And we also need some operation that can end this wait that can be called by the thread that changed the shared state so that we no longer need to wait. That's what notify does.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • Hey David, thanks for the answer, My question still is what difference does it make, if despite calling notify(), the waiting thread still has to wait to get the lock until the sync block of the notifier is complete? What extra use does notify provide inside the sync block, if the waiting thread still has to wait unless the sync block is over? What if I remove the notify() altogether? – ghostrider Jan 28 '20 at 04:05
  • 1
    @ghostrider If you remove the `notify` altogether, what would you do in the thread that needs to wait? You can't call `wait` because it may wait forever if `notify` is not called. So what do you do in the example of a thread that currently has no work to do but something another thread might do may mean it then needs to do work? How could you wait for something to happen without some way to know when that thing may have happened? – David Schwartz Jan 28 '20 at 04:31