死锁的产生
我们先从简单的死锁到复杂的问题展开讨论。
首先是一个线程、一把锁,因多次加锁而导致死锁问题。由于 Java 的 synchronized 实现了可重入锁,因此这个死锁问题不存在。这意味着当一个线程拥有一把锁时,可以对该锁进行多次加锁操作,而不会发生死锁问题,前文已详细讨论,此处不再赘述。
接下来是两个线程、两把锁的情况。当两个线程都想获得对方的锁时,就会发生死锁问题。代码如下:
public static void main(String[] args) {
Object locker1 = new Object();
Object locker2 = new Object();
Thread t1 = new Thread(() -> {
synchronized (locker1) {
System.out.println("t1 线程获得了 锁 1");
synchronized (locker2) {
System.out.println("t1 线程成功获得了 锁 2");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (locker2) {
System.out.println("t2 线程获得了 锁 2");
synchronized (locker1) {
System.out.println("t2 线程成功获得了 锁 1");
}
}
});
t1.start();
t2.start();
}
代码可能看似能成功执行,但这属于小概率情况。如果发生小概率事件,即死锁状态,程序就会一直卡住。
为什么会执行成功? t1.start() 的速度较快,可能直接获得了两把锁,t2 此时都还没开始执行就结束了。
小概率事件是指什么? 理论上,当 t1 和 t2 线程同时开始执行时,t1 会获得 locker1,t2 获得 locker2。由于 t1 还需要获得 locker2,t2 还需要获得 locker1,但都被对方先拿到了,此时这两个线程就无法继续执行下去,导致两个线程一直处于阻塞状态。
如何查看两个线程阻塞状态? 在 t1 线程加个 sleep,保证 t2 线程获得了 locker2。


