Table of contents
1.
Introduction
2.
What is Java wait() Method?
3.
Syntax
4.
Parameters
5.
Return Value
6.
Exceptions in wait() method
7.
Example of Java Wait()
7.1.
Example 1-: Let’s see a simple example that demonstrates the usage of the wait() method in Java
7.2.
Example 2-:  Producer-Consumer Problem:
7.3.
Example 3-:  Bounded Buffer Problem
8.
Comparison of the wait() and sleep() methods in Java
9.
Frequently Asked Questions
9.1.
What happens when a thread calls the wait() method without acquiring the lock on the object?
9.2.
Can multiple threads call the wait() method on the same object simultaneously?
9.3.
What is the difference between the notify() and notifyAll() methods?
10.
Conclusion
Last Updated: Nov 18, 2024
Easy

Wait() Method in Java

Author Rinki Deka
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

The wait() method is used to pause the execution of a thread until another thread notifies it to resume. It's a part of the Java synchronization mechanism that allows threads to communicate and coordinate their activities. The wait() method is very useful in multi-threaded applications where multiple threads need to work together to achieve a common goal. 

Wait() Method in Java

In this article, we'll look at the syntax, parameters, return value, and exceptions of the wait() method in Java. 

What is Java wait() Method?

The wait() method in Java is used to pause the execution of a thread until another thread notifies it to resume. It is a part of the Java synchronization mechanism that allows threads to communicate & coordinate their activities.

When a thread calls the wait() method on an object, it must first acquire the lock (monitor) of that object. Once the lock is acquired, the thread releases the lock & enters the waiting state. 

The thread will remain in the waiting state until one of the following occurs:

1. Another thread calls the notify() or notifyAll() method on the same object.
 

2. The specified timeout expires (if a timeout is provided).
 

3. The thread is interrupted by another thread.


Once the thread is notified, it re-acquires the lock on the object & resumes execution from the point where it left off.

The wait() method is used in situations where a thread needs to wait for a specific condition to be met before proceeding. For example, a thread may need to wait for data to become available, for a resource to be freed, or for another thread to complete a certain task.


Always Remember: The wait() method should always be called inside a synchronized block or method to ensure that the thread owns the lock on the object. Failing to do so will result in an IllegalMonitorStateException.


Note: The wait() method is often used in conjunction with the notify() & notifyAll() methods to implement thread communication & synchronization.

Syntax

The syntax of the wait() method in Java is:

public final void wait() throws InterruptedException
public final void wait(long timeout) throws InterruptedException
public final void wait(long timeout, int nanos) throws InterruptedException


The wait() method is declared as a final method in the Object class, which means it cannot be overridden by any subclass. It takes an optional timeout parameter that specifies the maximum time the thread should wait before resuming execution. If the timeout is not specified, the thread will wait indefinitely until it is notified by another thread.

Parameters

The wait() method can take up to two parameters:

1. timeout: This parameter specifies the maximum time (in milliseconds) the thread should wait before resuming execution. If the timeout is not specified, the thread will wait indefinitely until it is notified by another thread.
 

2. nanos: This parameter is used in conjunction with the timeout parameter to specify the additional nanoseconds the thread should wait. It allows for more precise control over the waiting time.


Note: Always Remember that the thread must own the monitor (lock) of the object on which it is calling the wait() method. If the thread doesn't own the monitor, an IllegalMonitorStateException will be thrown.

Return Value

The wait() method does not return a value. It is a void method, which means it does not have a return type.

When a thread calls the wait() method, it releases the monitor (lock) of the object & enters the waiting state. The thread will remain in the waiting state until one of the following conditions occurs:
 

1. Another thread calls the notify() or notifyAll() method on the same object.
 

2. The specified timeout expires.
 

3. The thread is interrupted by another thread.
 

Note: Once the thread is notified or the timeout expires, it will re-acquire the monitor and resume execution from where it left off.

Exceptions in wait() method

The wait() method can throw two types of exceptions:

1. InterruptedException: This exception is thrown when a waiting thread is interrupted by another thread. It occurs when the interrupt() method is called on the waiting thread. The InterruptedException is a checked exception, which means it must be caught or declared in the method signature.
 

2. IllegalMonitorStateException: This exception is thrown when a thread tries to call the wait() method on an object without owning its monitor (lock). It occurs when the thread doesn't synchronize on the object before calling the wait() method. The IllegalMonitorStateException is an unchecked exception, which means it doesn't need to be caught or declared in the method signature.

For example: 

synchronized (obj) {
    try {
        obj.wait();
    } catch (InterruptedException e) {
        // Handle the interruption
        Thread.currentThread().interrupt();
    }
}


In this example, the thread synchronizes on the object 'obj' before calling the wait() method. If an InterruptedException occurs, it is caught & handled by setting the interrupt status of the thread.

Example of Java Wait()

Example 1-: Let’s see a simple example that demonstrates the usage of the wait() method in Java

public class WaitExample {
    private static final Object lock = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    try {
                        System.out.println("Thread 1 is waiting...");
                        lock.wait();
                        System.out.println("Thread 1 resumed.");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });


        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    System.out.println("Thread 2 is notifying...");
                    lock.notify();
                }
            }
        });


        thread1.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.start();
    }
}
You can also try this code with Online Java Compiler
Run Code


In this example, we have two threads: thread1 & thread2. Thread1 acquires the lock on the 'lock' object & calls the wait() method, which causes it to release the lock & enter the waiting state. Thread2 acquires the same lock & calls the notify() method, which notifies thread1 to resume execution. 

The output of this program will be:

Thread 1 is waiting...
Thread 2 is notifying...
Thread 1 resumed.


As you can see, thread1 waits until thread2 notifies it to resume execution.

Example 2-:  Producer-Consumer Problem:

In this example, we have a producer thread that generates data & adds it to a queue, and a consumer thread that consumes the data from the queue. The consumer thread waits if the queue is empty, and the producer thread notifies the consumer when data is added to the queue.

public class ProducerConsumer {
    private static final Object lock = new Object();
    private static Queue<Integer> queue = new LinkedList<>();
    private static final int QUEUE_SIZE = 5;


    public static void main(String[] args) {
        Thread producer = new Thread(new Producer());
        Thread consumer = new Thread(new Consumer());


        producer.start();
        consumer.start();
    }


    static class Producer implements Runnable {
        @Override
        public void run() {
            int count = 0;
            while (true) {
                synchronized (lock) {
                    while (queue.size() == QUEUE_SIZE) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.add(count);
                    System.out.println("Produced: " + count);
                    count++;
                    lock.notify();
                }
            }
        }
    }


    static class Consumer implements Runnable {
        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    while (queue.isEmpty()) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    int data = queue.poll();
                    System.out.println("Consumed: " + data);
                    lock.notify();
                }
            }
        }
    }
}
You can also try this code with Online Java Compiler
Run Code

 

In this example, the producer thread waits if the queue is full, and the consumer thread waits if the queue is empty. The threads use the wait() & notify() methods to coordinate their activities.

Example 3-:  Bounded Buffer Problem

This example is similar to the producer-consumer problem, but it uses a bounded buffer to store the data. The producer thread waits if the buffer is full, and the consumer thread waits if the buffer is empty.

public class BoundedBuffer {
    private static final Object lock = new Object();
    private static final int BUFFER_SIZE = 5;
    private static int[] buffer = new int[BUFFER_SIZE];
    private static int count = 0;
    private static int in = 0;
    private static int out = 0;
    public static void main(String[] args) {
        Thread producer = new Thread(new Producer());
        Thread consumer = new Thread(new Consumer());


        producer.start();
        consumer.start();
    }
  static class Producer implements Runnable {
        @Override
        public void run() {
            int data = 0;
            while (true) {
                synchronized (lock) {
                    while (count == BUFFER_SIZE) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    buffer[in] = data;
                    System.out.println("Produced: " + data);
                    data++;
                    in = (in + 1) % BUFFER_SIZE;
                    count++;
                    lock.notify();
                }
            }
        }
    }
static class Consumer implements Runnable {
        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    while (count == 0) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    int data = buffer[out];
                    System.out.println("Consumed: " + data);
                    out = (out + 1) % BUFFER_SIZE;
                    count--;
                    lock.notify();
                }
            }
        }
    }
}
You can also try this code with Online Java Compiler
Run Code


In this example, the producer thread adds data to the buffer & waits if the buffer is full. The consumer thread consumes data from the buffer & waits if the buffer is empty. The threads use the wait() & notify() methods to synchronize their access to the buffer.

Comparison of the wait() and sleep() methods in Java

wait()sleep()
The wait() method is used for inter-thread communication and synchronization. It allows a thread to wait for a specific condition to be met before proceeding.The sleep() method is used to pause the execution of a thread for a specified amount of time. It does not involve any inter-thread communication or synchronization.
The wait() method releases the lock (monitor) on the object when the thread enters the waiting state. Other threads can acquire the lock and proceed with their execution.The sleep() method does not release the lock on the object. If a thread holds a lock and calls the sleep() method, other threads cannot acquire the lock until the sleeping thread wakes up.
The wait() method is called on an object. The thread must first acquire the lock on the object before calling the wait() method.The sleep() method is called on the Thread class itself. It is a static method and does not require any lock acquisition.
The wait() method is typically used in conjunction with the notify() or notifyAll() methods to achieve thread communication and coordination.The sleep() method is typically used to introduce a pause or delay in the execution of a thread, without any coordination with other threads.
The wait() method can be overloaded with a timeout parameter to specify the maximum time the thread should wait before resuming execution.The sleep() method takes a duration parameter that specifies the amount of time the thread should sleep.
The wait() method throws an InterruptedException if the waiting thread is interrupted by another thread. It also throws an IllegalMonitorStateException if the thread does not own the lock on the object.The sleep() method throws an InterruptedException if the sleeping thread is interrupted by another thread.

Frequently Asked Questions

What happens when a thread calls the wait() method without acquiring the lock on the object?

When a thread calls the wait() method without acquiring the lock on the object, it will throw an IllegalMonitorStateException. The thread must first acquire the lock using the synchronized keyword before calling the wait() method to avoid this exception.

Can multiple threads call the wait() method on the same object simultaneously?

Yes, multiple threads can call the wait() method on the same object simultaneously. Each thread will release the lock on the object and enter the waiting state independently. When a thread is notified, it will compete with other waiting threads to re-acquire the lock and resume execution.

What is the difference between the notify() and notifyAll() methods?

The notify() method wakes up a single thread that is waiting on the object's monitor, chosen arbitrarily among the waiting threads. The notifyAll() method wakes up all the threads that are waiting on the object's monitor. The awakened threads will compete for the lock, and only one thread will acquire it and proceed with its execution.

Conclusion

In this article, we discussed the wait() method in Java, which is used for inter-thread communication and synchronization. We learned about its syntax, parameters, return value, and the exceptions it can throw. We also saw examples of how the wait() method can be used in different scenarios, like the producer-consumer problem and the bounded buffer problem. Moreover, we compared the wait() method with the sleep() method and highlighted their differences. 

You can also check out our other blogs on Code360.

Live masterclass