Site Search:

Deadlock Analysis with Thread Dumps

Deadlock Analysis with Thread Dumps

In this section, we demonstrate a real deadlock scenario using the LeftRightDeadlock program. We reproduce the issue on macOS, capture a thread dump using kill -3, and analyze the output to understand how the deadlock occurs.

Source Code: LeftRightDeadlock.java


public class LeftRightDeadlock {
    private final Object left = new Object();
    private final Object right = new Object();

    public void leftRight() {
        synchronized (left) {
            synchronized (right) {
                doSomething();
            }
        }
    }

    public void rightLeft() {
        synchronized (right) {
            synchronized (left) {
                doSomethingElse();
            }
        }
    }

    void doSomething() {
        for (int i = 0; i < 10000; i++) {
            int j = i * i;
        }
    }

    void doSomethingElse() {
        for (int i = 0; i < 10000; i++) {
            int j = i * i;
        }
    }

    public static void main(String... args) {
        LeftRightDeadlock lrdl = new LeftRightDeadlock();
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                lrdl.leftRight();
                lrdl.rightLeft();
            }).start();
        }
    }
}

Terminal Commands to Reproduce the Deadlock


# Compile the program
javac LeftRightDeadlock.java

# Run the program and redirect output to a file
java LeftRightDeadlock > threaddump.out 2>&1 &

# Find the Java process
ps -ef | grep java

Example output:


501 36829   601   0 12:34AM ttys000    0:00.27 /usr/bin/java LeftRightDeadlock

# Send SIGQUIT to capture thread dump
kill -3 36829

Thread Dump Output (Excerpt)


>grep -A 300 "Found one Java-level deadlock:" threaddump.out
Found one Java-level deadlock:
=============================
"Thread-2":
  waiting to lock monitor 0x00006000008e4750 (object 0x000000070ff665a8, a java.lang.Object),
  which is held by "Thread-3"

"Thread-3":
  waiting to lock monitor 0x00006000008ec410 (object 0x000000070ff665b8, a java.lang.Object),
  which is held by "Thread-7"

"Thread-7":
  waiting to lock monitor 0x00006000008e4750 (object 0x000000070ff665a8, a java.lang.Object),
  which is held by "Thread-3"

Java stack information for the threads listed above:
===================================================
"Thread-2":
	at LeftRightDeadlock.leftRight(LeftRightDeadlock.java:7)
	- waiting to lock <0x000000070ff665a8> (a java.lang.Object)
	at LeftRightDeadlock.lambda$main$0(LeftRightDeadlock.java:37)
	at LeftRightDeadlock$$Lambda$1/0x0000000800000a30.run(Unknown Source)
	at java.lang.Thread.run(java.base@17.0.14/Thread.java:840)
"Thread-3":
	at LeftRightDeadlock.leftRight(LeftRightDeadlock.java:8)
	- waiting to lock <0x000000070ff665b8> (a java.lang.Object)
	- locked <0x000000070ff665a8> (a java.lang.Object)
	at LeftRightDeadlock.lambda$main$0(LeftRightDeadlock.java:37)
	at LeftRightDeadlock$$Lambda$1/0x0000000800000a30.run(Unknown Source)
	at java.lang.Thread.run(java.base@17.0.14/Thread.java:840)
"Thread-7":
	at LeftRightDeadlock.rightLeft(LeftRightDeadlock.java:16)
	- waiting to lock <0x000000070ff665a8> (a java.lang.Object)
	- locked <0x000000070ff665b8> (a java.lang.Object)
	at LeftRightDeadlock.lambda$main$0(LeftRightDeadlock.java:38)
	at LeftRightDeadlock$$Lambda$1/0x0000000800000a30.run(Unknown Source)
	at java.lang.Thread.run(java.base@17.0.14/Thread.java:840)

Found 1 deadlock.

Heap
 garbage-first heap   total 262144K, used 2949K [0x0000000700000000, 0x0000000800000000)
  region size 2048K, 2 young (4096K), 0 survivors (0K)
 Metaspace       used 4459K, committed 4672K, reserved 1114112K
  class space    used 343K, committed 448K, reserved 1048576K

Deadlock Analysis

The thread dump reveals a circular wait condition:

  • Thread-2 is waiting to lock left, which is held by Thread-3.
  • Thread-3 is waiting to lock right, which is held by Thread-7.
  • Thread-7 is waiting to lock left, which is held by Thread-3.

All threads are stuck waiting on each other, forming a cycle. The root cause is that leftRight() and rightLeft() methods acquire the same two locks but in opposite order, making deadlock inevitable when multiple threads interleave.

How to Fix

To eliminate the deadlock, use a consistent lock acquisition order throughout your code. For example, always acquire left before right, regardless of context. This ensures that no circular wait condition can occur.

Alternatively, refactor critical sections to avoid nested locking, or use higher-level concurrency utilities like ReentrantLock with try-lock and timeouts.

No comments:

Post a Comment