5

I would like to ask you a question related to multithreading in Java.

I have a monitor and multiple threads are eager to own it. Also inside the critical section this.wait() is invoked based on some conditions.

AFAIK, the monitor has 2 sets of threads:

  • entry set - where just arrived threads congregate and wait for their turn to own the monitor
  • wait set - where this.wait() threads that are waiting to be awakened

But how do they compete when notify/notifyAll is called? Do threads from wait set have a priority in acquiring the monitor over threads in entry set or do they move to entry set?

Can I be sure that in case of notify the next executed thread will be one from the wait set?

Pasha
  • 1,768
  • 6
  • 22
  • 43
  • 2
    Re, "I have a monitor and multiple threads are eager to own it." That's a bad start right there. Having a highly contested lock in your program is almost never a good thing. I would think deeply about whether I could change the architecture of the program in some way that reduces the need for locking. If that was not possible, then I would think deeply about whether I could change the architecture to do everything in a single thread. – Solomon Slow Jun 27 '19 at 14:10
  • @SolomonSlow simply added some dramatism – Pasha Jun 27 '19 at 15:00

1 Answers1

3

No. The scheduler is in charge of which thread gets the lock next. It might be one from the wait set that got notified. It might be a thread that is just arrived and hasn't entered the wait set. Assuming the thread that just got notified will get the monitor next is not safe.

The standard advice is to call wait in a loop where we check the condition being waited on:

synchronized (lock) {
    while (!condition) {
        lock.wait();
    }
    ...

That way when a thread comes out of a wait, it makes the same check as any thread that hasn't waited yet to know whether to progress or not.

If you need fairness, where you want the longest-waiting thread to acquire the lock next, then you might try one of the explicit Locks from java.util.concurrent.locks, such as ReentrantLock, but read the fine print:

The constructor for this class accepts an optional fairness parameter. When set true, under contention, locks favor granting access to the longest-waiting thread. Otherwise this lock does not guarantee any particular access order. Programs using fair locks accessed by many threads may display lower overall throughput (i.e., are slower; often much slower) than those using the default setting, but have smaller variances in times to obtain locks and guarantee lack of starvation. Note however, that fairness of locks does not guarantee fairness of thread scheduling. Thus, one of many threads using a fair lock may obtain it multiple times in succession while other active threads are not progressing and not currently holding the lock. Also note that the untimed tryLock method does not honor the fairness setting. It will succeed if the lock is available even if other threads are waiting.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • 1
    If you need a process that is more "fair" you can use a Lock from the concurrency utils package. – Thilo Jun 27 '19 at 13:25
  • "Assuming the thread that just got notified will get the monitor next is not safe." - This isn't true. A thread can *only* call `wait()` if and when it owns the monitor, and once it is notified, i.e. `wait()` returns, it is guarateed that it owns the monitor again. There is no being notified without implicitly and atomically re-aquiring the monitor. – JimmyB Jun 27 '19 at 13:41
  • @JimmyB: is that guarantee in the JLS? JLS17.2.2 describes how a thread is picked from the waitset for notification but doesn't say it will get the monitor next. – Nathan Hughes Jun 27 '19 at 13:42
  • Can't quote the JLS now, but a) [`Object.wait()`](https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#wait()) states "The thread **releases ownership of this monitor and waits** ... . The thread then **waits until it can re-obtain ownership** of the monitor and resumes execution." and b) any different behavior would make synchronization via notify/wait impossible and c) the JLS guarantees that *any* code inside a `synchronized` block will only execute when the current thread owns the monitor, irrespective if it calls `wait()` somewhere inside the block or not. – JimmyB Jun 27 '19 at 13:49
  • The loop code you suggested addresses the issue of "spurious wakeups", which has nothing to do with ownership of a monitor. – JimmyB Jun 27 '19 at 13:51
  • @JimmyB: the doc doesn't say it's atomic. It just says the thread has to re-acquire the monitor so it can make progress within the synchronized block. – Nathan Hughes Jun 27 '19 at 13:55
  • JLS "17.2.1. Wait", sequence step #3 "Thread t performs n lock actions on m." – JimmyB Jun 27 '19 at 13:56
  • "the doc doesn't say it's atomic. It just says the thread has to re-acquire the monitor so it can make progress within the synchronized block." - From the perspective of the thread, it *is* atomic. Neither wakeup nor re-aquisition of the monitor can happen alone. Both are *always* done inside `wait()` before it returns. – JimmyB Jun 27 '19 at 13:59
  • @JimmyB: i'm not convinced. but why don't you post a question that specifically addresses that? I would be interested to see the responses. – Nathan Hughes Jun 27 '19 at 14:01
  • Sorry if I could not make clear enough what I mean. I'm not going to open a question to which I already know the answer, and even found it in the JLS ;-) - Do you agree that a thread must not execute *any* (Java) code inside a `synchronized` block if it does not own the monitor? This holds for the code *before* and *after* any `wait()` calls, doesn't it? – JimmyB Jun 27 '19 at 14:06
  • @JimmyB: Again, nothing in the JLS quote said the sequence was executed atomically. done arguing. I encourage you to ask a specific question about this, I would upvote it. – Nathan Hughes Jun 27 '19 at 14:10
  • Define "atomically". As I said, `wait()` will only ever return after the thread got woken up *and* re-acquired the monitor, there is no way around that. Of course, while a thread waits to re-acquire, any number of other threads may acquire and release the monitor. Not necessarily fair but safe. – JimmyB Jun 27 '19 at 14:20
  • And again: Your code sample guards against spurious wake-ups but has nothing to do with either fairness nor acquisition of any monitors. – JimmyB Jun 27 '19 at 14:21
  • 1
    Sorry, but I asked more do threads from wait set move to entry set? – Pasha Jun 27 '19 at 15:01
  • 1
    @Pavel: the way threads get to the wait set is when they call wait. and they move to entry set when they wake up from wait, where they have to reacquire the lock in order to continue on their way. and no need to apologize. – Nathan Hughes Jun 27 '19 at 15:09
  • 2
    @JimmyB it seems you misunderstood the sentence “*Assuming the thread that just got notified will get the monitor next is not safe*”. Mind the word *next*. All it says, is what you acknowledged yourself: “*Of course, while a thread waits to re-acquire, any number of other threads may acquire and release the monitor.*” Nobody said that `wait()` could return without owning the monitor after. But since arbitrary threads could acquire and release the monitor, there is no guarantee that the `condition` made `true` prior to calling `notify()` still is `true` when `wait()` returns. Hence, the loop. – Holger Jun 27 '19 at 15:09
  • I see what you mean now. However, your answer implies a question which was not asked. Of course, in special cases concurrent threads may change `condition` in between one thread being notified and it actually resuming, but that was not the question. The question was just if a notified thread has priority over other monitor-requesting threads, which you correcly answered as "no". - Another simple reason for the "no" answer to the OP's question: If multiple threads are waiting and `notifyAll()` is called, there is obviously no guarantee that every one of the waiting threads is resumed "next". – JimmyB Jun 28 '19 at 09:57
  • @JimmyB: very possible I answered a different question from what was asked. – Nathan Hughes Jun 28 '19 at 13:59