死锁的四大必要条件有哪些?如何解决?
死锁是因为多个线程互相等待对方释放资源,导致谁也动不了。🛑
要避免死锁,可以破坏“互斥、占有且等待、不可抢占、循环等待”这四个条件中的一个。🔑
例如:
让资源共享 ✅
一次性申请所有资源 ✅
允许资源被抢占 ✅
避免循环等待 ✅
知识内容:
🔍 死锁的四个必要条件:
互斥条件:每个资源只能被一个线程占用。
占有和等待:线程在持有至少一个资源的同时,等待获取其他资源。
不可抢占:线程所获得的资源在未使用完毕之前不能被其他线程抢占。
循环等待:多个线程形成一种头尾相接的循环等待资源关系。
🔍 避免死锁的方法:
按序申请资源:确保所有线程在获取多个锁时,按照相同的顺序获取锁。
尽量减少锁的范围:将锁的粒度尽可能缩小,减少持有锁的时间。可以通过拆分锁或使用更细粒度的锁来实现。
使用尝试锁机制:使用 ReentrantLock 的 tryLock 方法,尝试在一段时间内获取锁,如果无法获取,则可以选择放弃或采取其他措施,避免死锁。
设置超时等待时间:为锁操作设置超时,防止线程无限期地等待锁。
避免嵌套锁:尽量避免在一个锁的代码块中再次尝试获取另一个锁。
🎉 代码演示:
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(100); } catch (InterruptedException e) {}
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(100); } catch (InterruptedException e) {}
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();
}
}
🔑 通俗解释:
想象一下,两个小朋友 A 和 B 在抢玩具。A 拿着玩具车等 B 的积木,B 拿着积木等 A 的玩具车,结果谁都玩不成!这就是死锁。为了避免这种情况,我们可以让 A 和 B 按照相同的顺序拿玩具,或者让他们不要同时抢两个玩具。😅
避免死锁的代码示例
🔍 按序申请资源:
public class AvoidDeadlockExample {
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(100); } catch (InterruptedException e) {}
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 (lock1) {
System.out.println("Thread 2: Holding lock 1...");
try { Thread.sleep(100); } catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 2...");
synchronized (lock2) {
System.out.println("Thread 2: Holding lock 1 and lock 2...");
}
}
});
thread1.start();
thread2.start();
}
}
🔍 使用尝试锁机制:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TryLockExample {
private static final Lock lock1 = new ReentrantLock();
private static final Lock lock2 = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
try {
if (lock1.tryLock()) {
System.out.println("Thread 1: Holding lock 1...");
try { Thread.sleep(100); } catch (InterruptedException e) {}
if (lock2.tryLock()) {
System.out.println("Thread 1: Holding lock 1 and lock 2...");
lock2.unlock();
}
lock1.unlock();
}
} catch (Exception e) {
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
try {
if (lock2.tryLock()) {
System.out.println("Thread 2: Holding lock 2...");
try { Thread.sleep(100); } catch (InterruptedException e) {}
if (lock1.tryLock()) {
System.out.println("Thread 2: Holding lock 2 and lock 1...");
lock1.unlock();
}
lock2.unlock();
}
} catch (Exception e) {
e.printStackTrace();
}
});
thread1.start();
thread2.start();
}
}
🔑 通俗解释:
为了避免死锁,我们可以让线程按照相同的顺序获取锁,或者使用 tryLock 尝试获取锁。如果获取不到锁,线程可以选择放弃或等待一段时间再尝试,而不是一直等待。这样就能有效避免死锁的发生。😊
总结
🔒 死锁的发生需要满足四个必要条件:互斥、占有和等待、不可抢占、循环等待。
通过按序申请资源、减少锁的范围、使用尝试锁机制等方法,可以有效避免死锁。
记住,死锁就像小朋友抢玩具,只要大家按规矩来,就不会出问题!😅
