Java
Thread Management
Programming Errors
Exception Handling
Synchronization

I get exception when using Thread.sleep(x) or wait()

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Thread.sleep(...) and wait() both pause execution, but they are not interchangeable and they fail for different reasons. If you are seeing an exception around either one, the usual causes are InterruptedException for both methods and IllegalMonitorStateException specifically for wait() when it is called without owning the correct monitor.

sleep() and wait() Solve Different Problems

Thread.sleep(...) pauses the current thread for a period of time. It does not release any monitor locks that the thread already holds.

java
1try {
2    Thread.sleep(1000);
3} catch (InterruptedException e) {
4    Thread.currentThread().interrupt();
5}

wait() is part of Java's monitor-based coordination model. It must be called on an object whose monitor the current thread already owns, and it releases that monitor while waiting.

java
1Object lock = new Object();
2
3synchronized (lock) {
4    try {
5        lock.wait();
6    } catch (InterruptedException e) {
7        Thread.currentThread().interrupt();
8    }
9}

That difference is why sleep() is about time, while wait() is about coordination.

Why InterruptedException Happens

Both sleep() and wait() can throw InterruptedException because another thread can interrupt the current thread while it is blocked. This is not necessarily an error in the sense of broken code. It is part of Java's cancellation and coordination model.

A correct handling pattern usually looks like this:

java
1try {
2    Thread.sleep(5000);
3} catch (InterruptedException e) {
4    Thread.currentThread().interrupt();
5    return;
6}

Re-setting the interrupted flag matters because the exception clears it. If you swallow the exception and continue as if nothing happened, higher-level shutdown or cancellation logic may break.

Why wait() Also Throws IllegalMonitorStateException

wait(), notify(), and notifyAll() must be called while synchronized on the same object. If you call wait() without owning that object's monitor, Java throws IllegalMonitorStateException.

Broken example:

java
Object lock = new Object();
lock.wait();

Correct version:

java
1Object lock = new Object();
2
3synchronized (lock) {
4    lock.wait();
5}

This is the biggest source of confusion when people switch from sleep() to wait().

Use wait() in a Condition Loop

wait() should almost always be placed inside a loop that rechecks the condition:

java
1class SharedFlag {
2    private final Object lock = new Object();
3    private boolean ready = false;
4
5    public void awaitReady() throws InterruptedException {
6        synchronized (lock) {
7            while (!ready) {
8                lock.wait();
9            }
10        }
11    }
12
13    public void markReady() {
14        synchronized (lock) {
15            ready = true;
16            lock.notifyAll();
17        }
18    }
19}

The loop matters because:

  • wakeups can be spurious
  • another thread may consume the condition first
  • waking up does not guarantee the condition is now true

So if is usually wrong here. Use while.

When sleep() Is the Wrong Tool

A common beginner mistake is using Thread.sleep(...) to wait for another thread's state to change:

java
Thread.sleep(1000);
// hope another thread is finished now

That is fragile because it waits for time, not for a real condition. If the other thread is slower than expected, the code still fails. If it is faster, you wasted time.

When coordination is the real goal, prefer:

  • 'wait() and notify'
  • 'CountDownLatch'
  • 'BlockingQueue'
  • 'Semaphore'
  • higher-level concurrency utilities from java.util.concurrent

A Better Alternative with CountDownLatch

For simple one-time signaling, CountDownLatch is often clearer than manual wait():

java
1import java.util.concurrent.CountDownLatch;
2
3public class Main {
4    public static void main(String[] args) throws Exception {
5        CountDownLatch latch = new CountDownLatch(1);
6
7        Thread worker = new Thread(() -> {
8            System.out.println("worker done");
9            latch.countDown();
10        });
11
12        worker.start();
13        latch.await();
14        System.out.println("main continues");
15    }
16}

This avoids monitor mistakes and reads more directly.

Common Pitfalls

  • Calling wait() without synchronizing on the same object causes IllegalMonitorStateException.
  • Catching InterruptedException and ignoring it loses the cancellation signal.
  • Using sleep() as a crude synchronization tool makes concurrency timing fragile.
  • Using if instead of while around wait() can break under spurious wakeups.
  • Forgetting that sleep() does not release locks while wait() does.

Summary

  • 'Thread.sleep(...) waits for time, while wait() waits for a condition tied to an object's monitor.'
  • Both methods can throw InterruptedException.
  • 'wait() also requires the current thread to own the target monitor.'
  • Restore the interrupt flag when catching InterruptedException unless you have a deliberate reason not to.
  • Prefer real coordination primitives over sleep() when another thread's state is what actually matters.

Course illustration
Course illustration