MultiThreaded Programming  «Prev  Next»

Lesson 8wait()and notify() methods
ObjectiveFind out how the wait() and notify() methods are used to synchronize Threads.

wait() and notify() methods are used to synchronize Threads

The task of thread synchronization is to ensure that threads do not interrupt each other while performing sensitive tasks. Although thread synchronization can often be tricky, Java helps by providing a couple of methods in the standard Object class to help out:
  1. wait() and
  2. notify().

wait() and notify() methods still used to synchronize threads in Java SE 21

The wait() and notify() methods are still used for synchronizing threads in Java SE 21, just as they have been since early versions of Java. These methods, defined in the Object class, remain part of the foundational approach to inter-thread communication in Java. However, while they are still valid, modern Java versions (like Java SE 21) provide higher-level concurrency utilities that are generally preferred due to their ease of use, better performance, and clearer abstraction. These include classes from the java.util.concurrent package, such as:
  1. Locks and Condition Variables (Lock, ReentrantLock, Condition)
    • Java introduced explicit locks through the Lock interface (and ReentrantLock implementation) which provide greater flexibility than synchronized blocks and methods.
    • Condition objects associated with locks can replace the traditional wait()/notify() mechanisms. They offer a more explicit and flexible way to handle complex thread synchronization.
    • Example:
       Lock lock = new ReentrantLock();
       Condition condition = lock.newCondition();
      
       // Waiting thread
       lock.lock();
       try {
      	 condition.await();  // Replaces wait()
       } finally {
      	 lock.unlock();
       }
      
       // Notifying thread
       lock.lock();
       try {
      	 condition.signal();  // Replaces notify()
       } finally {
      	 lock.unlock();
       }
       

  2. CountDownLatch, CyclicBarrier, and Semaphore
    • These are high-level synchronization constructs that can handle more complex scenarios and reduce the need for manual thread communication using wait() and notify().
      • CountDownLatch: Used when one or more threads need to wait for a set of operations in other threads to complete.
      • CyclicBarrier: Allows a set of threads to all wait for each other to reach a common barrier point.
      • Semaphore: Controls access to a shared resource with a given number of permits.
  3. CompletableFuture and ExecutorService
    • With Java 8 and above, the CompletableFuture class and Executor Framework became the preferred ways to handle asynchronous computations and thread management.
    • These constructs simplify asynchronous programming and allow non-blocking execution, reducing the need for manual thread synchronization.
wait() and notify() Use Cases in Java SE 21
While wait() and notify() are still used, their direct use is now less common in modern applications due to:
  • Readability issues: Code using wait() and notify() can become hard to maintain and prone to subtle bugs, especially around handling conditions like spurious wakeups.
  • Newer abstractions: Modern concurrency classes are generally more robust and intuitive.

Example of Using wait() and notify()
class SharedResource {
   private boolean isAvailable = false;

   public synchronized void waitForAvailability() throws InterruptedException {
       while (!isAvailable) {
           wait();  // Releases the lock and waits
       }
   }

   public synchronized void makeAvailable() {
       isAvailable = true;
       notify();  // Wakes up a waiting thread
   }
}

When to Use wait() and notify() You might still use wait() and notify():
  • In educational contexts or when working with legacy code.
  • When fine-grained control over thread synchronization is required and using higher-level constructs seems unnecessary.
  • For low-level thread coordination in simple scenarios.

In summary, while wait() and notify() remain part of the Java language, modern concurrency utilities provide more powerful and flexible mechanisms to synchronize threads in Java SE 21. Therefore, in most cases, the use of java.util.concurrent package classes is preferred for better performance, ease of development, and thread safety.


The wait() method pauses the current thread until it is allowed to execute by another thread. This allowance is provided when another thread calls the notify() method, which awakens the waiting thread and allows it to continue executing. You can think of the wait() and notify() methods as bringing order to the chaos of concurrently executing threads. Threads utilizing wait() and notify() execute in an orderly fashion and are generally more considerate to other threads.
The wait() and notify() methods must be called from within synchronized code such as a synchronized method or statement.
Not doing so will result in an exception being thrown.

wait() method

Wait actions occur upon invocation of wait(), or the timed forms wait(long millisecs) and wait(long millisecs, int nanosecs).
A call of wait(long millisecs) with a parameter of zero, or a call of wait(long millisecs, int nanosecs) with two zero parameters, is equivalent to an invocation of wait().
Observe the following scenario:
A thread returns normally from a wait if it returns without throwing an InterruptedException. Let thread t be the thread executing the wait method on object 'm', and let 'n' be the number of lock actions by 't' on 'm' that have not been matched by unlock actions.
One of the following actions occurs:
  1. If 'n' is zero (i.e., thread 't' does not already possess the lock for target 'm'), then an IllegalMonitorStateException is thrown.
  2. If this is a timed wait and the nanosecs argument is not in the range of 0-999999 or the millisecs argument is negative, then an IllegalArgumentException is thrown.
  3. If thread 't' is interrupted, then an InterruptedException is thrown and t's interruption status is set to false.
  4. Otherwise, the following sequence occurs:
    1. Thread t is added to the wait set of object m, and performs n unlock actions on m.
    2. Thread t does not execute any further instructions until it has been removed from m's wait set. The thread may be removed from the wait set due to any one of the following actions, and will resume sometime afterward:
  5. A notify action being performed on m in which t is selected for removal from the wait set.
    1. A notifyAll action being performed on m.
    2. An interrupt action being performed on t.
    3. If this is a timed wait, an internal action removing t from m's wait set that occurs after at least millisecs milliseconds plus nanosecs nanoseconds elapse since the beginning of this wait action.
    4. An internal action by the implementation. Implementations are permitted, although not encouraged, to perform "spurious wake-ups", that is, to remove threads from wait sets and thus enable resumption without explicit instructions to do so. Notice that this provision necessitates the Java coding practice of using wait only within loops that terminate only when some logical condition that the thread is waiting for holds.

Each thread must determine an order over the events that could cause it to be removed from a wait set. That order does not have to be consistent with other orderings, but the thread must behave as though those events occurred in that order.
For example, if a thread t is in the wait set for m, and then both an interrupt of t and a notification of m occur, there must be an order over these events.
If the interrupt is deemed to have occurred first, then t will eventually return from wait by throwing InterruptedException, and some other thread in the wait set for m (if any exist at the time of the notification) must receive the notification. If the notification is deemed to have occurred first, then t will eventually return normally from wait with an interrupt still pending.
  • Thread t performs n lock actions on m.
  • If thread t was removed from m's wait set in step 2 due to an interrupt, then t's interruption status is set to false and the wait method throws InterruptedException.

  • SEMrush Software