极客时间链接
Q: 什么情况下 Java 程序会产生死锁?如何定位、修复?
死锁
多线程环境下,多个线程之间,相互持有对方所需的锁,永久处于等待中
写一个死锁例子
1 | public class DeadLockDemo extends Thread { |
定位死锁
jstack 分析线程堆栈
定位pid
1
2
3ps -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 DeadLockjstack命令
1
2
3
4
5
6
7
8
9
10
11cd ${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
22ScheduledExecutorService 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: BLOCKEDThreadMXBean扩展,打印所有线程
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
26public 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
避免死锁
- 避免设计多个锁,让设计简单
- 多个锁的情况要设置好获取顺序,通过多种场景的锁获取顺序图来判断是否存在循环等待情况
- 使用超时的方法,或者使用ReentrantLock.tryLock非阻塞式方法
- FindBugs 静态代码检查的工具来发现一些场景的死锁问题
死循环导致的死锁
死循环死锁,认为是自旋死锁的一种,会导致其他线程阻塞,cpu使用时间片最高,可以用jstack来排查