top of page
Writer's pictureThe Tech Platform

What is Deadlock in Java?

Updated: Jun 16, 2023

Deadlock is a common challenge in multithreaded programming that can occur when two or more threads are unable to proceed because each is waiting for a resource or lock that is held by another thread.


In Java, where concurrent programming is prevalent, deadlocks can pose significant issues for application performance and stability. Understanding the causes of deadlocks, how to detect them, and effective strategies to prevent or resolve them is crucial for Java developers.


In this article, we will explore what is deadlock in Java, examine common scenarios that can lead to deadlocks, discuss techniques to detect and diagnose deadlocks and provide practical strategies to mitigate and avoid deadlocks in your Java applications.


What is Deadlock in Java?

Deadlock in Java is a phenomenon that can occur in multithreading scenarios. It arises when two or more threads are unable to proceed because each is waiting for a resource or lock that is held by another thread within the same system. This creates a situation where the threads are essentially stuck in a deadlock, as none of them can progress without the other relinquishing the lock.


In Java, a deadlock typically occurs when two or more threads are competing for the same set of locks or resources. When Thread A holds a lock that Thread B needs, and at the same time, Thread B holds a lock that Thread A needs, a deadlock situation arises.

What is deadlock in java

Example:

public class DeadlockExample {
    public static void main(String[] args) {
        final String resource1 = "ratan jaiswal";
        final String resource2 = "vimal jaiswal";

        Thread t1 = new Thread() {
            public void run() {
                synchronized (resource1) {
                    System.out.println("Thread 1: locked resource 1");

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

                    synchronized (resource2) {
                        System.out.println("Thread 1: locked resource 2");
                    }
                }
            }
        };

        Thread t2 = new Thread() {
            public void run() {
                synchronized (resource2) {
                    System.out.println("Thread 2: locked resource 2");

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

                    synchronized (resource1) {
                        System.out.println("Thread 2: locked resource 1");
                    }
                }
            }
        };

        t1.start();
        t2.start();
    }
}

Output:

Thread 1: locked resource 1
Thread 2: locked resource 2

This code example demonstrates a typical scenario that can lead to a deadlock. Two threads, t1 and t2, are competing for the locks on resource1 and resource2 respectively. In this case, t1 acquires resource1 while t2 acquires resource2. However, both threads then try to acquire the lock on the resource that the other thread holds, creating a deadlock. As a result, the program hangs and the deadlock cannot be resolved.


A program that will result in deadlock?

Consider the below code which will result in a deadlock in Java:

public class DeadlockDemo {
    public void method1() {
        synchronized (String.class) {
            System.out.println("Acquired lock on String.class object");

            synchronized (Integer.class) {
                System.out.println("Acquired lock on Integer.class object");
            }
        }
    }

    public void method2() {
        synchronized (Integer.class) {
            System.out.println("Acquired lock on Integer.class object");

            synchronized (String.class) {
                System.out.println("Acquired lock on String.class object");
            }
        }
    }
}

In this example, if method1() and method2() are called by two or more threads, there is a potential for deadlock. If Thread 1 acquires the lock on the String object while executing method1(), and Thread 2 acquires the lock on the Integer object while running method2(), both threads will be waiting for each other to release the locks on Integer and String to proceed further, which will never happen.


The diagram illustrates this scenario, where Thread 1 holds a lock on Object 2 and waits for the lock on Object 1 held by Thread 2, while Thread 2 waits for the lock on Object 2 held by Thread 1. This circular wait creates a deadlock, and the Java program becomes stuck.

what is deadlock in java 2


How do you detect deadlock in Java?

To detect deadlock in Java, there are several approaches you can take, such as:


1. Code Analysis: Review the code and look for potential causes of deadlock. Check if there are nested synchronized blocks or if one synchronized method calls another synchronized method. If there are multiple threads trying to acquire locks on different objects, it increases the likelihood of a deadlock occurring if not handled carefully.


2. Thread Dump Analysis: If the application becomes deadlocked during runtime, you can obtain a thread dump to analyze the situation. In Linux, you can use the command "kill -3" to generate a thread dump. This command will print the status of all threads in an application log file. By examining the thread dump, you can identify which threads are locked on which objects.


3. Thread Dump Analysis Tools: Utilize specialized tools like fastthread.io to analyze the thread dump. These tools allow you to upload the thread dump and provide detailed analysis, making it easier to identify the threads involved in the deadlock and the objects they are locked on.


4. jConsole/VisualVM: These profiling and monitoring tools can assist in detecting deadlocks. They provide visual representations of threads and locks, allowing you to identify which threads are being locked and on which objects.


By combining these methods, you can effectively detect and troubleshoot deadlocks in Java applications.


How to avoid deadlock in Java?

To avoid deadlocks, you can follow these measures:

  1. Avoid Nested Locks: Minimize the usage of nested locks where possible, as they increase the chances of deadlock.

  2. Avoid Unnecessary Locks: Only lock the necessary resources, avoiding locks on irrelevant objects that could contribute to deadlock.

  3. Use Thread Join: Utilize the Thread.join method with a maximum waiting time to prevent threads from waiting indefinitely.

Now, let's consider the fixed version:

public class DeadlockFixed 
{     
    public void method1() 
    {         
        synchronized (Integer.class) 
        {             
            System.out.println("Acquired lock on Integer.class object");              
            
            synchronized (String.class) 
            {                 
                System.out.println("Acquired lock on String.class object");             
            }         
        }     
    }      
    
    public void method2() 
    {         
        synchronized (Integer.class) 
        {             
            System.out.println("Acquired lock on Integer.class object");              
        synchronized (String.class) 
        {                 
            System.out.println("Acquired lock on String.class object");             
            }         
        }     
    } 
} 

In the fixed version, both method1() and method2() now request locks in the same order: first Integer and then String. By ensuring consistent ordering of lock acquisition, we eliminate the circular wait condition and avoid deadlocks. Now, if Thread A acquires a lock on the Integer object, Thread B will not proceed until Thread A releases the Integer lock. Similarly, Thread A will not be blocked even if Thread B holds the String lock, as Thread B will no longer expect Thread A to release the Integer lock to proceed further.


Conclusion

Deadlock is a challenging issue that can arise in multithreaded Java applications, halting their progress and causing system instability. However, armed with a solid understanding of the causes, detection methods, and prevention strategies, developers can effectively manage deadlocks and ensure the smooth execution of their concurrent programs.

0 comments

Comments


bottom of page