Java Deadlock Example and How to analyze deadlock situation

简介: source:http://www.journaldev.com/1058/java-deadlock-example-and-how-to-analyze-deadlock-situation   Deadlock is a programming situation where two or...

source:http://www.journaldev.com/1058/java-deadlock-example-and-how-to-analyze-deadlock-situation

 

Deadlock is a programming situation where two or more threads are blocked forever, this situation arises with at least two threads and two or more resources. Here I have written a simple program that will cause deadlock scenario and then we will see how to analyze it.

Java Deadlock Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.journaldev.threads;
 
public class ThreadDeadlock {
 
     public static void main(String[] args) throws InterruptedException {
         Object obj1 = new Object();
         Object obj2 = new Object();
         Object obj3 = new Object();
     
         Thread t1 = new Thread( new SyncThread(obj1, obj2), "t1" );
         Thread t2 = new Thread( new SyncThread(obj2, obj3), "t2" );
         Thread t3 = new Thread( new SyncThread(obj3, obj1), "t3" );
         
         t1.start();
         Thread.sleep( 5000 );
         t2.start();
         Thread.sleep( 5000 );
         t3.start();
         
     }
 
}
 
class SyncThread implements Runnable{
     private Object obj1;
     private Object obj2;
 
     public SyncThread(Object o1, Object o2){
         this .obj1=o1;
         this .obj2=o2;
     }
     @Override
     public void run() {
         String name = Thread.currentThread().getName();
         System.out.println(name + " acquiring lock on " +obj1);
         synchronized (obj1) {
          System.out.println(name + " acquired lock on " +obj1);
          work();
          System.out.println(name + " acquiring lock on " +obj2);
          synchronized (obj2) {
             System.out.println(name + " acquired lock on " +obj2);
             work();
         }
          System.out.println(name + " released lock on " +obj2);
         }
         System.out.println(name + " released lock on " +obj1);
         System.out.println(name + " finished execution." );
     }
     private void work() {
         try {
             Thread.sleep( 30000 );
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }
}

In above program SyncThread is implementing Runnable interface and it works on two Objects by acquiring lock on each one of them one by one using synchronized block.

In main method, I have three threads running for SyncThread and there is a shared resource between each of the threads. The threads are run in such a way that it will be able to acquire lock on the first object but when it’s trying to acquire lock on second object, it goes on wait state because it’s already locked by another thread. This forms a cyclic dependency for resource between Threads causing deadlock.

When I execute the above program, here is the output generated but program never terminates because of deadlock.

 

1
2
3
4
5
6
7
8
9
t1 acquiring lock on java.lang.Object@6d9dd520
t1 acquired lock on java.lang.Object@6d9dd520
t2 acquiring lock on java.lang.Object@22aed3a5
t2 acquired lock on java.lang.Object@22aed3a5
t3 acquiring lock on java.lang.Object@218c2661
t3 acquired lock on java.lang.Object@218c2661
t1 acquiring lock on java.lang.Object@22aed3a5
t2 acquiring lock on java.lang.Object@218c2661
t3 acquiring lock on java.lang.Object@6d9dd520

Here we can clearly identify the deadlock situation from the output but in real life applications it’s very hard to find the deadlock situation and debug them.

Analyze Deadlock

To analyze a deadlock, we need to look at the java thread dump of the application, in last post I explained how we can generate thread dump using VisualVM profiler or using jstack utility.

Here is the thread dump of above program.

2012-12-27 19:08:34
Full thread dump Java HotSpot(TM) 64-Bit Server VM (23.5-b02 mixed mode):
 
"Attach Listener" daemon prio=5 tid=0x00007fb0a2814000 nid=0x4007 waiting on condition [0x0000000000000000]
    java.lang.Thread.State: RUNNABLE
 
"DestroyJavaVM" prio=5 tid=0x00007fb0a2801000 nid=0x1703 waiting on condition [0x0000000000000000]
    java.lang.Thread.State: RUNNABLE
 
"t3" prio=5 tid=0x00007fb0a204b000 nid=0x4d07 waiting for monitor entry [0x000000015d971000]
    java.lang.Thread.State: BLOCKED (on object monitor)
     at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41)
     - waiting to lock <0x000000013df2f658> (a java.lang.Object)
     - locked <0x000000013df2f678> (a java.lang.Object)
     at java.lang.Thread.run(Thread.java:722)
 
"t2" prio=5 tid=0x00007fb0a1073000 nid=0x4207 waiting for monitor entry [0x000000015d209000]
    java.lang.Thread.State: BLOCKED (on object monitor)
     at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41)
     - waiting to lock <0x000000013df2f678> (a java.lang.Object)
     - locked <0x000000013df2f668> (a java.lang.Object)
     at java.lang.Thread.run(Thread.java:722)
 
"t1" prio=5 tid=0x00007fb0a1072000 nid=0x5503 waiting for monitor entry [0x000000015d86e000]
    java.lang.Thread.State: BLOCKED (on object monitor)
     at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41)
     - waiting to lock <0x000000013df2f668> (a java.lang.Object)
     - locked <0x000000013df2f658> (a java.lang.Object)
     at java.lang.Thread.run(Thread.java:722)
 
"Service Thread" daemon prio=5 tid=0x00007fb0a1038000 nid=0x5303 runnable [0x0000000000000000]
    java.lang.Thread.State: RUNNABLE
 
"C2 CompilerThread1" daemon prio=5 tid=0x00007fb0a1037000 nid=0x5203 waiting on condition [0x0000000000000000]
    java.lang.Thread.State: RUNNABLE
 
"C2 CompilerThread0" daemon prio=5 tid=0x00007fb0a1016000 nid=0x5103 waiting on condition [0x0000000000000000]
    java.lang.Thread.State: RUNNABLE
 
"Signal Dispatcher" daemon prio=5 tid=0x00007fb0a4003000 nid=0x5003 runnable [0x0000000000000000]
    java.lang.Thread.State: RUNNABLE
 
"Finalizer" daemon prio=5 tid=0x00007fb0a4800000 nid=0x3f03 in Object.wait() [0x000000015d0c0000]
    java.lang.Thread.State: WAITING (on object monitor)
     at java.lang.Object.wait(Native Method)
     - waiting on <0x000000013de75798> (a java.lang.ref.ReferenceQueue$Lock)
     at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
     - locked <0x000000013de75798> (a java.lang.ref.ReferenceQueue$Lock)
     at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
     at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:177)
 
"Reference Handler" daemon prio=5 tid=0x00007fb0a4002000 nid=0x3e03 in Object.wait() [0x000000015cfbd000]
    java.lang.Thread.State: WAITING (on object monitor)
     at java.lang.Object.wait(Native Method)
     - waiting on <0x000000013de75320> (a java.lang.ref.Reference$Lock)
     at java.lang.Object.wait(Object.java:503)
     at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
     - locked <0x000000013de75320> (a java.lang.ref.Reference$Lock)
 
"VM Thread" prio=5 tid=0x00007fb0a2049800 nid=0x3d03 runnable
 
"GC task thread#0 (ParallelGC)" prio=5 tid=0x00007fb0a300d800 nid=0x3503 runnable
 
"GC task thread#1 (ParallelGC)" prio=5 tid=0x00007fb0a2001800 nid=0x3603 runnable
 
"GC task thread#2 (ParallelGC)" prio=5 tid=0x00007fb0a2003800 nid=0x3703 runnable
 
"GC task thread#3 (ParallelGC)" prio=5 tid=0x00007fb0a2004000 nid=0x3803 runnable
 
"GC task thread#4 (ParallelGC)" prio=5 tid=0x00007fb0a2005000 nid=0x3903 runnable
 
"GC task thread#5 (ParallelGC)" prio=5 tid=0x00007fb0a2005800 nid=0x3a03 runnable
 
"GC task thread#6 (ParallelGC)" prio=5 tid=0x00007fb0a2006000 nid=0x3b03 runnable
 
"GC task thread#7 (ParallelGC)" prio=5 tid=0x00007fb0a2006800 nid=0x3c03 runnable
 
"VM Periodic Task Thread" prio=5 tid=0x00007fb0a1015000 nid=0x5403 waiting on condition
 
JNI global references: 114
 
 
Found one Java-level deadlock:
=============================
"t3" :
   waiting to lock monitor 0x00007fb0a1074b08 (object 0x000000013df2f658, a java.lang.Object),
   which is held by "t1"
"t1" :
   waiting to lock monitor 0x00007fb0a1010f08 (object 0x000000013df2f668, a java.lang.Object),
   which is held by "t2"
"t2" :
   waiting to lock monitor 0x00007fb0a1012360 (object 0x000000013df2f678, a java.lang.Object),
   which is held by "t3"
 
Java stack information for the threads listed above:
===================================================
"t3" :
     at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41)
     - waiting to lock <0x000000013df2f658> (a java.lang.Object)
     - locked <0x000000013df2f678> (a java.lang.Object)
     at java.lang.Thread.run(Thread.java:722)
"t1" :
     at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41)
     - waiting to lock <0x000000013df2f668> (a java.lang.Object)
     - locked <0x000000013df2f658> (a java.lang.Object)
     at java.lang.Thread.run(Thread.java:722)
"t2" :
     at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41)
     - waiting to lock <0x000000013df2f678> (a java.lang.Object)
     - locked <0x000000013df2f668> (a java.lang.Object)
     at java.lang.Thread.run(Thread.java:722)
 
Found 1 deadlock.

The thread dump output clearly shows the deadlock situation and threads and resources involved causing deadlock situation.

For analyzing deadlock, we need to look out for the threads with state as BLOCKED and then the resources it’s waiting to lock, every resource has a unique ID using which we can find which thread is already holding the lock on the object. For example Thread “t3” is waiting to lock 0x000000013df2f658 but it’s already locked by thread “t1”.

Once we analyze the deadlock situation and found out the threads which are causing deadlock, we need to make code changes to avoid deadlock situation.

Avoid deadlock

These are some of the guidelines using which we can avoid most of the deadlock situations.

  • Avoid Nested Locks: This is the most common reason for deadlocks, avoid locking another resource if you already hold one. It’s almost impossible to get deadlock situation if you are working with only one object lock. For example, here is the another implementation of run() method without nested lock and program runs successfully without deadlock situation.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public void run() {
         String name = Thread.currentThread().getName();
         System.out.println(name + " acquiring lock on " + obj1);
         synchronized (obj1) {
             System.out.println(name + " acquired lock on " + obj1);
             work();
         }
         System.out.println(name + " released lock on " + obj1);
         System.out.println(name + " acquiring lock on " + obj2);
         synchronized (obj2) {
             System.out.println(name + " acquired lock on " + obj2);
             work();
         }
         System.out.println(name + " released lock on " + obj2);
     
         System.out.println(name + " finished execution." );
    }
  • Lock Only What is Required: You should acquire lock only on the resources you have to work on, for example in above program I am locking the complete Object resource but if we are only interested in one of it’s fields, then we should lock only that specific field not complete object.
  • Avoid waiting indefinitely: You can get deadlock if two threads are waiting for each other to finish indefinitely using thread join. If your thread has to wait for another thread to finish, it’s always best to use join with maximum time you want to wait for thread to finish.

If you liked this tutorial, make sure to check out other java thread tutorial.

目录
相关文章
|
7月前
|
Java
Java多线程:什么是死锁(Deadlock)?
Java多线程:什么是死锁(Deadlock)?
96 0
|
运维 监控 数据可视化
Java - 死锁 Dead Lock 定位分析
Java - 死锁 Dead Lock 定位分析
123 0
|
安全 Java
java安全编码指南之:死锁dead lock
java安全编码指南之:死锁dead lock
|
3天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
30 6
|
18天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
16天前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
18天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
12天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
12天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
35 3
|
13天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####