Thread Communication in Java Using wait(), notify(), and notifyAll()

Illustration for Thread Communication in Java Using wait(), notify(), and notifyAll()
By Last updated:

Multithreading enables performance and responsiveness, but coordination between threads is critical. Java provides wait(), notify(), and notifyAll() as native methods to facilitate thread communication.

This tutorial unpacks how these methods work, when to use them, and how to avoid concurrency pitfalls in Java applications.


🧵 What is Thread Communication?

Thread communication in Java allows threads to cooperate over shared data, avoiding race conditions and unnecessary CPU cycles from busy waiting.

Real-world Analogy

Think of a delivery system: a producer thread (sender) prepares a parcel and puts it in a dropbox. A consumer thread (delivery boy) waits until there is a parcel. The consumer sleeps (wait()), and the producer wakes it up (notify()) once the parcel is ready.


🚦 Thread Lifecycle Overview

Java threads move between the following states:

  • NEW → RUNNABLE → RUNNING → WAITING/BLOCKED → TERMINATED

The wait() method transitions the thread to the WAITING state, pausing its execution.


🔄 Core Thread Communication Methods

wait()

  • Causes the current thread to release the monitor and go into waiting state.
  • Only resumes when notify() or notifyAll() is called.
  • Must be used inside a synchronized block.

notify()

  • Wakes one waiting thread on the object's monitor.

notifyAll()

  • Wakes all threads waiting on the object's monitor.
synchronized (sharedLock) {
    while (!conditionMet) {
        sharedLock.wait();
    }
    // Do work once condition is true
}

synchronized (sharedLock) {
    conditionMet = true;
    sharedLock.notify(); // or notifyAll()
}

⚠️ Key Rules to Remember

  • All methods must be called within synchronized blocks.
  • Always use wait() inside a while loop, not if, to prevent spurious wakeups.
  • notify() randomly picks one thread; notifyAll() wakes all.

👷‍♂️ Producer-Consumer Example

class Buffer {
    private boolean full = false;

    synchronized void produce() throws InterruptedException {
        while (full) wait();
        System.out.println("Produced item");
        full = true;
        notify();
    }

    synchronized void consume() throws InterruptedException {
        while (!full) wait();
        System.out.println("Consumed item");
        full = false;
        notify();
    }
}

🔁 wait/notify vs Modern Alternatives

Goal Preferred Tool
Thread coordination wait(), notify(), notifyAll()
Producer-consumer BlockingQueue
Task completion Future, CompletableFuture
Scheduling Executors, ForkJoinPool

📌 What's New in Java Concurrency (8–21)

  • Java 8: Lambdas, CompletableFuture, parallel streams
  • Java 9: Flow API for reactive programming
  • Java 11: Minor improvements in CompletableFuture
  • Java 21: Structured concurrency, virtual threads, scoped values (Project Loom)

🛠 Best Practices

  • Keep synchronized sections short and efficient.
  • Avoid calling wait/notify on this unless it's safe.
  • Prefer higher-level constructs like BlockingQueue when applicable.
  • Ensure proper lock release using try-finally blocks.

❓ FAQ

  1. Can wait() be used outside synchronized?
    No. It throws IllegalMonitorStateException.

  2. Why use while instead of if with wait()?
    To protect against spurious wakeups.

  3. Is notify() guaranteed to wake the right thread?
    No, it's non-deterministic. notifyAll() is safer.

  4. What is a spurious wakeup?
    When a thread wakes without being notified.

  5. How is join() different from wait()?
    join() waits for thread completion, not for condition.

  6. Can I use notify() without wait()?
    Yes, but it does nothing if no thread is waiting.

  7. What happens if a thread is never notified?
    It remains in WAITING state indefinitely (potential deadlock).

  8. Should I use notifyAll() always?
    Use it when multiple waiting threads need rechecking.

  9. Are wait() and notify() deprecated?
    No, but newer constructs often provide better abstraction.

  10. What is the monitor object?
    It's the object whose lock is acquired in a synchronized block.


🧾 Conclusion and Key Takeaways

  • wait(), notify(), and notifyAll() are essential for low-level thread coordination.
  • Always use them with synchronization and within loops.
  • In modern Java, consider high-level alternatives when available.
  • Understanding these core tools is key for writing responsive and correct concurrent applications.