死锁分析

死锁常用面试知识整理

Posted by BY 大雁小鱼 on October 18, 2018

面试的时候会问到死锁问题,下面我就编写一个死锁程序来进行分析。

要想编写一个100%死锁的程序有点难度,大多数人编写的死锁程序是使用Thread.sleep函数来实现,但大家都明白,其实由于最终的线程调度取决于OS调度器,而它如何执行调度是不确定的,所以没有100%死锁的程序,只能尽可能死锁。
下面这个例子就是一个在大多数运行时死锁的程序。

package com.iydsj.server.hole;

/**
 * 尽可能死锁的程序
 */
public class DeadLock implements Runnable{

    public static final Object AAA = new Object();
    public static final Object BBB = new Object();
    boolean flag;
    public DeadLock(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if (flag) {
            synchronized (AAA) {
                System.out.println("if   lock  AAA");
                synchronized (BBB) {
                    try {
                        Thread.sleep(1000);
                    }catch (Exception ex){
                        System.out.println("error");
                    }
                    System.out.println("if   lock  BBB");
                }
            }
        }
        else {
            synchronized (BBB) {
                System.out.println("else   lock  BBB");
                synchronized (AAA) {
                    try {
                        Thread.sleep(1000);
                    }catch (Exception ex){
                        System.out.println("error");
                    }
                    System.out.println("else   lock  AAA");
                }
            }
        }
    }

    public static void main(String[] args) {
        DeadLock A = new DeadLock(true);
        DeadLock B = new DeadLock(false);
        new Thread(A).start();
        new Thread(B).start();
    }
}

  • 死锁检查 当你怀疑出现死锁的时候,可以通过一系列的工具来检查是否出现了死锁。
    1. 首先通过命令jcmd找到你运行的程序的PID
 [root@iZ23tgthnkpZ ~]# jcmd
21009 org.apache.catalina.startup.Bootstrap start
17133 com.aliyun.tianji.cloudmonitor.Application
5935 org.apache.catalina.startup.Bootstrap start
6284 org.apache.catalina.startup.Bootstrap start
13612 sun.tools.jcmd.JCmd
13550 com/i/DeadLock
6184 org.apache.catalina.startup.Bootstrap start
6442 org.apache.catalina.startup.Bootstrap start
31880 org.apache.catalina.startup.Bootstrap start

这里我的程序是com.i.DeadLock.java,它的PID是13550。

  1. 接着通过命令jstack 查看堆栈信息
[root@iZ23tgthnkpZ ~]# jstack 13550
2018-10-18 10:45:44
Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.79-b02 mixed mode):

"Attach Listener" daemon prio=10 tid=0x00007f1278001000 nid=0x3548 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" prio=10 tid=0x00007f12b0008800 nid=0x34ef waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" prio=10 tid=0x00007f12b00b2000 nid=0x34fd waiting for monitor entry [0x00007f129fefd000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.i.DeadLock.run(DeadLock.java:35)
	- waiting to lock <0x00000007d704c498> (a java.lang.Object)
	- locked <0x00000007d704c4a8> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:745)

"Thread-0" prio=10 tid=0x00007f12b00b0000 nid=0x34fc waiting for monitor entry [0x00007f129fffe000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.i.DeadLock.run(DeadLock.java:22)
	- waiting to lock <0x00000007d704c4a8> (a java.lang.Object)
	- locked <0x00000007d704c498> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:745)

"Service Thread" daemon prio=10 tid=0x00007f12b0096800 nid=0x34fa runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" daemon prio=10 tid=0x00007f12b0094800 nid=0x34f9 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" daemon prio=10 tid=0x00007f12b0091800 nid=0x34f8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" daemon prio=10 tid=0x00007f12b008f000 nid=0x34f7 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" daemon prio=10 tid=0x00007f12b006e800 nid=0x34f6 in Object.wait() [0x00007f12ac6b3000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000007d7004858> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
	- locked <0x00000007d7004858> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
	at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" daemon prio=10 tid=0x00007f12b006c800 nid=0x34f5 in Object.wait() [0x00007f12ac7b4000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000007d7004470> (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 <0x00000007d7004470> (a java.lang.ref.Reference$Lock)

"VM Thread" prio=10 tid=0x00007f12b0068000 nid=0x34f4 runnable 

"GC task thread#0 (ParallelGC)" prio=10 tid=0x00007f12b001e000 nid=0x34f0 runnable 

"GC task thread#1 (ParallelGC)" prio=10 tid=0x00007f12b0020000 nid=0x34f1 runnable 

"GC task thread#2 (ParallelGC)" prio=10 tid=0x00007f12b0022000 nid=0x34f2 runnable 

"GC task thread#3 (ParallelGC)" prio=10 tid=0x00007f12b0023800 nid=0x34f3 runnable 

"VM Periodic Task Thread" prio=10 tid=0x00007f12b00a1800 nid=0x34fb waiting on condition 

JNI global references: 107


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00007f12840062c8 (object 0x00000007d704c498, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007f1284004ed8 (object 0x00000007d704c4a8, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
	at com.i.DeadLock.run(DeadLock.java:35)
	- waiting to lock <0x00000007d704c498> (a java.lang.Object)
	- locked <0x00000007d704c4a8> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:745)
"Thread-0":
	at com.i.DeadLock.run(DeadLock.java:22)
	- waiting to lock <0x00000007d704c4a8> (a java.lang.Object)
	- locked <0x00000007d704c498> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:745)

Found 1 deadlock.

可以看到最后一句”Found 1 deadlock”,说明程序中出现了死锁。那么究竟是哪两个线程死锁了呢?基本思路是查找锁的地址,一个线程在等待某个地址的锁,另一个获取了某个地址的锁,如下,仔细看两个地址0x00000007d704c4a8和0x00000007d704c498

"Thread-1" prio=10 tid=0x00007f12b00b2000 nid=0x34fd waiting for monitor entry [0x00007f129fefd000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.i.DeadLock.run(DeadLock.java:35)
        - waiting to lock <0x00000007d704c498> (a java.lang.Object)
        - locked <0x00000007d704c4a8> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:745)

"Thread-0" prio=10 tid=0x00007f12b00b0000 nid=0x34fc waiting for monitor entry [0x00007f129fffe000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.i.DeadLock.run(DeadLock.java:22)
        - waiting to lock <0x00000007d704c4a8> (a java.lang.Object)
        - locked <0x00000007d704c498> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:745)
  • 如何在编程中避免死锁 思路如下:
    1. 如果可能的话,尽量避免使用多把锁
    2. 如果必须使用多把锁,尽量设计好锁的获取顺序