java线程死锁的检测方法
Java中线程状态:新建(New)、可运行(Runnable)、等待(Wating)、超时等待(Timed_Wating)、阻塞(Blocked)、Terminated(终止)。所有状态在Thread.state枚举类中。
状态转换图:
当线程中互相等待对方释放锁的时候就会都变成Blocked状态形成死锁,产生死锁的必要条件:
1. 互斥条件:一个资源每次只能被一个进程使用。
2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3. 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
例如:
测试threadA和threadB都进入无线等待,形成死锁。
代码如下:
public class ThreadTest {
private static final Object lockA = new Object();
private static final Object lockB = new Object();
public static void main(String[] args) throws Exception {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lockA) {
System.out.println("a thead run ...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
synchronized (lockB) {
System.out.println("a thead end ...");
}
}
}
});
threadA.setName("threadA");
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lockB) {
System.out.println("b thead run ...");
synchronized (lockA) {
System.out.println("b thead end ...");
}
}
}
});
threadB.setName("threadB");
threadA.start();
Thread.sleep(1000);
threadB.start();
}
}
当发生的死锁后,JDK自带了两个工具(jstack和JConsole),也可以使用VisualVm进行监控。
VisualVm
VisualVM会看到如下:
通过threadump可以看到更加详细的信息:
jstack
通过jstack展示线程堆栈信息(以Windows环境为例)
1.找到运行当前程序的JVM的进程id(测试使用的是eclipse运行,17572pid是eclipse)
2.执行jstack [pid]
这样就能清晰的查出是threadA
和threadB
线程互相等待产生死锁。推荐将线程命名,这样在线程堆栈dump中就能准确的定位是哪个线程,如果不进行命名将会由系统自动命名,不便于问题的查找。
工具是如何检测死锁的呢?
产生死锁也就意味着先生之间的等待关系出现了闭合环路,发生死锁。我们可以将等待关系想象成一个单项链表。那么也就将问题转化为判断这个链表中是否出现环路。如何检测单项链表中是否有闭合环路,请看我的另一片博客。