0

This question might be duplicated, so please tell me if it's true.

I'm trying to understand synchronization in Java, but it's not clear to me that 'When to use wait() method in Object class.

Let's say I have simple class as below.

class Test {

    static class Example {
        int value = 0;

        public synchronized void increment() {
            value++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Example example = new Example();
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> example.increment());
            threads[i].start();
        }
        for (Thread thread : threads) {
            thread.join();
        }
        System.out.println(example.value);
    }
}

10 threads try to invoke increment method which is synchronized. This works well, but the curious thing is that I've never called notify() method in increment() method. If a thread 'X' enters synchronized block with monitor(mutex), then other threads would be blocked and wait for signal from 'X' thread, isn't it ? But how is it possible ?

Is the compiler adding a function at the end of the code ? (But when I added calling wait() method without notify() method, it didn't work well) What is happening in my code?

Update

Let's say I add a condition that threads should increase value only if thread number is equal to current thread number

class Test {

    static class Example {
        int value = 0;

        @SneakyThrows
        public synchronized void increment() {
            String name = Thread.currentThread().getName();
            int threadNumber = name.charAt(name.length() - 1) - 48;
            while (threadNumber != value) {
                System.out.println(threadNumber + " : release");
                wait();
            }
            value++;
            System.out.println(threadNumber + " notify");
            notifyAll();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Example example = new Example();
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> example.increment());
            threads[i].start();
        }
        for (Thread thread : threads) {
            thread.join();
        }
        System.out.println(example.value);
    }
}

In this case, when I delete notifyAll() method, it never ends. So, I thought that "notify() method is not executed after exit of synchronized block". Is this right?

cjlee
  • 378
  • 3
  • 10
  • You don’t need wait() for anything. There is no condition to wait for. Java takes care of the synchronization and prevents multiple threads from simultaneously executing example.increment(). – VGR Mar 09 '23 at 03:01
  • All objects are on a thread, so it is possible to call it to wait related to the object, more specifically "sub objects inside it" , notify is a broadcast that execution can continue or start. It is more complex related to synchronize keyword and I am gnawing away at learning it. (Wait and notify notifyAll were used in early java in gui's as a primitive form of event trigger). https://www.baeldung.com/java-wait-notify – Samuel Marchant Mar 09 '23 at 03:15
  • `notify` is executed when you execute `notify`, no more, no less. – Amadan Mar 09 '23 at 03:54
  • Wait and notify are used when one thread needs to go first (or after another). There's a simple example where one thread prints "o" and the other "k". The logic must be that they're printed in the correct order. Synchronized, wait, and notify are all part of the solution: https://stackoverflow.com/q/15595538/1168342 – Fuhrmanator Mar 09 '23 at 04:11
  • TLDR: Use wait/notify when some thread W should not proceed beyond a certain point until some other thread N has done some thing. Thread W calls `wait()` if _and only if_ the thing has not yet been done. Thread N unconditionally calls `notify()` after it does the thing. Use `synchronized` when two threads need to access the same shared variables. Since the wait/notify scenario practically always involve shared variables, the wait/notify mechanism is designed to work in tandem with `synchronized` blocks. See https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html for more. – Solomon Slow Mar 09 '23 at 14:37
  • NB: with object wait() and notify() an override of the update() method is written in the extended Object in early java GUI and other program parts. – Samuel Marchant Mar 09 '23 at 17:42

2 Answers2

8

If a thread enters synchronized section, then other threads must wait (plain English wait, not Java wait) when they attempt to enter the same block until the thread executing the synchronized section exits it. The exit wakes up the other threads, not notify.

notify is useless without wait (and vice-versa). If your code does not need to wait, then it does not need wait, and consequently it does not need notify.

You use wait to "pause" inside the synchronized block, temporarily relinquishing its monitor so other threads can enter; then such other thread can notify to tell the waiting thread that it is okay to resume it (after it manages to acquire the monitor again, which will happen some time after the notifying thread relinquishes it). An increment function is too simple to require any waiting, and consequently can't be notified.

You can see a simple example of how wait and notify are used in A simple scenario using wait() and notify() in java: A BlockingQueue class with put and take classes. take is synchronized (which creates an object-level lock), so put and take cannot be executed at the same time. If something executes take, any other thread will block if it tries to execute not just take but also put. However, in the case the queue is empty, the "blocking queue" take is supposed to block if there are no elements, and only return when an element becomes available. The only way for an element could become available is to put one it — but put can't run because take is blocking on the object! This is where wait and notify come in: when take notices the queue is empty, it waits: pauses itself and relinquishes the monitor, enabling other threads to enter the synchronized methods again. This will let another thread execute put, which notifies the paused threads that they are free to continue. When put thread finishes with put, the blocked take thread wakes up and is able to compete for the monitor again, and when it manages to lock it, it resumes from where it left off. Without wait and notify, such an interplay between threads would not be possible.

EDIT: You are overthinking it. A thread can be New (hasn't started yet), Runnable (what we would normally say "running"), Blocked (has run into a locked section), Waiting (paused because it executed wait), Timed Waiting (paused because it executed wait with timeout), or Terminated (done, dead, gone, finished). To the extent of my understanding:

  • When a Runnable thread runs into locked synchronized section, it is Blocked.
  • When a Runnable thread runs into an unlocked synchronized section, it continues to be Runnable, and the associated lock is locked. When a thread exits a synchronized section, the associated lock is unlocked.
  • When the lock is unlocked, if there are any threads Blocked on the same lock, one of them becomes Runnable, and the lock is locked again. As far as I know, the method of picking which thread wakes up is not specified, so it is not necesarily a "queue".
  • If wait is executed by a Runnable thread inside a synchonized section, it becomes Waiting (or Timed Waiting, if the timeout was provided to wait), and the lock unlocks.
  • If a Runnable thread inside a synchronized section executes notify, a random Waiting or Timed Waiting becomes Blocked (and will have a chance to become Runnable when the current lock owner exits the synchronized section). (notifyAll will change all Waiting and Timed Waiting threads on the same lock into Blocked.)
  • If Timed Waiting thread's timeout is reached, it turns into Blocked on its own, without needing a notify.

Note that the first three points completely describe what happens with synchronize; there is no need for wait or notify for JVM to correctly block and wake up the threads accessing a synchronized section.

"how threads can wait and wake up after exit of synchronized block" is not your concern, it is JVM doing JVM things. You have done your part by writing synchronized. If I may make an analogy, it is close to asking how a variable is incremented when you write i++, even though you did not use any assignments. That's just what ++, and synchronized, do.

Amadan
  • 191,408
  • 23
  • 240
  • 301
  • On your first paragraph, how threads can wait and wake up after exit of synchronized block ? Does it mean that there is another waiting queue which is not same with the waiting queue(i.e. condition variable) for threads which to be notified(java `notify`) ? – cjlee Mar 09 '23 at 03:12
  • I believe there are two queues, yes. One is a `wait()` queue and the other is for blocked threads for that monitor. The "queues" may not be real queues, for example monitors aren't "fair" and don't execute threads in the order that they block. – markspace Mar 09 '23 at 03:35
  • I added more details, does that answer your question? – Amadan Mar 09 '23 at 03:39
  • Re: "You use `wait` to 'pause' inside the `synchronized` block, temporarily relinquishing its monitor so other threads can enter": I don't know if I agree with this characterization. Often the `wait` call is the main reason for even having the `synchronized` block. – ruakh Mar 09 '23 at 03:55
  • @ruakh "Often the `wait` call is the main reason for even having the `synchronized` block." Sure, I agree with that. I can't see what about it invalidates my characterization, though. Can you explain further? – Amadan Mar 09 '23 at 03:58
  • @Amadan Now I can grab a clue for how's it going. I've overthought as you described, because 'wait' method makes other thread can enter the `synchronized` block which makes me think that there is a relevance(like sharing of condition variable) between both of them. The difference between two concept of `waiting` and `blocked` was helpful. Thank you ! – cjlee Mar 09 '23 at 05:08
  • @cjlee You may also want to look at the `java.util.concurrent.locks` package, which splits the "lock" and "condition" into the two `Lock` and `Condition` interfaces. That may make it easier to understand the concepts involved. Of course, it may not help at all, or worse make things more confusing (on top of just being more information to comprehend, that API is slightly more complicated than `synchronized`) :/ – Slaw Mar 09 '23 at 10:34
0

I'm trying to understand synchronization in Java, but it's not clear to me that 'When to use wait() method in Object class.

You use the wait() method when you need to pause the running thread until a condition is reached. A perfect example is a blocking-queue. When you go to take() an item off of the queue, if the queue is empty then you will have to wait() for another thread to add an item to the queue. Another thread adds an item to the queue and calls notify() to wake any threads up that are waiting.

If a thread 'X' enters synchronized block with monitor(mutex), then other threads would be blocked and wait for signal from 'X' thread, isn't it ? But how is it possible ?

This is a bit confusing. You shouldn't use the word "wait" here because the blocking and releasing of threads around a synchronized section work different from wait() and notify() methods. If a thread enters the synchronized section then the other threads block until the thread exits the section and they are released. They are in a BLOCKED state not a WAITING state.

Is the compiler adding a function at the end of the code ? (But when I added calling wait() method without notify() method, it didn't work well) What is happening in my code?

No. The Java language releases the lock when a thread leaves the synchronized block allowing another thread to take the lock and move from BLOCKED to RUNNING. No use of wait().

In this case, when I delete notifyAll() method, it never ends. So, I thought that "notify() method is not executed after exit of synchronized block". Is this right?

That's right. There is no magic notify methods. Either your code calls notify() or notifyAll() on the specific object or it doesn't. Since your code is entering a synchronized method on the Example instance and calling wait() on that instance, then some other thread will need to call notify() or notifyAll() otherwise the waiting thread will wait forever.

Gray
  • 115,027
  • 24
  • 293
  • 354