Deadlock in Java

Naveen AnamaneniUpdated · 6 min read

A deadlock in Java is a situation where two or more threads are stuck forever, each waiting for a lock that another one holds. Navika holds the pens and waits for the paper; Srihitha holds the paper and waits for the pens; neither can continue, so neither ever lets go. The program doesn't crash; it simply freezes. In this lesson, you will create a deadlock on purpose, find it with a thread dump, detect it from code, and fix it with lock ordering.

The Four Conditions

A deadlock can happen only when all four of these are true. Breaking any one of them prevents it.

ConditionMeaning
Mutual exclusionA lock can be held by only one thread at a time
Hold and waitA thread holds one lock while waiting for another
No preemptionA lock can't be taken away; only its holder releases it
Circular waitThread A waits for B's lock, and B waits for A's
Each thread holds one lock and waits for the other's: a circular wait

The Checkpoint: Creating, Finding and Fixing a Deadlock

Step 1: A Deliberate Deadlock

This program never finishes, so it is run in a terminal rather than by the example runner. The short pause makes sure both threads grab their first lock before trying for the second.

Deadlock.java
public class Deadlock {
    static final Object pens = new Object();
    static final Object paper = new Object();
 
    static void pause() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
 
    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (pens) {
                System.out.println("Navika has the pens, wants the paper");
                pause();
                synchronized (paper) {
                    System.out.println("Navika draws");
                }
            }
        }, "navika").start();
 
        new Thread(() -> {
            synchronized (paper) {
                System.out.println("Srihitha has the paper, wants the pens");
                pause();
                synchronized (pens) {
                    System.out.println("Srihitha draws");
                }
            }
        }, "srihitha").start();
    }
}
Terminal output
Navika has the pens, wants the paper
Srihitha has the paper, wants the pens

The program then hangs: neither "draws" line is ever printed.

Step 2: Capturing a Thread Dump with jstack

While it hangs, find its process ID with jps and ask the JVM for a thread dump with jstack:

Terminal
jps
jstack <process-id>

The end of the dump reports the deadlock directly (memory addresses shortened):

Terminal output
Found one Java-level deadlock:
=============================
"navika":
  waiting to lock monitor 0x... (object 0x...ce0, a java.lang.Object),
  which is held by "srihitha"
 
"srihitha":
  waiting to lock monitor 0x... (object 0x...cd0, a java.lang.Object),
  which is held by "navika"
 
Java stack information for the threads listed above:
===================================================
"navika":
    at Deadlock.lambda$main$0(Deadlock.java:19)
    - waiting to lock <0x...ce0> (a java.lang.Object)
    - locked <0x...cd0> (a java.lang.Object)
"srihitha":
    at Deadlock.lambda$main$1(Deadlock.java:29)
    - waiting to lock <0x...cd0> (a java.lang.Object)
    - locked <0x...ce0> (a java.lang.Object)
 
Found 1 deadlock.

Explanation: The dump names each thread, the lock it holds ("locked"), the lock it waits for, and the exact source line (19 and 29, the inner synchronized lines). Good thread names, such as navika and srihitha, make the dump easy to read.

Step 3: Detecting a Deadlock from Code

The JVM can also report deadlocks to the program itself through ThreadMXBean. Here the threads are daemons, so the program can exit after reporting:

DetectDeadlock.java▶ Try it
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Arrays;
 
public class DetectDeadlock {
    static final Object pens = new Object();
    static final Object paper = new Object();
 
    static Thread start(String name, Object first, Object second) {
        Thread t = new Thread(() -> {
            synchronized (first) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    return;
                }
                synchronized (second) {
                    System.out.println(name + " finished");
                }
            }
        }, name);
        t.setDaemon(true);
        t.start();
        return t;
    }
 
    public static void main(String[] args) throws InterruptedException {
        start("navika", pens, paper);
        start("srihitha", paper, pens);
        Thread.sleep(500);
 
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        long[] ids = bean.findDeadlockedThreads();
        String[] names = Arrays.stream(bean.getThreadInfo(ids))
            .map(ThreadInfo::getThreadName).sorted().toArray(String[]::new);
        System.out.println("Deadlocked threads: " + Arrays.toString(names));
    }
}
Output
Deadlocked threads: [navika, srihitha]

Explanation: After half a second, findDeadlockedThreads() returns the IDs of the threads stuck in a lock cycle. Monitoring tools use this to raise an alarm.

Step 4: The Fix, Lock Ordering

The circular wait disappears if every thread takes the locks in the same order:

NoDeadlock.java▶ Try it
public class NoDeadlock {
    static final Object pens = new Object();
    static final Object paper = new Object();
 
    static Thread draw(String name) {
        Thread t = new Thread(() -> {
            synchronized (pens) {
                synchronized (paper) {
                    System.out.println(name + " draws");
                }
            }
        }, name);
        t.start();
        return t;
    }
 
    public static void main(String[] args) throws InterruptedException {
        Thread a = draw("Navika");
        a.join();
        Thread b = draw("Srihitha");
        b.join();
        System.out.println("No deadlock: both always take pens, then paper");
    }
}
Output
Navika draws
Srihitha draws
No deadlock: both always take pens, then paper

Explanation: Both threads now lock pens first and paper second. Whoever gets pens first will also get paper, so a cycle can never form. (The threads are joined one at a time here only to make the output order fixed; the fix works with them running together too.)

Other Ways to Avoid Deadlock

  • Hold one lock at a time whenever possible.
  • Use timeouts: ReentrantLock.tryLock(timeout) gives up instead of waiting forever (covered later in the course).
  • Keep synchronized code short, and never call unknown code (such as listeners) while holding a lock.

Common Mistakes

Nested synchronized blocks in different orders in different methods. That's the classic deadlock recipe.

Calling another object's synchronized method while holding your own lock. It hides a lock order you didn't plan.

Restarting a frozen program without a thread dump. Take a jstack first; it shows the cause.

Interview Corner

What is a deadlock, and how can you prevent it?

A deadlock is a state in which two or more threads wait indefinitely for locks held by each other. It requires mutual exclusion, hold-and-wait, no preemption and a circular wait. The usual prevention is a global lock ordering so a cycle can't form; other techniques are holding only one lock at a time, using tryLock with timeouts, and avoiding calls to external code while holding locks. Deadlocks can be diagnosed with jstack thread dumps or ThreadMXBean.findDeadlockedThreads().

Practice

  1. Easy: Change NoDeadlock to start both threads before joining them, and run it many times to confirm it never hangs.
  2. Harder: Write transfer(Account from, Account to, int amount) that locks both accounts without deadlocking, by always locking the account with the smaller ID first.

Quick check: Deadlock

4 questions
1What is a deadlock?
2Which tool prints a thread dump of a running JVM?
3What prevents the circular wait?
4Which method finds deadlocked threads from code?

Key Takeaways

Was this tutorial helpful?

Lesson 402 of 482