Java tutorials > Multithreading and Concurrency > Threads and Synchronization > Difference between `wait()`, `notify()`, `notifyAll()`?
Difference between `wait()`, `notify()`, `notifyAll()`?
In Java multithreading, wait()
, notify()
, and notifyAll()
are essential methods for thread synchronization and communication. They enable threads to coordinate their actions by pausing execution (waiting) and signaling other threads (notifying) when certain conditions are met. These methods are part of the Object
class and are used in conjunction with synchronized blocks or methods to prevent race conditions and ensure data consistency.
Core Concepts Behind `wait()`, `notify()`, and `notifyAll()`
These methods operate within a synchronized context (a synchronized block or method). They facilitate inter-thread communication by allowing threads to temporarily release the lock on an object ( It's crucial to call these methods from within a synchronized block or method; otherwise, an wait()
) and reacquire it when notified by another thread (notify()
or notifyAll()
).wait()
: Causes the current thread to release the lock on the object and enter a waiting state until another thread invokes notify()
or notifyAll()
for that object.notify()
: Wakes up a single thread that is waiting on the object's monitor. If multiple threads are waiting, the JVM chooses one arbitrarily.notifyAll()
: Wakes up all threads that are waiting on the object's monitor. Each thread then competes to reacquire the lock.IllegalMonitorStateException
will be thrown.
Basic Usage Example
This example illustrates a simple producer-consumer scenario using a DataBuffer
. The write()
method, used by the producer, waits if the buffer is full and then writes data and notifies the consumer. The read()
method, used by the consumer, waits if the buffer is empty, reads data and notifies the producer. Both methods are synchronized to protect shared resources.
class DataBuffer {
private String data;
private boolean isEmpty = true;
public synchronized void write(String newData) throws InterruptedException {
while (!isEmpty) {
wait(); // Wait if the buffer is not empty
}
data = newData;
isEmpty = false;
System.out.println("Written: " + data);
notifyAll(); // Notify waiting threads that data is available
}
public synchronized String read() throws InterruptedException {
while (isEmpty) {
wait(); // Wait if the buffer is empty
}
String result = data;
isEmpty = true;
System.out.println("Read: " + result);
notifyAll(); // Notify waiting threads that the buffer is empty
return result;
}
}
public class WaitNotifyExample {
public static void main(String[] args) {
DataBuffer buffer = new DataBuffer();
Thread writer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
buffer.write("Data " + i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread reader = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
buffer.read();
Thread.sleep(150);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
writer.start();
reader.start();
}
}
Real-Life Use Case: Message Queues
Message queues are a practical example. Producers place messages in a queue, and consumers retrieve messages from the queue. wait()
and notify()
/notifyAll()
are used to signal when the queue has new messages (for consumers) or available space (for producers). This enables asynchronous communication between different parts of a system.
When to use `wait()`, `notify()`, and `notifyAll()`
Use these methods when you need threads to coordinate their actions based on specific conditions. Specifically:
Difference between `notify()` and `notifyAll()` in detail
The key difference lies in which waiting threads are woken up:notify()
: Wakes up only one arbitrary thread waiting on the object's monitor. If multiple threads are waiting, there's no guarantee which one will be chosen. This can lead to 'spurious wakeups' where a thread wakes up but the condition it was waiting for is still not true.notifyAll()
: Wakes up all threads waiting on the object's monitor. Each thread then contends for the lock, and after acquiring it, checks the condition to see if it can proceed.notifyAll()
is generally preferred because it avoids the risk of a single thread being repeatedly notified while others remain waiting indefinitely ('lost wakeup' problem). However, notifyAll()
can be less efficient if many threads are waiting, as they all wake up and compete for the lock, even if only one can proceed.
Best Practices
wait()
in a loop: Conditions can change between the time a thread is notified and the time it reacquires the lock. Therefore, always re-check the condition in a loop. This handles spurious wakeups.notifyAll()
unless you have a very specific reason to use notify()
: notifyAll()
generally leads to more robust code.
Memory Footprint
The memory footprint of these methods is relatively small. Each object maintains a wait set (a queue of waiting threads). The size of this wait set depends on the number of threads that are waiting on the object's monitor. The overhead is generally negligible unless you have a very large number of threads waiting on a single object.
Alternatives
Alternatives to Using these higher-level constructs often leads to cleaner and more maintainable code.wait()
, notify()
, and notifyAll()
include:java.util.concurrent
package: This package provides higher-level concurrency utilities like Lock
, Condition
, BlockingQueue
, and Semaphore
, which can simplify complex synchronization scenarios.CountDownLatch
: Allows one or more threads to wait until a set of operations being performed in other threads completes.CyclicBarrier
: Allows a set of threads to all wait for each other to reach a common barrier point.Exchanger
: Allows two threads to exchange objects.
Interview Tip
When discussing these methods in an interview, be sure to explain the following:notify()
and notifyAll()
, including the advantages and disadvantages of each.java.util.concurrent
package.
Pros of Using `wait()`, `notify()`, and `notifyAll()`
Cons of Using `wait()`, `notify()`, and `notifyAll()`
FAQ
-
What happens if I call `wait()`, `notify()`, or `notifyAll()` outside of a synchronized block or method?
AnIllegalMonitorStateException
will be thrown at runtime. -
What is a spurious wakeup?
A spurious wakeup is when a thread wakes up from await()
call even though it has not been notified. This can happen due to reasons internal to the JVM. That's why you should always use `wait()` inside a loop that checks the condition you're waiting for. -
Why is `notifyAll()` generally preferred over `notify()`?
notifyAll()
avoids the 'lost wakeup' problem, where a thread might miss a notification and wait indefinitely. While it can be less efficient in some cases, it's generally more robust. -
How do I prevent deadlocks when using these methods?
Ensure that threads acquire locks in a consistent order and avoid holding multiple locks at the same time whenever possible. Careful design of your synchronization logic is crucial.