死锁分析

极客时间链接
Q: 什么情况下 Java 程序会产生死锁?如何定位、修复?

死锁

多线程环境下,多个线程之间,相互持有对方所需的锁,永久处于等待中

写一个死锁例子

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
public class DeadLockDemo extends Thread {
private Object first;
private Object second;
public DeadLockDemo(String name, Object first, Object second) {
super(name);
this.first = first;
this.second = second;
}
@Override
public void run() {
synchronized (first) {
System.out.println(getName() + " get lock " + first.toString());
synchronized (second) {
System.out.println(getName() + " get lock " + second.toString());
}
}
}
public static void main(String[] args) throws InterruptedException {
String a = "lockA";
String b = "lockB";
Thread thread1 = new DeadLockDemo("thread---a", a, b);
Thread thread2 = new DeadLockDemo("thread---b", b, a);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
//
output:
thread---b get lock lockB
thread---a get lock lockA

定位死锁

jstack 分析线程堆栈

  • 定位pid

    1
    2
    3
    ps -ef|grep DeadLockDemo
    501 34056 288 0 12:12下午 ?? 0:00.22 .../target/classes net.sudo.deadlock.DeadLockDemo
    501 34062 32674 0 12:12下午 ttys008 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn DeadLock
  • jstack命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    cd ${JAVA_HOME}/bin
    jstack 34056

    Found one Java-level deadlock:
    =============================
    "thread---b":
    waiting to lock monitor 0x00007fd7e5080608 (object 0x000000076ac7b178, a java.lang.String),
    which is held by "thread---a"
    "thread---a":
    waiting to lock monitor 0x00007fd7e60079f8 (object 0x000000076ac7b1b0, a java.lang.String),
    which is held by "thread---b"

ThreadMXBean

  • 新起一个线程定时扫描检查

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
    scheduler.scheduleAtFixedRate(() -> {
    long[] threadIds = mbean.findDeadlockedThreads();
    if (threadIds != null) {
    ThreadInfo[] threadInfos = mbean.getThreadInfo(threadIds);
    System.out.println("Detected deadlock threads:");
    for (ThreadInfo threadInfo : threadInfos) {
    System.out.println("ThreadName: " + threadInfo.getThreadName());
    System.out.println("ThreadState: " + threadInfo.getThreadState());
    }
    }
    }, 5L, 10L, TimeUnit.SECONDS);

    output:
    thread---a get lock lockA
    thread---b get lock lockB
    Detected deadlock threads:
    ThreadName: thread---b
    ThreadState: BLOCKED
    ThreadName: thread---a
    ThreadState: BLOCKED
  • ThreadMXBean扩展,打印所有线程

    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
    public class ThreadMxBeanDemo {
    public static void main(String[] args) {
    System.out.println("hellWorld");
    ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
    long[] threadIds = mbean.getAllThreadIds();
    if (threadIds != null) {
    ThreadInfo[] threadInfos = mbean.getThreadInfo(threadIds);
    for (ThreadInfo threadInfo : threadInfos) {
    System.out.println("ThreadName: " + threadInfo.getThreadName());
    System.out.println("ThreadState: " + threadInfo.getThreadState());
    }
    }
    }
    }
    output:
    hellWorld
    ThreadName: Monitor Ctrl-Break
    ThreadState: RUNNABLE
    ThreadName: Signal Dispatcher -- jvm接收系统命令的线程
    ThreadState: RUNNABLE
    ThreadName: Finalizer -- 处理Finalizer方法线程
    ThreadState: WAITING
    ThreadName: Reference Handler -- 处理引用对象的垃圾回收
    ThreadState: WAITING
    ThreadName: main -- 主线程
    ThreadState: RUNNABLE

Jconsole

命令行下输入jconsole 打开
jconsole1
jconsole2
jconsole3
jconsole4

避免死锁

  • 避免设计多个锁,让设计简单
  • 多个锁的情况要设置好获取顺序,通过多种场景的锁获取顺序图来判断是否存在循环等待情况
  • 使用超时的方法,或者使用ReentrantLock.tryLock非阻塞式方法
  • FindBugs 静态代码检查的工具来发现一些场景的死锁问题

死循环导致的死锁

死循环死锁,认为是自旋死锁的一种,会导致其他线程阻塞,cpu使用时间片最高,可以用jstack来排查