Java 示例中的死锁

Java中的死锁是两个或更多线程被永久阻塞的编程情况。Java死锁情况出现在至少两个线程和两个或更多资源的情况下。在这里,我已经写了一个简单的程序,将导致Java死锁的情况,然后我们将看到如何分析它。

Java中的死锁

Java中的死锁,Java线程deadlock让我们来看看一个简单的程序,我将在其中创建Java线程中的死锁。

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

在上面的程序中,SyncThread实现了Runnable接口,它通过使用同步块逐个获取每个对象上的锁来处理两个对象。在main方法中,我为SyncThread运行了三个线程,每个线程之间都有一个共享资源。线程的运行方式是,它能够在第一个对象上获得锁,但当它试图在第二个对象上获得锁时,它会进入等待状态,因为它已经被另一个线程锁定了。这在线程之间形成了资源的循环依赖关系,从而导致死锁。当我执行上面的程序时,这里是生成的输出,但由于Java线程中的死锁,程序永远不会终止。

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

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

如何检测Java中的死锁

要检测Java中的死锁,我们需要查看应用程序的Java线程dump,在上一篇文章中,我解释了如何使用VisualVM分析器或使用jstack实用程序生成线程转储。以下是上述程序的线程转储。

  12012-12-27 19:08:34
  2Full thread dump Java HotSpot(TM) 64-Bit Server VM (23.5-b02 mixed mode):
  3
  4"Attach Listener" daemon prio=5 tid=0x00007fb0a2814000 nid=0x4007 waiting on condition [0x0000000000000000]
  5   java.lang.Thread.State: RUNNABLE
  6
  7"DestroyJavaVM" prio=5 tid=0x00007fb0a2801000 nid=0x1703 waiting on condition [0x0000000000000000]
  8   java.lang.Thread.State: RUNNABLE
  9
 10"t3" prio=5 tid=0x00007fb0a204b000 nid=0x4d07 waiting for monitor entry [0x000000015d971000]
 11   java.lang.Thread.State: BLOCKED (on object monitor)
 12    at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41)
 13    - waiting to lock <0x000000013df2f658> (a java.lang.Object)
 14    - locked <0x000000013df2f678> (a java.lang.Object)
 15    at java.lang.Thread.run(Thread.java:722)
 16
 17"t2" prio=5 tid=0x00007fb0a1073000 nid=0x4207 waiting for monitor entry [0x000000015d209000]
 18   java.lang.Thread.State: BLOCKED (on object monitor)
 19    at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41)
 20    - waiting to lock <0x000000013df2f678> (a java.lang.Object)
 21    - locked <0x000000013df2f668> (a java.lang.Object)
 22    at java.lang.Thread.run(Thread.java:722)
 23
 24"t1" prio=5 tid=0x00007fb0a1072000 nid=0x5503 waiting for monitor entry [0x000000015d86e000]
 25   java.lang.Thread.State: BLOCKED (on object monitor)
 26    at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41)
 27    - waiting to lock <0x000000013df2f668> (a java.lang.Object)
 28    - locked <0x000000013df2f658> (a java.lang.Object)
 29    at java.lang.Thread.run(Thread.java:722)
 30
 31"Service Thread" daemon prio=5 tid=0x00007fb0a1038000 nid=0x5303 runnable [0x0000000000000000]
 32   java.lang.Thread.State: RUNNABLE
 33
 34"C2 CompilerThread1" daemon prio=5 tid=0x00007fb0a1037000 nid=0x5203 waiting on condition [0x0000000000000000]
 35   java.lang.Thread.State: RUNNABLE
 36
 37"C2 CompilerThread0" daemon prio=5 tid=0x00007fb0a1016000 nid=0x5103 waiting on condition [0x0000000000000000]
 38   java.lang.Thread.State: RUNNABLE
 39
 40"Signal Dispatcher" daemon prio=5 tid=0x00007fb0a4003000 nid=0x5003 runnable [0x0000000000000000]
 41   java.lang.Thread.State: RUNNABLE
 42
 43"Finalizer" daemon prio=5 tid=0x00007fb0a4800000 nid=0x3f03 in Object.wait() [0x000000015d0c0000]
 44   java.lang.Thread.State: WAITING (on object monitor)
 45    at java.lang.Object.wait(Native Method)
 46    - waiting on <0x000000013de75798> (a java.lang.ref.ReferenceQueue$Lock)
 47    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
 48    - locked <0x000000013de75798> (a java.lang.ref.ReferenceQueue$Lock)
 49    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
 50    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:177)
 51
 52"Reference Handler" daemon prio=5 tid=0x00007fb0a4002000 nid=0x3e03 in Object.wait() [0x000000015cfbd000]
 53   java.lang.Thread.State: WAITING (on object monitor)
 54    at java.lang.Object.wait(Native Method)
 55    - waiting on <0x000000013de75320> (a java.lang.ref.Reference$Lock)
 56    at java.lang.Object.wait(Object.java:503)
 57    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
 58    - locked <0x000000013de75320> (a java.lang.ref.Reference$Lock)
 59
 60"VM Thread" prio=5 tid=0x00007fb0a2049800 nid=0x3d03 runnable 
 61
 62"GC task thread#0 (ParallelGC)" prio=5 tid=0x00007fb0a300d800 nid=0x3503 runnable 
 63
 64"GC task thread#1 (ParallelGC)" prio=5 tid=0x00007fb0a2001800 nid=0x3603 runnable 
 65
 66"GC task thread#2 (ParallelGC)" prio=5 tid=0x00007fb0a2003800 nid=0x3703 runnable 
 67
 68"GC task thread#3 (ParallelGC)" prio=5 tid=0x00007fb0a2004000 nid=0x3803 runnable 
 69
 70"GC task thread#4 (ParallelGC)" prio=5 tid=0x00007fb0a2005000 nid=0x3903 runnable 
 71
 72"GC task thread#5 (ParallelGC)" prio=5 tid=0x00007fb0a2005800 nid=0x3a03 runnable 
 73
 74"GC task thread#6 (ParallelGC)" prio=5 tid=0x00007fb0a2006000 nid=0x3b03 runnable 
 75
 76"GC task thread#7 (ParallelGC)" prio=5 tid=0x00007fb0a2006800 nid=0x3c03 runnable 
 77
 78"VM Periodic Task Thread" prio=5 tid=0x00007fb0a1015000 nid=0x5403 waiting on condition 
 79
 80JNI global references: 114
 81
 82Found one Java-level deadlock:
 83=============================
 84"t3":
 85  waiting to lock monitor 0x00007fb0a1074b08 (object 0x000000013df2f658, a java.lang.Object),
 86  which is held by "t1"
 87"t1":
 88  waiting to lock monitor 0x00007fb0a1010f08 (object 0x000000013df2f668, a java.lang.Object),
 89  which is held by "t2"
 90"t2":
 91  waiting to lock monitor 0x00007fb0a1012360 (object 0x000000013df2f678, a java.lang.Object),
 92  which is held by "t3"
 93
 94Java stack information for the threads listed above:
 95===================================================
 96"t3":
 97    at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41)
 98    - waiting to lock <0x000000013df2f658> (a java.lang.Object)
 99    - locked <0x000000013df2f678> (a java.lang.Object)
100    at java.lang.Thread.run(Thread.java:722)
101"t1":
102    at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41)
103    - waiting to lock <0x000000013df2f668> (a java.lang.Object)
104    - locked <0x000000013df2f658> (a java.lang.Object)
105    at java.lang.Thread.run(Thread.java:722)
106"t2":
107    at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41)
108    - waiting to lock <0x000000013df2f678> (a java.lang.Object)
109    - locked <0x000000013df2f668> (a java.lang.Object)
110    at java.lang.Thread.run(Thread.java:722)
111
112Found 1 deadlock.

线程转储输出清楚地显示了死锁情况以及导致死锁情况的线程和资源。为了分析死锁,我们需要注意状态为阻塞 的线程,然后是它** 等待锁定** 的资源。每个资源都有一个唯一的ID,使用它我们可以找到哪个线程已经持有对象上的锁。例如,线程t3正在等待锁定0x000000013df2f658,但它已经被线程t1锁定。一旦我们分析了死锁情况并找出了导致死锁的线程,我们就需要进行代码更改以避免死锁情况。

如何在Java中避免死锁

这些是我们可以用来避免大多数死锁情况的一些指导原则。

  • 避免嵌套锁 :这是最常见的死锁原因,如果您已经持有其他资源,请避免锁定其他资源。如果您只使用一个对象锁,则几乎不可能出现死锁情况。例如,下面是run()方法的另一个实现,没有嵌套锁,程序成功运行,没有死锁的情况。
 1公共空运行(){
 2字符串名称=Thread.CurrentThread().getName();
 3System.out.println(名称+);
 4已同步(对象1){
 5System.out.println(名称+);
 6工作(Work);
 7}
 8System.out.println(名称+);
 9System.out.println(名称+);
10已同步(对象2){
11System.out.println(名称+);
12工作(Work);
13}
14System.out.println(名称+);
15
16System.out.println(名称+);
17}
  • 只锁定需要的资源 :您应该只在您必须处理的资源上获取锁定,例如在上面的程序中我锁定了整个对象资源,但如果我们只对它的一个字段感兴趣,那么我们应该只锁定那个特定的字段而不是完整的对象。
  • 避免无限期等待 :如果两个线程无限期地等待对方完成,则会出现死锁,使用线程join。如果您的线程必须等待另一个线程完成,则最好始终使用Join,并使其达到您希望等待线程完成的最大时间。

这就是Java线程中的死锁。

Published At
Categories with 技术
Tagged with
comments powered by Disqus