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.

目录
相关文章
|
6月前
|
Java
Java多线程:什么是死锁(Deadlock)?
Java多线程:什么是死锁(Deadlock)?
90 0
|
运维 监控 数据可视化
Java - 死锁 Dead Lock 定位分析
Java - 死锁 Dead Lock 定位分析
117 0
|
安全 Java
java安全编码指南之:死锁dead lock
java安全编码指南之:死锁dead lock
|
4天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
13天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
3天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
3天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
11 3
|
2天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
3天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
10 1
|
3天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。