在 Java 中,死锁(Deadlock)是指两个或多个线程被阻塞,它们在等待彼此持有的资源时陷入了无限期的等待状态,从而导致它们都无法继续执行。简单来说,死锁是多个线程因争夺资源而造成的一种僵局,每个线程都在等待其他线程释放资源,导致所有线程都无法继续执行下去。
死锁的产生条件
死锁通常发生在满足以下四个条件的情况下:
- 互斥条件:至少有一个资源是被独占的,即在一段时间内只能被一个线程使用。
- 持有并等待条件:一个线程在持有至少一个资源的同时,又申请获取其他资源,并且该资源已被其他线程持有。
- 不可抢占条件:资源不能被强制性地从持有它的线程中抢占,只能由持有它的线程显式地释放。
- 循环等待条件:存在一个线程链,每个线程都在等待下一个线程所持有的资源。
示例
下面是一个简单的死锁示例,展示了两个线程因互相持有对方所需的锁而陷入死锁状态:
public class DeadlockExample {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (lock1) {
System.out.println("Thread 1: Holding lock 1...");
try {
Thread.sleep(1000); // 延时一段时间,让线程 2 有机会获取 lock2
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (lock2) {
System.out.println("Thread 1: Holding lock 1 and lock 2...");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock2) {
System.out.println("Thread 2: Holding lock 2...");
try {
Thread.sleep(1000); // 延时一段时间,让线程 1 有机会获取 lock1
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for lock 1...");
synchronized (lock1) {
System.out.println("Thread 2: Holding lock 2 and lock 1...");
}
}
});
thread1.start();
thread2.start();
}
}
在上述示例中,thread1
和 thread2
分别尝试获取 lock1
和 lock2
,但它们的获取顺序不一致,因此可能会导致死锁。具体来说,如果 thread1
先获取了 lock1
,然后 thread2
先获取了 lock2
,那么它们互相等待对方释放所持有的资源,从而造成死锁。
避免和解决死锁
避免和解决死锁的常见策略包括:
- 避免循环等待:通过定义资源获取的顺序,使得所有线程都按照相同的顺序获取资源。
- 使用资源分配图:通过建立资源分配图,对资源的分配进行动态调整,避免出现环路。
- 设置超时:获取资源时设置超时,超时未获取到资源则放弃并释放已持有的资源,避免长时间等待。
- 使用锁的顺序:尽量按照固定的顺序获取锁,避免多个线程获取锁的顺序不一致而导致死锁。
了解和处理死锁是多线程编程中非常重要的一部分,有效地避免和解决死锁问题可以提高程序的稳定性和性能。