真实案例:使用Java Thread Dump分析ReadWriteLock导致的死锁问题
本文的死锁
本文的死锁是由jackson-databind造成的,版本是2.4.1。
这里的死锁是这样的:一组线程中的某一个线程获得写锁之后无限循环,导致其他的试图获取读锁的线程无限等待,从而导致此组线程的工作无法推进。这有区别于常规的死锁定义。
表象
最近经常收到某应用(tomcat部署)无法响应用户请求的报警。在线上使用curl
向问题实例发起请求,没法相应,然后使用ps
发现CPU飙高。
获取Thread Dump
使用kill -3 <pid>
。因为我们使用的是tomcat应用容器,tomcat会把Thread Dump打印到其安装目录下的logs/catalina.out文件里。
分析
- 首先我们需要确认有多少线程处于WAITING状态,以及在执行什么:
cat case/catalina.out|grep 'java.lang.Thread.State: WAITING'|wc -l
在我们的案例中,有378个线程处于WAITING状态,然后通过审查发现,有大量的tomcat nio线程在等待ReadWriteLock的读锁,如下:
"http-nio-9086-exec-40" daemon prio=10 tid=0x00007f8aa402a800 nid=0x7965 waiting on condition [0x00007f8a5ab68000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000d3643028> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireShared(AbstractQueuedSyn