Inter-thread communication in Java is a mechanism that allows threads to communicate with each other and coordinate their actions. This is important when threads share resources and need to avoid conflicts or ensure proper sequencing. Java provides built-in ways to achieve this, mainly using wait()
, notify()
, and notifyAll()
methods. Let’s break it down clearly.
1. Key Concepts
- Thread Communication: Allows threads to signal each other about changes in state (e.g., data availability).
- Shared Resource: Usually, threads communicate via a common object (like a queue or buffer).
- Synchronization: Communication methods (
wait
,notify
,notifyAll
) must be called inside a synchronized block because they require the thread to hold the object’s monitor.
2. Core Methods
These methods are defined in the java.lang.Object
class:
Method | Description |
---|---|
wait() | Causes the current thread to wait until another thread calls notify() or notifyAll() on the same object. The thread releases the lock while waiting. |
notify() | Wakes up one waiting thread on the object’s monitor. If multiple threads are waiting, one is chosen arbitrarily. |
notifyAll() | Wakes up all waiting threads on the object’s monitor. |
3. How It Works
- Producer-Consumer Problem is the classic example:
- Producer: Produces data and notifies consumer.
- Consumer: Consumes data and notifies producer if needed.
- Steps:
- Threads share a common object (e.g., a queue or buffer).
- A thread checks a condition (e.g., buffer full/empty).
- If condition isn’t met, thread calls
wait()
. - Another thread changes the condition and calls
notify()
ornotifyAll()
to wake waiting threads.
4. Example Code: Producer-Consumer
class SharedResource {
private int data;
private boolean available = false;
public synchronized void produce(int value) throws InterruptedException {
while (available) {
wait(); // Wait until consumer consumes
}
data = value;
available = true;
System.out.println("Produced: " + value);
notify(); // Notify consumer
}
public synchronized int consume() throws InterruptedException {
while (!available) {
wait(); // Wait until producer produces
}
available = false;
System.out.println("Consumed: " + data);
notify(); // Notify producer
return data;
}
}
public class Main {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Thread producer = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
try {
resource.produce(i);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread consumer = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
try {
resource.consume();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
producer.start();
consumer.start();
}
}
Explanation:
produce()
waits if data is already available.consume()
waits if no data is available.notify()
wakes the other thread after the state changes.
5. Tips
- Always use
while
instead ofif
when checking conditions to avoid spurious wakeups. notifyAll()
is safer in scenarios with multiple waiting threads.- Modern alternatives include
BlockingQueue
,Semaphore
, andLock
/Condition
, which simplify inter-thread communication.