Java死锁示例–如何分析死锁情况

死锁是两个或多个线程永远被阻塞的编程情况,这种情况发生在至少两个线程和两个或更多资源的情况下。 在这里,我编写了一个简单的程序,该程序将导致死锁情况,然后我们将看到如何对其进行分析。

Java死锁示例

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();
        }
    }
}

在上面的程序中,SyncThread实现了Runnable接口,并且通过使用同步块逐个获取每个对象上的锁,从而对两个对象起作用。

在main方法中,我为SyncThread运行了三个线程,并且每个线程之间都有一个共享资源。 线程以这样的方式运行:它将能够获取第一个对象的锁定,但是当它试图获取第二个对象的锁定时,它将进入等待状态,因为它已经被另一个线程锁定了。 这对导致死锁的线程之间的资源形成了循环依赖性。

当我执行上面的程序时,这是生成的输出,但是由于死锁,程序永远不会终止。

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

在这里,我们可以从输出中清楚地确定死锁情况,但是在实际应用中,很难找到死锁情况并对其进行调试。

分析死锁

要分析死锁,我们需要查看应用程序的Java线程转储 ,在上一篇文章中,我解释了如何使用VisualVM Profiler或jstack实用程序生成线程转储

这是上面程序的线程转储。

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.

线程转储输出清楚地显示了死锁情况,并且所涉及的线程和资源导致了死锁情况。

为了分析死锁,我们需要找出状态为BLOCKED的线程,然后寻找它正在等待锁定的资源,每个资源都有一个唯一的ID,通过它我们可以找到哪个线程已经在对象上保持了锁定。 例如,线程“ t3”正在等待锁定0x000000013df2f658,但它已被线程“ t1”锁定。

一旦我们分析了死锁情况并找出了导致死锁的线程,就需要进行代码更改以避免死锁情况。

避免死锁

这些是我们可以避免大多数僵局情况的一些准则。

  • 避免嵌套锁 :这是造成死锁的最常见原因,如果已经持有另一个资源,请避免锁定另一个资源。 如果仅使用一个对象锁,则几乎不可能出现死锁情况。 例如,这是不带嵌套锁的run()方法的另一种实现,并且程序成功运行且没有死锁情况。
    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.');
        }
  • 仅锁定所需条件 :您应该仅对必须处理的资源获得锁定,例如,在上述程序中,我正在锁定完整的Object资源,但是如果我们仅对其中一个字段感兴趣,则应仅锁定该对象具体字段不完整的对象。
  • 避免无限期地等待 :如果两个线程正在使用线程连接无限期地等待彼此完成,则可能会陷入死锁。 如果您的线程必须等待另一个线程完成,则始终最好使用join,并在等待线程完成的最长时间内使用它。

参考: Java死锁示例–如何开发人员食谱博客的JCG合作伙伴 Pankaj Kumar中分析死锁情况

翻译自: https://www.javacodegeeks.com/2013/01/java-deadlock-example-how-to-analyze-deadlock-situation.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值