Multithreading is one of the most powerful concepts in Java โ and understanding inter-thread communication using wait()
and notify()
is essential for writing efficient concurrent programs.
In this blog, weโll walk you through:
โ
What wait()
and notify()
do
โ
How to use them properly with synchronized blocks
โ
A real-world Producer-Consumer implementation
โ
Full code + output explanation
๐ What are wait()
and notify()
?
-
wait()
: Causes the current thread to release the monitor and go into a waiting state until another thread callsnotify()
ornotifyAll()
on the same object. -
notify()
: Wakes up a single thread that is waiting on the object's monitor.
๐ง These methods must be called inside a synchronized
block or method, otherwise a IllegalMonitorStateException
is thrown.
๐งช Use Case: Producer-Consumer Problem
A producer thread generates data and puts it in a shared queue. A consumer thread removes and processes data from that queue.
Weโll use wait()
and notify()
to make sure:
- Producer waits when the queue is full.
- Consumer waits when the queue is empty.
- They notify each other when they perform a task.
โ Full Java Code
๐ก ProducerConsumer.java
import java.util.LinkedList;
import java.util.Queue;
public class ProducerConsumer {
private final Queue<Integer> queue = new LinkedList<>();
private final int CAPACITY = 5;
public synchronized void produce(int item) throws InterruptedException {
while (queue.size() == CAPACITY) {
System.out.println("Queue is full. Producer is waiting...");
wait(); // wait until consumer consumes
}
queue.add(item);
System.out.println("Produced: " + item);
notify(); // notify a waiting consumer
}
public synchronized int consume() throws InterruptedException {
while (queue.isEmpty()) {
System.out.println("Queue is empty. Consumer is waiting...");
wait(); // wait until producer produces
}
int item = queue.poll();
System.out.println("Consumed: " + item);
notify(); // notify a waiting producer
return item;
}
}
๐ก Main.java
public class Main {
public static void main(String[] args) {
ProducerConsumer pc = new ProducerConsumer();
// Producer Thread
Thread producerThread = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
pc.produce(i);
Thread.sleep(500); // simulate time to produce
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// Consumer Thread
Thread consumerThread = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
pc.consume();
Thread.sleep(800); // simulate time to consume
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
producerThread.start();
consumerThread.start();
}
}
๐งพ Sample Output
Produced: 1
Consumed: 1
Produced: 2
Produced: 3
Consumed: 2
Consumed: 3
...
Queue is full. Producer is waiting...
Consumed: 5
Produced: 6
...
Queue is empty. Consumer is waiting...
Produced: 10
Consumed: 10
โ ๏ธ Common Pitfalls
โ Mistake | โ Solution |
---|---|
Calling wait() outside a synchronized block |
Always call inside synchronized methods or blocks |
Using if instead of while for wait condition |
Use while to re-check condition after wakeup |
Using notify() instead of notifyAll() when multiple threads wait |
Use notifyAll() if multiple consumers or producers may be waiting |
๐ Summary
-
wait()
andnotify()
are tools for coordinating threads sharing a common resource. - Always use them with a proper lock (like synchronized method or block).
- A correct usage of
while
,wait
, andnotify
can help you implement thread-safe coordination.
๐ Try This Yourself
- Change the queue capacity to 1 or 2 and observe behavior.
- Add multiple producer and consumer threads and use
notifyAll()
instead ofnotify()
.
Top comments (0)