Java Deadlock Example – How to analyze deadlock situation

本文通过一个简单的Java死锁实例,详细解释了死锁的形成原因,并演示了如何从Java堆栈跟踪中分析死锁情况。进一步提供了避免死锁的指导原则,包括避免嵌套锁定、仅锁定所需资源、避免无限等待等策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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.

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.
        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.

 

Reference: Java Deadlock Example – How to analyze deadlock situation from our JCG partner Pankaj Kumar at the Developer Recipes blog.

转自:http://www.javacodegeeks.com/2013/01/java-deadlock-example-how-to-analyze-deadlock-situation.html

CH341A编程器是一款广泛应用的通用编程设备,尤其在电子工程和嵌入式系统开发领域中,它被用来烧录各种类型的微控制器、存储器和其他IC芯片。这款编程器的最新版本为1.3,它的一个显著特点是增加了对25Q256等32M芯片的支持。 25Q256是一种串行EEPROM(电可擦可编程只读存储器)芯片,通常用于存储程序代码、配置数据或其他非易失性信息。32M在这里指的是存储容量,即该芯片可以存储32兆位(Mbit)的数据,换算成字节数就是4MB。这种大容量的存储器在许多嵌入式系统中都有应用,例如汽车电子、工业控制、消费电子设备等。 CH341A编程器的1.3版更新,意味着它可以与更多的芯片型号兼容,特别是针对32M容量的芯片进行了优化,提高了编程效率和稳定性。26系列芯片通常指的是Microchip公司的25系列SPI(串行外围接口)EEPROM产品线,这些芯片广泛应用于各种需要小体积、低功耗和非易失性存储的应用场景。 全功能版的CH341A编程器不仅支持25Q256,还支持其他大容量芯片,这意味着它具有广泛的兼容性,能够满足不同项目的需求。这包括但不限于微控制器、EPROM、EEPROM、闪存、逻辑门电路等多种类型芯片的编程。 使用CH341A编程器进行编程操作时,首先需要将设备通过USB连接到计算机,然后安装相应的驱动程序和编程软件。在本例中,压缩包中的"CH341A_1.30"很可能是编程软件的安装程序。安装后,用户可以通过软件界面选择需要编程的芯片类型,加载待烧录的固件或数据,然后执行编程操作。编程过程中需要注意的是,确保正确设置芯片的电压、时钟频率等参数,以防止损坏芯片。 CH341A编程器1.3版是面向电子爱好者和专业工程师的一款实用工具,其强大的兼容性和易用性使其在众多编程器中脱颖而出。对于需要处理25Q256等32M芯片的项目,或者26系列芯片的编程工作,CH341A编程器是理想的选择。通过持续的软件更新和升级,它保持了与现代电子技术同步,确保用户能方便地对各种芯片进行编程和调试。
### 尚硅谷 Java 死锁(Deadlock)示例代码及解决方法 #### 什么是死锁? 当两个或更多线程互相持有对方所需的资源并等待对方释放时,就会发生死锁。这种情况下,程序会陷入停滞状态,没有任何进展。 以下是尚硅谷课程中可能涉及的一个经典死锁案例及其解决方案: --- #### 示例代码:死锁现象 ```java public class DeadLockDemo { private static final Object lockA = new Object(); private static final Object lockB = new Object(); public static void main(String[] args) { Thread thread1 = new Thread(() -> { synchronized (lockA) { // 线程1先获取lockA System.out.println("Thread 1 acquired lockA"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB) { // 尝试获取lockB System.out.println("Thread 1 trying to acquire lockB..."); } } }); Thread thread2 = new Thread(() -> { synchronized (lockB) { // 线程2先获取lockB System.out.println("Thread 2 acquired lockB"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockA) { // 尝试获取lockA System.out.println("Thread 2 trying to acquire lockA..."); } } }); thread1.start(); // 启动线程1 thread2.start(); // 启动线程2 } } ``` 上述代码展示了典型的死锁场景[^5]。`thread1` 和 `thread2` 分别尝试按照不同的顺序锁定相同的对象 (`lockA`, `lockB`),从而导致彼此无限期等待。 --- #### 解决方案一:统一加锁顺序 通过确保所有线程以相同顺序获取锁来避免死锁。 修改后的代码如下: ```java public class NoDeadLockSolutionOne { private static final Object lockA = new Object(); private static final Object lockB = new Object(); public static void main(String[] args) { Runnable task = () -> { synchronized (lockA) { // 所有线程均优先获取lockA System.out.println(Thread.currentThread().getName() + " acquired lockA"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB) { // 统一之后再获取lockB System.out.println(Thread.currentThread().getName() + " acquired lockB"); } } }; Thread thread1 = new Thread(task, "Thread 1"); Thread thread2 = new Thread(task, "Thread 2"); thread1.start(); thread2.start(); } } ``` 此版本中,无论哪个线程运行,都会遵循一致的加锁顺序(先 `lockA` 再 `lockB`),因此可以有效防止死锁的发生[^3]。 --- #### 解决方案二:设置超时机制 如果某个线程未能成功获得所需的所有锁,则放弃当前操作并重新尝试。 利用 `tryLock()` 方法代替传统的 `synchronized` 块即可实现这一目标。下面是一个基于 `ReentrantLock` 的例子: ```java import java.util.concurrent.locks.ReentrantLock; public class NoDeadLockSolutionTwo { private static final ReentrantLock lockA = new ReentrantLock(); private static final ReentrantLock lockB = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(() -> { while (!attemptToAcquireBothLocks(lockA, lockB)) {} performTaskAndReleaseLocks(lockA, lockB); }); Thread thread2 = new Thread(() -> { while (!attemptToAcquireBothLocks(lockB, lockA)) {} performTaskAndReleaseLocks(lockB, lockA); }); thread1.start(); thread2.start(); thread1.join(); thread2.join(); } private static boolean attemptToAcquireBothLocks(ReentrantLock first, ReentrantLock second) { boolean gotFirstLock = false; boolean gotSecondLock = false; try { gotFirstLock = first.tryLock(100, TimeUnit.MILLISECONDS); // 设置短时间内的尝试期限 if (gotFirstLock) { gotSecondLock = second.tryLock(100, TimeUnit.MILLISECONDS); } return gotFirstLock && gotSecondLock; // 只有两者都成功才返回true } catch (InterruptedException ie) { throw new RuntimeException(ie.getMessage(), ie); } finally { if (!(gotFirstLock && gotSecondLock)) { // 如果任一失败则全部解锁 if (gotFirstLock) { first.unlock(); } if (gotSecondLock) { second.unlock(); } } } } private static void performTaskAndReleaseLocks(ReentrantLock... locks) { try { System.out.println(Thread.currentThread().getName() + " performing critical section work."); } finally { for (ReentrantLock l : locks) { l.unlock(); // 完成工作后立即释放所有已持有的锁 } } } } ``` 这种方法允许线程在一定时间内未完成整个事务处理的情况下主动退出竞争,并稍后再试,进一步降低了死锁风险[^4]。 --- #### 总结 为了预防死锁问题,建议采取以下措施之一: - **保持固定的加锁顺序** - **引入合理的超时策略** 以上两种方式均可显著减少甚至完全消除因不当管理同步而导致的应用崩溃隐患。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值