Understanding wait(), notify(), and notifyAll() in Java

Java provides a robust mechanism for inter-thread communication through three essential methods: wait(), notify(), and notifyAll(). These methods are part of the Object class and play a crucial role in implementing synchronization among threads. In this blog, we will explore these methods in detail, understand their functionalities, and demonstrate their usage with practical examples.

1. wait(): The wait() method is used to make a thread temporarily release its lock on the object it is synchronized on and wait until another thread notifies it to continue. It allows threads to cooperate effectively and avoid busy-waiting, where a thread continuously checks for a condition without giving a chance to other threads to execute.

class WaitExample {
    public synchronized void printMessage() {
        System.out.println("Thread " + Thread.currentThread().getId() + " is going to wait.");
        try {
            wait(); // The current thread will wait until another thread calls notify() or notifyAll().
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Thread " + Thread.currentThread().getId() + " has been notified and resumed.");
    }
}

2. notify(): The notify() method is used to wake up a single waiting thread from the pool of waiting threads. If multiple threads are waiting, only one will be selected to resume its execution. The choice of which thread to notify is not deterministic.

class NotifyExample {
    public synchronized void notifyThread() {
        System.out.println("Notifying one waiting thread to resume.");
        notify(); // This will wake up a single waiting thread.
    }
}

3. notifyAll(): The notifyAll() method is used to wake up all the waiting threads. When multiple threads are waiting, calling notifyAll() will cause all of them to resume their execution.

class NotifyAllExample {
    public synchronized void notifyAllThreads() {
        System.out.println("Notifying all waiting threads to resume.");
        notifyAll(); // This will wake up all the waiting threads.
    }
}

Publisher-Subscriber Example:

Publisher-Subscriber Example is the best example to understand the above 3 methods. Let's demonstrate the concepts of wait(), notify(), and notifyAll() by implementing a simple Publisher-Subscriber pattern.

import java.util.LinkedList;
import java.util.Queue;

class Publisher {
    private Queue<String> messages = new LinkedList<>();

    public synchronized void publishMessage(String message) {
        messages.offer(message);
        System.out.println("Published: " + message);
        notifyAll(); // Notify all waiting subscribers that a new message is available.
    }
}

class Subscriber implements Runnable {
    private Publisher publisher;

    public Subscriber(Publisher publisher) {
        this.publisher = publisher;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (publisher) {
                try {
                    // Wait until a message is available for consumption.
                    while (publisher.getMessages().isEmpty()) {
                        publisher.wait();
                    }
                    // Process the message.
                    String message = publisher.getMessages().poll();
                    System.out.println("Subscriber received: " + message);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

public class PublisherSubscriberExample {
    public static void main(String[] args) {
        Publisher publisher = new Publisher();

        // Create three subscribers
        Thread subscriber1 = new Thread(new Subscriber(publisher));
        Thread subscriber2 = new Thread(new Subscriber(publisher));
        Thread subscriber3 = new Thread(new Subscriber(publisher));

        subscriber1.start();
        subscriber2.start();
        subscriber3.start();

        // Publish messages
        for (int i = 1; i <= 5; i++) {
            publisher.publishMessage("Message " + i);
            try {
                Thread.sleep(1000); // Introduce a delay between publishing messages.
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Conclusion: In this blog, we explored the wait(), notify(), and notifyAll() methods in Java and their significance in enabling communication between threads. We learned how these methods can be used to implement a simple Publisher-Subscriber pattern effectively. Using these methods, you can enhance the synchronization and coordination between threads in multi-threaded Java applications. It's essential to use these methods judiciously and follow best practices to avoid potential issues like deadlocks or missed notifications.

Did you find this article valuable?

Support Java Blogs By Hemant by becoming a sponsor. Any amount is appreciated!