← Back to Advanced Java Course | Chapter 2: Multithreading | Lesson 5 of 8

Java wait & notify

Inter-Thread Communication

Inter-thread communication lets synchronized threads coordinate their work and exchange signals using three methods inherited from the Object class: wait(), notify(), and notifyAll(). These let threads pause until conditions elsewhere in the program change, rather than wastefully polling in a loop.

Example: Inter-Thread Communication

java
public class Main {
	public static void main(String[] args) throws InterruptedException {
		Object monitor = new Object();
		synchronized (monitor) {
			System.out.println("wait()/notify()/notifyAll() coordinate threads via this monitor");
		}
	}
}

The wait() Method

Calling wait() inside a synchronized block immediately pauses the calling thread and, crucially, releases the monitor lock it was holding, letting other threads acquire that same lock and make progress. The waiting thread stays paused until some other thread calls notify() or notifyAll() on that same object.

Example: The wait() Method

java
public class Main {
	public static void main(String[] args) throws InterruptedException {
		final Object lock = new Object();
		Thread waiter = new Thread(() -> {
			synchronized (lock) {
				try {
					System.out.println("Waiting...");
					lock.wait(200); // releases the lock while paused
					System.out.println("Resumed");
				} catch (InterruptedException e) {}
			}
		});
		waiter.start();
		waiter.join();
	}
}

The notify() Method

The notify() method wakes up a single, arbitrarily chosen thread that's currently waiting on the object's monitor lock. It must be called from inside a synchronized block on that same object, and if multiple threads are waiting, there's no guarantee which one gets picked.

Example: The notify() Method

java
public class Main {
	public static void main(String[] args) throws InterruptedException {
		final Object lock = new Object();
		Thread waiter = new Thread(() -> {
			synchronized (lock) {
				try { lock.wait(); } catch (InterruptedException e) {}
				System.out.println("Woken by notify()");
			}
		});
		waiter.start();
		Thread.sleep(100);
		synchronized (lock) { lock.notify(); }
		waiter.join();
	}
}

The notifyAll() Method

The notifyAll() method wakes up every thread currently waiting on the object's monitor, rather than just one. This is generally the safer choice over notify() when multiple threads might be waiting for different conditions, since notify() alone could accidentally wake the wrong one.

Example: The notifyAll() Method

java
public class Main {
	public static void main(String[] args) throws InterruptedException {
		final Object lock = new Object();
		Runnable waitTask = () -> {
			synchronized (lock) {
				try { lock.wait(); } catch (InterruptedException e) {}
				System.out.println(Thread.currentThread().getName() + " woken");
			}
		};
		Thread t1 = new Thread(waitTask), t2 = new Thread(waitTask);
		t1.start(); t2.start();
		Thread.sleep(100);
		synchronized (lock) { lock.notifyAll(); } // wakes both, not just one
		t1.join(); t2.join();
	}
}

Producer-Consumer Simulation

The classic producer-consumer pattern uses wait() and notify() to coordinate a shared, bounded buffer: the producer calls wait() when the buffer is full, and the consumer calls wait() when it's empty, with each side calling notify() after changing the buffer's state to wake the other up.

Example: Producer-Consumer Simulation

java
import java.util.LinkedList;
public class Main {
	static final LinkedList<Integer> buffer = new LinkedList<>();
	static final int CAPACITY = 1;
	public static void main(String[] args) throws InterruptedException {
		Thread producer = new Thread(() -> {
			synchronized (buffer) {
				while (buffer.size() == CAPACITY) { try { buffer.wait(); } catch (InterruptedException e) {} }
				buffer.add(42);
				System.out.println("Produced 42");
				buffer.notify();
			}
		});
		Thread consumer = new Thread(() -> {
			synchronized (buffer) {
				while (buffer.isEmpty()) { try { buffer.wait(); } catch (InterruptedException e) {} }
				System.out.println("Consumed " + buffer.poll());
			}
		});
		producer.start(); producer.join();
		consumer.start(); consumer.join();
	}
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.