Deadlock in Java Multithreading

Last Updated : 6 Oct, 2026

Deadlock in Java occurs when two or more threads remain blocked because each thread is waiting for a resource or lock held by another thread. As a result, the involved threads cannot continue their execution.

  • Commonly occurs when threads acquire multiple locks in different orders.
  • The involved threads remain blocked unless the deadlock is resolved externally or the application terminates.

How Deadlock Occurs

Consider two threads and two locks:

Thread 1 holds Lock A -> waits for Lock B
Thread 2 holds Lock B -> waits for Lock A

Neither thread can release its current lock because it is waiting to acquire the other lock. This creates a circular dependency and results in deadlock.

Example: Below is a simple example demonstrating a deadlock condition in Java.

Java
// Utility class to pause thread execution
class Util { 
    static void sleep(long millis)
    {
        try {
            Thread.sleep(millis);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
// this class is shared by both threads
class Shared {
  
    // first synchronized method
    synchronized void test1(Shared s2)
    {
        System.out.println(Thread.currentThread().getName()
                           + " enters test1 of " + this);
        Util.sleep(1000);
      
        // Trying to call test2 on another object
        s2.test2();
        System.out.println(Thread.currentThread().getName()
                           + " exits test1 of " + this);
    }
  
    // Second synchronized method
    synchronized void test2()
    {
        System.out.println(Thread.currentThread().getName()
                           + " enters test2 of " + this);
        Util.sleep(1000);
      
        // taking object lock of s1 enters into test1 method
        System.out.println(Thread.currentThread().getName()
                           + " exits test2 of " + this);
    }
}

class Thread1 extends Thread {
    private Shared s1;
    private Shared s2;
  
    // constructor to initialize fields
    public Thread1(Shared s1, Shared s2)
    {
        this.s1 = s1;
        this.s2 = s2;
    }
  
    // run method to start a thread
    @Override public void run() { s1.test1(s2); }
}

class Thread2 extends Thread {
    private Shared s1;
    private Shared s2;
  
    // constructor to initialize fields
    public Thread2(Shared s1, Shared s2)
    {
        this.s1 = s1;
        this.s2 = s2;
    }
  
    // run method to start a thread
    @Override public void run() { s2.test1(s1); }
}
public class Geeks {
  
  // In this class deadlock occurs
    public static void main(String[] args)
    {
        // creating one object
        Shared s1 = new Shared();
        Shared s2 = new Shared();
      
        // creating first thread and starting it
        Thread1 t1 = new Thread1(s1, s2);
        t1.setName("Thread1");
        t1.start();
      
        // creating second thread and starting it
        Thread2 t2 = new Thread2(s1, s2);
        t2.setName("Thread2");
        t2.start();
        Util.sleep(2000);
    }
}

Output:

Output
Output

Note: It is not recommended to run the program in an online IDE. We can run the above source code locally, but it gets stuck in a deadlock, preventing execution.

Explanation:

  • Thread t1 starts by acquiring a lock on the s1 and enters the test1() method of s1.
  • Thread t2 starts by acquiring a lock on the s2 and enters the test1() method of s2.
  • In the test1() method both threads try to acquire locks on each other's objects but the locks are already held by the other thread causing both threads to wait indefinitely for the other to release the lock.
  • Neither test1() nor test2() methods complete execution and the program remains stuck in the deadlock state.

Conditions for Deadlock

A deadlock can occur when these four conditions hold simultaneously:

  • Mutual Exclusion: A resource can be held by only one thread at a time.
  • Hold and Wait: A thread holds one resource while waiting for another.
  • No Preemption: A resource cannot simply be taken away from the thread holding it.
  • Circular Wait: A circular chain exists in which each thread waits for a resource held by another thread.

Breaking at least one of these conditions is the basis of common deadlock-prevention strategies.

Locks

Java uses locks to coordinate access to shared resources. For example, entering a synchronized instance method requires acquiring the object's monitor lock. If another thread already owns that monitor, the requesting thread must wait until the lock becomes available.

thread
Locks in Java

Detecting Deadlocks

We can detect deadlocks in a running Java program using the following steps:

1. List the active Java processes:

jps -l

Response:

List of Running Thread
Response

This will list the running Java processes and also mention that there is a deadlock if we want to generate a thread dump.

2. Identify the process ID (PID) of the target program and run:

jcmd <PID> Thread.print // replace PID with the process ID

Replace <PID> with the process ID from the list provided by jps -l. This command outputs the state of the threads, which you can then analyze for deadlocks.

After running the above two commands, we can see deadlock occurs:

As we can see it is mentioned that found 1 deadlock.

Preventing Deadlocks

Deadlocks are easier to prevent through careful lock management than to handle after they occur. Java itself does not automatically prevent deadlocks, so applications that acquire multiple locks should follow consistent locking strategies.

  • Maintain a Consistent Lock Order: When multiple locks are required, ensure that threads acquire them in the same order.
  • Avoid Unnecessary Nested Locks: Keep the number of simultaneously held locks as small as possible.
  • Keep Critical Sections Small: Hold locks only for the code that actually requires synchronization.
  • Use Timed Lock Attempts When Appropriate: APIs such as ReentrantLock.tryLock() can allow a thread to stop waiting after a specified period instead of waiting indefinitely.
  • Use Higher-Level Concurrency Utilities: When suitable, classes from java.util.concurrent can reduce the need to manage low-level locking manually.
Comment