先看一个死锁的代码:
public class ClassB {
private static ClassB instance;
public static ClassB getInstance() {
if (instance == null) {
instance = new ClassB();
}
return instance;
}
public synchronized void b1() {
...
}
public synchronized void b2() {
Thread.sleep(2000);
ClassA.getInstance.a2();
}
}
public class ClassA {
private static ClassA instance;
public static ClassA getInstance() {
if (instance == null) {
instance = new ClassA();
}
return instance;
}
public synchronized void a1() {
ClassB.getInstance.b1();
}
public synchronized void a2() {
...
}
}
Thread thread1 = new Thread() {
@Override
public void run() {
ClassA.getInstance.a1();
}
};
Thread thread2 = new Thread() {
@Override
public void run() {
ClassB.getInstance.b2();
}
};
//Main 方法中:
thread2.start();
thread1.start();
先启动 thread2,thread2 中调用ClassB 的 b2,b2 中先睡了两秒,然后调用a2;
再启动 thread1,thread1 中调用ClassA 的 a1,a1 中调用 b1;
a1 调用 b1 时,因为 b2 在 sleep,没有释放锁,所以a1 等待 b2 释放锁。
b2 sleep 时间过后,调用 a2,而 a1 这时还在执行,没有释放锁,所以 b2 也在等待 a1 释放锁。
所以就出现了 ClassA 等待 ClassB 释放锁,而 ClassB 也在等待 ClassA 释放锁 的无限等待的情况。
这种情况便称为 死锁。
用一副图来标识上面的调用关系:

上面的代码是 两个类 互相调用:A->B->A->B...,产生死锁。
而实际中的死锁有可能更隐蔽,有可能是 A->B->C->A->B... 的情况,形成闭环,有可能闭环中的类更多。
死锁的根本原因是出现了嵌套锁,要避免嵌套锁的发生,下面给出一种解决方式:
public void b2() {
synchronized(this) {
Thread.sleep(2000);
}
ClassA.getInstance.a2();
}
synchronized 不再修饰方法,而是修饰一个代码块,调用 a2 不放在代码块中。
这样便断开了锁链,不能形成死锁了。
下面给出一个 trace 文件中的死锁的日志:

重点看 第8行 和 第23行,thread1 等待 thread14 ,thread14 在等待 thread1,形成死锁。
网友评论