Java
wait
notify
IllegalMonitorStateException
multithreading

How to use wait and notify in Java without IllegalMonitorStateException?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Java provides a robust and flexible mechanism for managing multithreading through synchronization. Among the tools at your disposal are the wait(), notify(), and notifyAll() methods, which are essential for coordinating access to shared resources. However, improper use of these methods can lead to runtime exceptions, particularly the IllegalMonitorStateException. This article explains how to use these methods effectively, avoiding common pitfalls.

Understanding the Basics

Monitor and Synchronization

In Java, every object has an intrinsic lock known as a monitor. The wait(), notify(), and notifyAll() methods are designed to be used within synchronized blocks or methods that synchronize on the object's monitor. This ensures orderly access to the resource and prevents race conditions.

Common Pitfall: IllegalMonitorStateException

This exception is typically thrown when a thread attempts to call wait(), notify(), or notifyAll() without holding the appropriate lock on the object’s monitor. The following sections describe how to correctly use these methods to avoid this exception.

How to Use wait() and notify()

Step 1: Acquire the Monitor

Before you can call wait() or notify(), you must first acquire the monitor lock on the object you are synchronizing on. This is done by using a synchronized block or method.

java
synchronized(lockObject) {
    // Your synchronized code here
}

Step 2: Use wait()

The wait() method causes the current thread to wait until another thread invokes notify() or notifyAll() on the same object. It is crucial that wait() is called within the block that has already acquired the object's monitor.

java
1synchronized(lockObject) {
2    try {
3        lockObject.wait();
4    } catch (InterruptedException e) {
5        e.printStackTrace();
6    }
7}

Step 3: Use notify()

The notify() method wakes up a single thread that is waiting on the object’s monitor. This method should also be called within a synchronized block.

java
synchronized(lockObject) {
    lockObject.notify();
}

Example: Producer-Consumer Problem

Here's an example demonstrating the correct use of wait() and notify() in the classic Producer-Consumer problem.

java
1import java.util.LinkedList;
2import java.util.Queue;
3
4public class ProducerConsumer {
5    private final Queue<Integer> queue = new LinkedList<>();
6    private final int CAPACITY = 5;
7
8    public static void main(String[] args) {
9        ProducerConsumer pc = new ProducerConsumer();
10        Thread producerThread = new Thread(() -> pc.produce());
11        Thread consumerThread = new Thread(() -> pc.consume());
12
13        producerThread.start();
14        consumerThread.start();
15    }
16
17    public void produce() {
18        synchronized(queue) {
19            while (true) {
20                if (queue.size() == CAPACITY) {
21                    try {
22                        queue.wait();
23                    } catch (InterruptedException e) {
24                        e.printStackTrace();
25                    }
26                }
27
28                queue.add(1);
29                System.out.println("Produced: " + queue.size());
30                queue.notify();
31            }
32        }
33    }
34
35    public void consume() {
36        synchronized(queue) {
37            while (true) {
38                if (queue.isEmpty()) {
39                    try {
40                        queue.wait();
41                    } catch (InterruptedException e) {
42                        e.printStackTrace();
43                    }
44                }
45                queue.poll();
46                System.out.println("Consumed: " + queue.size());
47                queue.notify();
48            }
49        }
50    }
51}

Key Points and Summary Table

The following table summarizes the key points when using wait() and notify():

Key PointDescription
SynchronizationAlways use wait() and notify() inside a synchronized block.
Object LockAcquire the monitor of the object you are using wait() on.
Handling ExceptionsUse try-catch around wait() to handle InterruptedException.
Precise Use of notify()Only call notify() after making a meaningful change in the object state.
Avoiding IllegalMonitorStateExceptionEnsure that the current thread holds the monitor before calling wait() or notify().

Additional Tips

  • Use notifyAll(): When possible, prefer notifyAll() if multiple threads may be waiting, to avoid missing wake-up signals.
  • Recheck Conditions: Always recheck your condition in a loop after waking up, as interrupt() or spurious wake-ups can happen.
  • Atomic Operations: Ensure that the operations between wait() and notify() are as small and quick as possible to reduce the lock contention.

With these guidelines and examples, you should be able to effectively use wait(), notify(), and notifyAll() in Java without encountering IllegalMonitorStateException. Understanding the importance of the object's monitor in the context of these methods is critical for synchronized thread communication.


Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.