Deadlock In Java

In Java's multithreading environment, a deadlock may occur when two threads are each waiting for an object lock held by the other. This results in a situation where neither thread can proceed because they are both waiting for the other to release the lock, leading to a deadlock scenario.

Conditions for Deadlock

  • Mutual Exclusion: At least one resource must be held in a non-shareable mode. In other words, only one process can use the resource at any given time.
  • Hold and Wait: A process holding at least one resource is waiting to acquire additional resources that are currently held by other processes.
  • No Preemption: Resources cannot be forcibly removed from a process holding them. The process must release the resource voluntarily.
  • Circular Wait: A set of processes is waiting on each other in a circular chain, where each process is holding a resource and waiting for the next process in the chain to release another resource.

It is important to emphasize that the absence of any of these conditions will prevent a deadlock from happening.

Example of Deadlock in Java

Example

Example

// Java Program to understand Deadlock 

public class Main {    

  public static void main(String[] args) {    

    final String resource1 = "foo";    

    final String resource2 = "bar";    

    // t1 tries to lock resource1 then resource2    

    Thread t1 = new Thread() {    

      public void run() {    

          synchronized (resource1) {    

           System.out.println("Thread 1: locked resource 1");   // first print statement 

           try { Thread.sleep(100);} catch (Exception e) {}    

           synchronized (resource2) {    

            System.out.println("Thread 1: locked resource 2");   // second print statement 

           }    

         }    

      }    

    };    

    // t2 tries to lock resource2 then resource1    

    Thread t2 = new Thread() {    

      public void run() {    

        synchronized (resource2) {    

          System.out.println("Thread 2: locked resource 2");    // third print statement

          try { Thread.sleep(100);} catch (Exception e) {}    

          synchronized (resource1) {    

            System.out.println("Thread 2: locked resource 1");    // fourth print statement

          }    

        }    

      }    

    };    

    t1.start();    

    t2.start();    

  }    

}

Output:

Output

Thread 1: locked resource 1

Thread 2: locked resource 2

Explanation

Two entities are present: resource1 and resource2. In the initial scenario, thread t1 secures resource1 and then goes into a sleep state. In a similar manner, the subsequent thread, t2, acquires resource2 and enters a sleep state as well. Both threads remain in this dormant state for a duration of 100 milliseconds.

Following that, the initial thread named t1 attempts to gain access to resource2, while the subsequent thread named t2 endeavors to acquire resource1. Nevertheless, t1 successfully acquires resource1, and t2 acquires resource2, leading to a deadlock scenario, as can be observed from the generated output. Consequently, the execution of the second and fourth print statements is omitted.

More Complicated Deadlocks

A deadlock can involve multiple threads beyond just two. Detecting a deadlock can pose challenges due to its complexity. For instance, consider a scenario where four threads are deadlocked:

Thread 1 locks A, waits for B

Thread 2 locks B, waits for C

Thread 3 locks C, waits for D

Thread 4 locks D, waits for A

Thread 1 is in a waiting state for thread 2, thread 2 is in a waiting state for thread 3, thread 3 is in a waiting state for thread 4, and thread 4 is in a waiting state for thread 1.

Deadlocks Detection

Follow these steps to identify deadlocks:

  1. Understand the concept of deadlocks and how they can occur in a system.
  2. Use tools like a resource allocation graph or wait-for graph to visualize potential deadlocks.
  3. Check for conditions necessary for deadlocks such as mutual exclusion, hold and wait, no preemption, and circular wait.
  4. Analyze system processes and their resource allocations to identify any potential deadlock situations.
  5. Implement deadlock prevention, avoidance, or detection strategies based on the analysis results.

Step 1: Compile the program using the javac command and then execute it using the Java command.

In this context, we are running the program mentioned above, which includes a situation of deadlock.

Proceed to the next step by launching a new command prompt instance and executing the following command: jps -l.

Executing the command jps -l will display a list of all currently running Java processes along with their respective process IDs.

We can see that the Main has the process ID 8680.

Step 3: Enter the provided command into the command line interface and execute it.

Example

jcmd <PID> Thread.print

Replace <PID> with the process identifier 8680 and then monitor the resulting output.

Output:

Output

8680:

2025-04-03 13:46:30

Full thread dump Java HotSpot(TM) 64-Bit Server VM (12.0.2+10 mixed mode, sharing):



Threads class SMR info:

_java_thread_list=0x000000ddaf977c70, length=12, elements={

0x000000ddae6d0000, 0x000000ddae6d1000, 0x000000ddae6e9800, 0x000000ddae6f0800,

0x000000ddae6f1800, 0x000000ddae6f5800, 0x000000ddae6f8800, 0x000000ddaf911000,

0x000000ddaf95b000, 0x000000ddaf976000, 0x000000ddaf976800, 0x000000dd8f70f800

}

"Reference Handler" #2 daemon prio=10 os_prio=2 cpu=0.00ms elapsed=448.77s tid=0x000000ddae6d0000 nid=0x5e68 waiting on condition  [0x000000ddaf14f000]

   java.lang.Thread.State: RUNNABLE

        at java.lang.ref.Reference.waitForReferencePendingList(java.base@12.0.2/Native Method)

        at java.lang.ref.Reference.processPendingReferences(java.base@12.0.2/Reference.java:241)

        at java.lang.ref.Reference$ReferenceHandler.run(java.base@12.0.2/Reference.java:213)



"Finalizer" #3 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=448.77s tid=0x000000ddae6d1000 nid=0x29ac in Object.wait()  [0x000000ddaf24e000]

   java.lang.Thread.State: WAITING (on object monitor)

        at java.lang.Object.wait(java.base@12.0.2/Native Method)

        - waiting on <0x000000008a90af78> (a java.lang.ref.ReferenceQueue$Lock)

        at java.lang.ref.ReferenceQueue.remove(java.base@12.0.2/ReferenceQueue.java:155)

        - locked <0x000000008a90af78> (a java.lang.ref.ReferenceQueue$Lock)

        at java.lang.ref.ReferenceQueue.remove(java.base@12.0.2/ReferenceQueue.java:176)

        at java.lang.ref.Finalizer$FinalizerThread.run(java.base@12.0.2/Finalizer.java:170)



"Signal Dispatcher" #4 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=448.77s tid=0x000000ddae6e9800 nid=0x5220 runnable  [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE



"Attach Listener" #5 daemon prio=5 os_prio=2 cpu=0.00ms elapsed=448.77s tid=0x000000ddae6f0800 nid=0x52d8 waiting on condition  [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE



"C2 CompilerThread0" #6 daemon prio=9 os_prio=2 cpu=15.63ms elapsed=448.77s tid=0x000000ddae6f1800 nid=0x1c4 waiting on condition  [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE

   No compile task



"C1 CompilerThread0" #8 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=448.77s tid=0x000000ddae6f5800 nid=0x5338 waiting on condition  [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE

   No compile task



"Sweeper thread" #9 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=448.77s tid=0x000000ddae6f8800 nid=0x5e70 runnable  [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE



"Service Thread" #10 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=448.76s tid=0x000000ddaf911000 nid=0x3040 runnable  [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE



"Common-Cleaner" #11 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=448.75s tid=0x000000ddaf95b000 nid=0x888 in Object.wait()  [0x000000ddaff2e000]

   java.lang.Thread.State: TIMED_WAITING (on object monitor)

        at java.lang.Object.wait(java.base@12.0.2/Native Method)

        - waiting on <0x000000008a9e7440> (a java.lang.ref.ReferenceQueue$Lock)

        at java.lang.ref.ReferenceQueue.remove(java.base@12.0.2/ReferenceQueue.java:155)

        - locked <0x000000008a9e7440> (a java.lang.ref.ReferenceQueue$Lock)

        at jdk.internal.ref.CleanerImpl.run(java.base@12.0.2/CleanerImpl.java:148)

        at java.lang.Thread.run(java.base@12.0.2/Thread.java:835)

        at jdk.internal.misc.InnocuousThread.run(java.base@12.0.2/InnocuousThread.java:134)



"Thread-0" #12 prio=5 os_prio=0 cpu=0.00ms elapsed=448.75s tid=0x000000ddaf976000 nid=0x2100 waiting for monitor entry  [0x000000ddb002f000]

   java.lang.Thread.State: BLOCKED (on object monitor)

        at Main$1.run(Main.java:15)

        - waiting to lock <0x000000008a800800> (a java.lang.String)

        - locked <0x000000008a800000> (a java.lang.String)



"Thread-1" #13 prio=5 os_prio=0 cpu=0.00ms elapsed=448.75s tid=0x000000ddaf976800 nid=0x593c waiting for monitor entry  [0x000000ddb012f000]

   java.lang.Thread.State: BLOCKED (on object monitor)

        at Main$2.run(Main.java:30)

        - waiting to lock <0x000000008a800000> (a java.lang.String)

        - locked <0x000000008a800800> (a java.lang.String)



"DestroyJavaVM" #14 prio=5 os_prio=0 cpu=31.25ms elapsed=448.75s tid=0x000000dd8f70f800 nid=0x5db4 waiting on condition  [0x0000000000000000]

   java.lang.Thread.State: RUNNABLE



"VM Thread" os_prio=2 cpu=0.00ms elapsed=448.78s tid=0x000000ddae6c9000 nid=0x5da4 runnable



"GC Thread#0" os_prio=2 cpu=0.00ms elapsed=448.78s tid=0x000000dd8f750000 nid=0x5b84 runnable



"G1 Main Marker" os_prio=2 cpu=0.00ms elapsed=448.78s tid=0x000000dd8f75d800 nid=0x1134 runnable



"G1 Conc#0" os_prio=2 cpu=0.00ms elapsed=448.78s tid=0x000000dd8f760000 nid=0x3624 runnable



"G1 Refine#0" os_prio=2 cpu=0.00ms elapsed=448.78s tid=0x000000dd8f7fa800 nid=0x56b8 runnable



"G1 Young RemSet Sampling" os_prio=2 cpu=0.00ms elapsed=448.78s tid=0x000000dd8f7fb800 nid=0x2464 runnable

"VM Periodic Task Thread" os_prio=2 cpu=0.00ms elapsed=448.75s tid=0x000000ddaf959800 nid=0x19bc waiting on condition



JNI global refs: 4, weak refs: 0





Found one Java-level deadlock:

=============================

"Thread-0":

  waiting to lock monitor 0x000000ddae6db280 (object 0x000000008a800800, a java.lang.String),

  which is held by "Thread-1"

"Thread-1":

  waiting to lock monitor 0x000000ddae6d9280 (object 0x000000008a800000, a java.lang.String),

  which is held by "Thread-0"



Java stack information for the threads listed above:

===================================================

"Thread-0":

        at Main$1.run(Main.java:15)

        - waiting to lock <0x000000008a800800> (a java.lang.String)

        - locked <0x000000008a800000> (a java.lang.String)

"Thread-1":

        at Main$2.run(Main.java:30)

        - waiting to lock <0x000000008a800000> (a java.lang.String)

        - locked <0x000000008a800800> (a java.lang.String)



Found 1 deadlock.

The output indicates the presence of one deadlock.

Preventing Deadlock

The resolution to a problem lies in its origins. When facing a deadlock, the primary concern is the sequence of accessing resources A and B. The key to resolving this predicament is to rearrange the order in which the code accesses the shared resources.

Example

Example

//Java Program to avoid deadlock   

public class Main {    

    public static void main(String ar[]) {    

        Main test = new Main();    

        final resource1 a = test.new resource1();    

        final resource2 b = test.new resource2();    

   // Thread - 1    

    Runnable t1 = new Runnable() {    

    public void run() {    

        synchronized (b) {    

            try {    

                /* Adding delay so that both threads can start trying to lock resources */    

                Thread.sleep(100);    

            } catch (InterruptedException e) {    

                e.printStackTrace();    

            }    

            // Thread - 1 have resource2 but need resource1 also    

            synchronized (a) {    

                System.out.println("In block 1");    

            }    

        }    

    }    

    };    

    // Thread - 2    

    Runnable t2 = new Runnable() {    

    public void run() {    

        synchronized (b) {    

            // Thread - 2 have resource2 but need resource1 also    

            synchronized (a) {    

                System.out.println("In block 2");    

            }    

        }    

    }    

    };    

        new Thread(b1).start();    // first thread 

        new Thread(b2).start();    // second thread

    }    

    // resource1    

    private class resource1 {    

        private int i = 10;    

        public int getI() {    

            return i;    

        }    

        public void setI(int i) {    

            this.i = i;    

        }    

    }    

    // resource2    

    private class resource2 {    

        private int i = 20;    

        public int getI() {    

            return i;    

        }    

        public void setI(int i) {    

            this.i = i;    

        }    

    }    

}

Output:

Output

In block 1

In block 2

Explanation

Within the program mentioned previously, there exist a pair of threads denoted as t1 and t2. Upon the creation of both threads, t1 proceeds to obtain resource b and then enters a sleep state lasting 100 milliseconds. Concurrently, t2 attempts to also acquire resource b; however, as it has already been claimed by t1, t2 is forced to wait for its availability.

Subsequently, thread t1 awakens from its sleep, locks resource a, and completes its operation, subsequently releasing resources a and b. Following this, thread t2 proceeds to acquire resource b first, then resource a, and carries out its task. It is important to highlight that both threads successfully execute their respective operations, thereby avoiding any deadlock situation.

How to avoid deadlocks in Java?

Deadlocks cannot be completely resolved. But we can avoid them by following the basic rules as given below:

  • Avoid Nested Locks : We must avoid giving locks to multiple threads; this is the main reason for a deadlock condition. It normally happens when you provide locks for various threads.
  • Avoid Unnecessary Locks : The locks should be given to the important threads. Giving locks to the unnecessary threads that cause the deadlock condition.
  • Using Thread Join : A deadlock usually happens when one thread is waiting for the other to finish. In this case, we can use join with the maximum time that a thread will take.

To read more Click here

Important Points to Remember

  • If multiple threads use synchronized blocks or methods to access shared resources, they can create a deadlock situation.
  • Improper usage of locks may lead to deadlocks.
  • Minimize the use of nested synchronized blocks. It reduces the chances of a deadlock.
  • Use timeouts with locks to avoid indefinite waiting.
  • Tools like VisualVM or jConsole can help detect deadlocks during runtime.
  • Deadlocks cannot be resolved automatically. Developers need to either avoid situations leading to deadlock or use techniques like lock hierarchy to ensure threads acquire locks in a specific order.
  • Java Deadlock MCQs

  1. What is a deadlock in Java?
  • When threads execute sequentially
  • When threads execute in parallel
  • When threads terminate abruptly
  • When threads are in a waiting state forever and are unable to proceed

Explanation: Deadlock can occur in a situation when a thread is waiting for an object lock that is acquired by another thread, and a second thread is waiting for an object lock that is acquired by the first thread. Since both threads are waiting for each other to release the lock, the condition is called a deadlock.

  1. Which of the following is not a condition for a deadlock to occur?
  • Circular Wait
  • Mutual Exclusion
  • Preemption
  • Hold and Wait

Explanation: Preemption is not a condition for a deadlock because resources cannot be forcibly removed from a process holding them. The process must release the resource voluntarily.

  1. What is the best way to prevent deadlock?
  • Use synchronized methods without restrictions
  • Avoid nested locks and acquire locks in a consistent order
  • Let the JVM handle it automatically
  • Use infinite loops to retry lock acquisition

Explanation: Deadlocks cannot be resolved automatically. Developers need to either avoid situations leading to deadlock or use techniques like lock hierarchy to ensure threads acquire locks in a specific order.

  1. What is a common approach to avoid deadlocks in Java?
  • Use timeouts for acquiring locks
  • Let threads compete for locks
  • Assign locks randomly
  • Do not use locks at all

Explanation: Use timeouts with locks to avoid indefinite waiting.

  1. Which tool in the Java Development Kit (JDK) helps to diagnose deadlocks?
  • javac
  • jstack
  • javadoc

The main tool provided by JDK for detecting deadlocks is called jstack. This tool is utilized to produce thread dumps that illustrate the status of all threads, including the ones caught in a deadlock situation, and pinpoint the specific locks they are trying to acquire.

Input Required

This code uses input(). Please provide values below: