生产环境当中还没真正遇到过死锁的问题。有些疑似死锁的问题,后来经过排查也只是其它问题导致的。所以通过jstack到底怎样排查死锁问题有点疏忽了。这里作个记录。
模拟一个死锁
顺便复习一下。
死锁的产生有四个必要的条件
互斥使用,即当资源被一个线程占用时,别的线程不能使用
不可抢占,资源请求者不能强制从资源占有者手中抢夺资源,资源只能由占有者主动释放
请求和保持,当资源请求者在请求其他资源的同时保持对原因资源的占有
循环等待,多个线程存在环路的锁依赖关系而永远等待下去,例如T1占有T2的资源,T2占有T3的资源,T3占有T1的资源,这种情况可能会形成一个等待环路
对于死锁产生的四个条件只要能破坏其中一条即可让死锁消失,但是条件一是基础,不能被破坏。
模拟一个死锁。
private static String lock1 = "lock1";
private static String lock2 = "lock2";
public static void main(String[] args) {
Runnable r1 = new Runnable() {
@Override
public void run() {
synchronized (lock1){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + " 锁住了lock1");
synchronized (lock2){
System.out.println(Thread.currentThread() + " 锁住了lock2");
}
}
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
synchronized (lock2){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + " 锁住了lock2");
synchronized (lock1){
System.out.println(Thread.currentThread() + " 锁住了lock1");
}
}
}
};
ExecutorService executorService = Executors.newFixedThreadPool(5);
executorService.submit(r1);
executorService.submit(r2);
}
执行输出
Thread[pool-1-thread-1,5,main] 锁住了lock1
Thread[pool-1-thread-2,5,main] 锁住了lock2
后面一直卡住,通过idea查看堆栈信息可以看到,两个线程互相一直在等待对方释放锁。
"pool-1-thread-2" #13 prio=5 os_prio=0 tid=0x000000001ebc6000 nid=0xcf950 waiting for monitor entry [0x00000000207fe000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.nyp.test.DeadlockTest$2.run(DeadlockTest.java:49)
- waiting to lock <0x000000076b19de70> (a java.lang.String)
- locked <0x000000076b19dea8> (a java.lang.String)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"pool-1-thread-1" #12 prio=5 os_prio=0 tid=0x000000001ebc5000 nid=0xcfa20 waiting for monitor entry [0x00000000206ff000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.nyp.test.DeadlockTest$1.run(DeadlockTest.java:31)
- waiting to lock <0x000000076b19dea8> (a java.lang.String)
- locked <0x000000076b19de70> (a java.lang.String)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
通过idea我们很方便的观察到了两个线程在等待对方释放锁,而且通过观察其它的堆栈信息我们也能方便的知道,两个线程也分别锁住了对方想要申请的锁,因此造成了死锁。
但是在生产环境中,通过jstack会打印出一大堆线程的信息,而且只有有并发环境必然会上锁,堆栈信息当中必然会出现waiting for monitor``waiting on condition``locked
等信息,这并不是死锁的完全充要条件。
将代码放到生产环境。通过jstack pid
命令,可以看到会出现明确的deadlock
的信息。
Found one Java-level deadlock:
"pool-4-thread-2":
waiting to lock monitor 0x00007f0c24026408 (object 0x00000005d0e7a708, a java.lang.String),
which is held by "pool-4-thread-1"
"pool-4-thread-1":
waiting to lock monitor 0x00007f0c24025c78 (object 0x00000005d0e7a740, a java.lang.String),
which is held by "pool-4-thread-2"
Java stack information for the threads listed above:
"pool-4-thread-2":
at com.alpha.data.util.DeadlockTest$2.run(DeadlockTest.java:49)
- waiting to lock <0x00000005d0e7a708> (a java.lang.String)
- locked <0x00000005d0e7a740> (a java.lang.String)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:750)
"pool-4-thread-1":
at com.alpha.data.util.DeadlockTest$1.run(DeadlockTest.java:31)- waiting to lock <0x00000005d0e7a740> (a java.lang.String)
- locked <0x00000005d0e7a708> (a java.lang.String)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:750)
Found 1 deadlock.
也就是在生产环境中,通过jstack排查死锁问题时 ,只需要盯着deadlock
字样即可,如果有死锁会明显的提示出产生死锁的代码所在。否则,便是没有死锁。
顺便复习一下通过jstack排查cpu占用高的问题
1.通过top
命令找到cpu占用高的应用程序进程
2.通过top -H -p pid
查看该应用中占用CPU高的线程。
3.通过printf "%x\n" pid
将线程高的线程号转为十六进制。
4.通过jstack过滤该十六进制的关键信息。jstack pid | grep 十六进制 -c 10
这样就可以看到占用CPU高的代码位置。
总结:就是先查到占用高的应用和具体的线程,然后根据线程到堆积信息查找即可。
不过堆栈信息非十进制,需提前把线程号转为十六进制。