Sunday, September 17, 2023

How to avoid deadlock in Java? Example Tutorial and Tips

How to avoid deadlock in Java? Is one of the popular Java interview question and flavor of the season for multi-threading, asked mostly at a senior level with lots of follow up questions. Even though the problem looks very basic but most of the Java developers get stuck once you start going deep. Interview questions start with, "What is a deadlock?" The answer is simple when two or more threads are waiting for each other to release the resource they need (lock) and get stuck for infinite time, the situation is called deadlock. It will only happen in the case of multitasking or multi-threading.

Btw, if you are serious about mastering Java multi-threading and concurrency then I also suggest you take a look at the Java Multithreading, Concurrency, and Performance Optimization course by Michael Pogrebinsky on Udemy. It's an advanced course to become an expert in Multithreading, concurrency, and Parallel programming in Java with a strong emphasis on high performance


How do you detect deadlock in Java?

Though this could have many answers, my version is; first I would look at the code if I see a nested synchronized block or calling one synchronized method from other, or trying to get a lock on a different object then there is a good chance of deadlock if a developer is not very careful.

Another way is to find it when you actually get dead-locked while running the application, try to take a thread dump, in Linux you can do this by the command "kill -3", this will print status of all threads in an application log file, and you can see which thread is locked on which object.

You can analyze that thread dump with using tools like fastthread.io which allows you to upload your thread dump and analyze it.

Another way is to use the jConsole/VisualVM, it will show you exactly which threads are getting locked and on which object.

If you are interested to learn about troubleshooting tools and processes to analyze your thread dump, I suggest you take a look at Analyzing Java Thread Dumps course on Pluralsight by Uriah Levy. An advanced practical course to learn more about Java thread dumps, and familiarize you with other popular advanced troubleshooting tools.



I also suggest you join the Java Concurrency in Practice Bundle by Heinz Kabutz, one of the most advanced course material to master concurrency and multi-threading in Java. It's based on the classic Java Concurrency in Practice book by Brian Goetz, which is a recommended reading for every Java developer.


Write a Java program that will result in deadlock?

Once you answer the earlier question, they may ask you to write code which will result in a deadlock in Java?

here is one of my version

/**
 * Java program to create a deadlock by imposing circular wait.
 * 
 * @author WINDOWS 8
 *
 */
public class DeadLockDemo {

    /*
     * This method request two locks, first String and then Integer
     */
    public void method1() {
        synchronized (String.class) {
            System.out.println("Aquired lock on String.class object");

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

    /*
     * This method also requests same two lock but in exactly
     * Opposite order i.e. first Integer and then String. 
     * This creates potential deadlock, if one thread holds String lock
     * and other holds Integer lock and they wait for each other, forever.
     */
    public void method2() {
        synchronized (Integer.class) {
            System.out.println("Aquired lock on Integer.class object");

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


If method1() and method2() both will be called by two or many threads, there is a good chance of deadlock because if thread 1 acquires lock on String object while executing method1() and thread 2 acquires lock on Integer object while running method2() both will be waiting for each other to release the lock on Integer and String to proceed further which will never happen.

This diagram precisely demonstrates our program, where one thread holds a lock on one object and waiting for other object locks which are owned by other thread.

How do you avoid deadlock in Java?


You can see that Thread 1 wants the lock on object 2 which is held by Thread 2, and Thread 2 wants a lock on Object 1 which is held by Thread 1. Since no thread is willing to give up, there is a deadlock, and the Java program is stuck.

The idea is that you should know the right way to use common concurrent patterns, and if you are not familiar with them then Applying Concurrency and Multi-threading to Common Java Patterns by Jose Paumard is a good starting point to learn.



How to avoid deadlock in Java?

Now the interviewer comes to the final part, one of the most important in my view; How do you fix a deadlock in code? Or How to avoid deadlock in Java?

If you have looked above code carefully, then you may have figured out that real reason for the deadlock is not multiple threads but the way they are requesting a lock, if you provide ordered access then the problem will be resolved.

Here is my fixed version, which avoids deadlock by avoiding circular wait with no preemption, one of the four conditions which need for deadlock.

public class DeadLockFixed {

    /**
     * Both method are now requesting lock in same order, 
     * first Integer and then String.
     * You could have also done reverse e.g. first String and then Integer,
     * both will solve the problem, as long as both method are requesting lock
     * in consistent order.
     */
    public void method1() {
        synchronized (Integer.class) {
            System.out.println("Aquired lock on Integer.class object");

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

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

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


Now there would not be any deadlock because both methods are accessing lock on Integer and String class literal in the same order. So, if thread A acquires a lock on Integer object, thread B will not proceed until thread A releases Integer lock, same way thread A will not be blocked even if thread B holds String lock because now thread B will not expect thread A to release Integer lock to proceed further.

That's all about how to avoid deadlock in Java. If you are serious about improving your multi-threading and concurrency skills there are here are some of my recommended online courses and books for Java developers.

Further Learning
Multithreading and Parallel Computing in Java
Java Concurrency in Practice - The Book
Java Multithreading, Concurrency, and Performance Optimization
Java Concurrency in Practice Bundle by Heinz Kabutz


Other Java Multithreading Articles you may like
  • The Java Developer RoadMap (roadmap)
  • 5 Courses to Learn Java Multithreading in-depth (courses)
  • 10 Tips to become a better Java Developer (tips)
  • Difference between CyclicBarrier and CountDownLatch in Java? (answer)
  • Top 5 Books to Master Concurrency in Java (books)
  • 10 Java Multithreading and Concurrency Best Practices (article)
  • 50+ Thread Interview Questions for Beginners (questions)
  • How to do inter-thread communication in Java using wait-notify? (answer)
  • Thread, Code, and Data - Story of Java application (article)
  • Understanding the flow of data and code in Java program (answer)
  • Is Java Concurrency in Practice still valid (answer)
  • Top 50 Multithreading and Concurrency Questions in Java (questions)
  • 10 Advanced books for Experienced Programmers (books)
  • Top 5 skills to Crack Coding interviews (article)
  • 10 Advanced Core Java Courses for Experienced Programmers (courses)

Thanks for reading this article so for. If you like the Java Multithreading tutorial, then please share it with your friends and colleagues. If you have any questions or feedback, then please drop a note.

P.S. - If you are new to Java and looking for a free online training course to learn Multithreading in Java basics then I also suggest you check out this Java Multithreading a free course on Udemy. It's completely free and all you need is a free Udemy account to join this course.

And now, over to you, what was the last question you were asked on Thread or concurrency in Java? And, how do you take the thread dump for a Java application? (Answer you can also find in this article)

42 comments:

  1. Thanks Gautam.
    Yes you are right deadlock is the most common problem arise with little bit of carelessness during multi threading programming and it can completely stop the program.

    Thanks
    Javin

    ReplyDelete
  2. Consistent lock acquisition ordering prevents deadlock indeed. But it is possible to have a deadlock even then. Not common scenario but plausible - Deadlock despite consistent lock acquisition ordering

    ReplyDelete
  3. never read such a great article on deadlock. deadlock in java or any other programming language is quite common because of multi-threading behavior and I agree its favorite interview topic. the best part of this java interview is how to fix deadlock in java, simply great.

    ReplyDelete
  4. are you trying to answer "What is deadlock in Java" or "How to fix deadlock in Java", what I think the key is to write thread-safe, deadlock free concurrent code in java.

    ReplyDelete
  5. Using this example there is a very bit chance to get deadlock! Just try this example :) You'll be running it for years to get a deadlock.

    I'd like to suggest following sample that produces deadlock with 100% guarantee.


    public class DeadLockTest
    {

    static class ThreadOne implements Runnable {

    public void run()
    {
    synchronized (Integer.class)
    {
    System.out.println(Thread.currentThread().getName() + " - Got lock on Integer.class");
    synchronized (String.class)
    {
    System.out.println(Thread.currentThread().getName() + " - Got lock on String.class");
    }
    }
    }
    }

    static class ThreadTwo implements Runnable {

    public void run()
    {
    synchronized (String.class)
    {
    System.out.println(Thread.currentThread().getName() + " - Got lock on String.class");
    synchronized (Integer.class)
    {
    System.out.println(Thread.currentThread().getName() + " - Got lock on Integer.class");
    }
    }
    }

    }
    /**
    * @param args
    */
    public static void main(String[] args)
    {
    new Thread(new ThreadOne(), "ThreadOne").start();
    new Thread(new ThreadTwo(), "ThreadTwo").start();
    }

    }

    You are free to use this sample in your article. Please, just mention my name - Arseny Kovalchuk.

    Thanks for initial info that caused my to check this.

    ReplyDelete
  6. Hi Arseny Kovalchuk, Thanks for your example but will you explain what is difference between earlier example and this for everybody's understanding , what I see run() method of your example is similar to method1, Also to get guaranteed deadlock one can use Sleep to hold one thread to hold the lock and increase deadlock chance , isn't it ?

    ReplyDelete
  7. @Javin, The main difference in my sample is that the synchronized block in the run methods are nested. And the major thing to get a deadlock is

    1. threadOne acquires the lock on monitorOne
    2. threadTwo acquires the lock on monitorTwo
    3. threadOne wants to get lock on monitorTwo, but !mportant! it should not release lock on monitorOne, until it gets the lock on monitorTwo
    4. at this time threadTwo wants to get lock on monitorOne, and it doesn't release lock on monitorTwo, until it gets lock on monitorOne.

    That's the deadlock. In your sample there is a possibility for both threads to release the lock!

    Regards, Arseny Kovalchuk.

    ReplyDelete
  8. Hi Arseny Kovalchuk, Your Explanation is quite clear and indeed in this condition deadlock will occur, but in my program also synchronized blocks are nested because until its nested probability of deadlock reduces, is it formatting or am I missing something ?

    ReplyDelete
  9. @Javin, now it's clear, sorry :) The formatting confused me. I recognized it as synchronized block in series, but not as nested. So, the sample is the same.

    Regards, Arseny.

    ReplyDelete
  10. Thanks Arseny ,this discussion make it more clear. I am using blogger and seems [code] is not working, any way I will definitely try to formatting more clear.

    ReplyDelete
  11. Hi Javin,
    I came across a scenario of deadlock in my application and after struggling for a while to detect it, I used the java.util.concurrent.locks.Lock.tryLock(time, unit) method which waits for specified time to acquire a lock and returns a boolean. I used this method for every lock in that scenario and logged the scenario when it could not acquire the lock. This helped me identify and fix all the possible scenarios which can cause dead lock at run time.
    Thought, it might be useful to someone if they are trying to detect a dead lock.

    ReplyDelete
  12. Thanks for your comment Santosh, isn't tryLock() can return even if there is no deadlock I mean it just not been able to acquire the lock within specified period. I guess taking a thread dump by using kill -3 is another way of finding out which thread is waiting for which lock and than detecting deadlock pattern. JConsole also helps you to find deadlock in Java. Anyway tryLock() can also be used as Indicator and thanks for raising this point.

    ReplyDelete
  13. You are right Javin, tryLock() false just indicates failure to acquire lock in the specified time. But if we carefully choose the time, based on the context that would only be possible in case of a deadlock (e.g. 10 or 20 minutes) than it would be unlikely that its anything other than a dead lock.

    Using kill -3 is a good way, but it requires a manual intervention at the particular time. By putting the tryLock(), we can check the logs anytime.

    Using tryLock() can also be used to write some recovery code and the system will never crash because of a dead lock. In worst case a process has to die.

    ReplyDelete
  14. On event of deadlock in java application you can do following to investigate it:

    1) By pressing Ctrl+Break on Windows machines print thread dump in console of Java application. by analyzing this thread dump you will know cause of deadlock.

    2) On Linux and Solaris machine you can take thread dump by sending SIGQUIT or kill -3 to Java application process id. From Java 6 onwards budled Jconsole utility can also be attached to hung java process and find out deadlocks in it.

    ReplyDelete
  15. hi im arun,the article was very clear for me to know about deadlocks....thanx author

    ReplyDelete
  16. Dear Arseny Kovalchuk your example fails ie( dead lock not happens) if first ThreadOne Class completely got executed please check it out

    ReplyDelete
  17. Hi,
    Its a gr8 article.. I understood how it results in deadlock but I didnt understand the explanation behind the fix.. I mean u just changed the order. Could u explain the flow please as to how deadlock will not arise ones the order s changed Threads n Java has been my weakest area hence the unclarity

    ReplyDelete
  18. Hi Pallavi,not a problem, threads in Java are always confusing. original order of acquring lock in two methods are opposite e..g in method 1 its lock 1-->lock2 while in method 2 its lock2-->lock1 which can result in deadlock because if two thread calls method 1 and method 2 thread 1 may end of lock 1 and thread 2 end of lock 2 and both will wait for other locks, i.e. deadlock.

    By having a particular order of acquiring lock e.g. lock1-->lock2 in both method we can eliminate this problem. Also remember to release locks in opposite order e.g. lock2--> lock1.

    ReplyDelete
    Replies
    1. Hey javin, I know this might be a silly question but in ur code where are you exactly releasing locks and as u mentioned that locks should be released in opposite order y should it be so, I'm a beginner so I do not know much about locks old help me out.

      Delete
  19. You can also use TIMED and POLLED locks from ReentrantLock to have a probabilistic deadlock avoidation. By using timed and polled lock acquision approach, one thread will eventually back-off, giving another thread to either acquire or release the lock. Though this is not guaranteed, it certainly helps to reduce probability of deadlock in Java program.

    ReplyDelete
  20. Imposing ordering is an example of avoiding deadlock by braking "Circular Wait" condition. As you know, in order for a deadlock to occur, four condition must met :

    1) Mutual Exclusion
    2) Hold and Wait
    3) No Preemption
    4) Circular Wait

    Though you can break any of these conditions to avoid deadlock, it's often easy to break circular wait. But, even with consistent ordering, you can not prevent deadlock, if order is not predefined i.e. if Id of next resource comes after acquiring first resource, your system still get into deadlock. This is true for all programming language, which supports multi-threading and concurrency, not just Java.

    ReplyDelete
  21. If you talk about How to detect deadlock in Java, then there is a cleaver way by using ThreadMXBean, which provides a method called findDeadLockthreads() which returns id of all those thread which are in deadlock and waiting for each other to release monitor or acquire lock. here is the code :

    ThreadMXBean threadMBean = ManagementFactory.getThreadMXBean();

    threadMBean.findDeadlockedThreads();

    ReplyDelete
  22. Just remember, how critical is to write a deadlock free concurrent application in Java, because only way to break deadlock is to restart the server. If you could create deadlock detector at runtime, which can also break deadlock as and when they happen, may be by intruppting or killing thread, without losing data invariant than, it would be just fantastic.

    ReplyDelete
  23. hi all, can you explain what is the diff between race condition & deadlock? & how can deadlock be preventive?

    ReplyDelete
  24. race condition is when two or more thread trying to use the same block of code/method... i.e racing each other to use particular resoruce and that is locked by one or other thread.

    Race condition is sympton of deadlock and go hands in hand.

    ReplyDelete
  25. Beginner in java:

    I really can't get the difference between the deadlock version example program, and fixed version example program. Please clarify.

    ReplyDelete
  26. Perhaps synchronizing the method itself is the best way to avoid deadlock situations rather than block synchronization and ordering...

    Uma

    ReplyDelete
  27. Hi Javin,

    I would like to point one mistake in this code, since I used the example above in one of my interviews and the interviewer took my case because he was very unhappy with the performance of the above code, even after reordering the locks.

    When you sue the synchronized block and obtain the lock on something like Integer.class or String.class, you are blocking the entire class object. It is generally a very bad idea to obtain locks on class objects directly. A better way if you are not using the lock interface is using the following lines:

    Object lock1 = new Object();
    Object lock2 = new Object();

    This way you are just obtaining the intrinsic locks of these objects rather than the entire Object.class object. Even when explaining I would request you to take care of bad practices like these, since sometimes beginners can pick up bad habits and continue them until their case is taken by someone :P.

    Thanks and keep the awesome articles coming.

    ReplyDelete
  28. Hello Bhavya Shah, you are absolutely correct, using String.class or Integer.class or just using class literal from any other class is NOT a good idea. In fact, I have also pointed this fact on my article on Java synchronization. I completely agree with you that beginners can pick up these as it is, hopefully this will improve quality of examples going forward. On another noted, using Object lock = new Object(); is probably one of the best use of Object class as well. Once again, thanks for your comment.

    ReplyDelete
  29. here inner class must and should start with static keyword .otherwise its not working .its give comple time error

    ReplyDelete
  30. clear & concise explanation. thanks

    ReplyDelete
  31. 'Now there would not be any deadlock because both method is accessing lock on Integer and String object in same order' What if I really need to acces the locks in the reverse order. How do I deal with that?

    ReplyDelete
  32. we can avoid deadlock using lock time out .
    can you using java code example.

    ReplyDelete
  33. I know what is dead Lock and how it forms in program but in above explanation and example what you gave is i dint get you used synchronized lock which is nested instead of nested if we use non-nested loop than we can avoid deadlocks

    ReplyDelete
  34. @Anonymous, if situation demand that you need multiple locks to access a resource then you have to. For example, how do you access something which is behind two doors? Accessing them in proper order helps to break the deadlock.

    ReplyDelete
  35. http://fastthread.io/ is a universal thread dump analyser. It parses complex thread dumps and presents with you with insightful metrics and beautiful graphs. I will recommend this tool to all my java team members.

    ReplyDelete
  36. @Anonymous, thanks for sharing such a nice tool, did you built it?

    ReplyDelete
  37. Are there still anyone who uses synchronized?

    ReplyDelete
  38. public void method1() {
    synchronized (Integer.class) {
    System.out.println("Aquired lock on Integer.class object");

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

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



    How about this scenario ?

    ReplyDelete
  39. I cudnt understand the example given, both method1 and method2 are synchronized, so if any thread aquires 1 lock on Integer Class, another one will wait until it releases the lock to proceed. So where is the deadlock occurring?

    ReplyDelete
  40. Hello Anshul, methods are not synchronized - they are using Synchronized block with explicit locks.

    ReplyDelete
  41. What crap is this? Remove this post right now. The solution on how to avoid deadlock is not to have it. DUH!!!
    Solution for not being a father is not have kids!!

    ReplyDelete