死锁产生的原因
- 资源竞争:多个线程同时竞争有限的资源,例如锁、文件句柄等。当线程获取资源的顺序不一致时,就可能导致死锁。
- 循环等待:线程之间形成一种环形的等待关系,每个线程都在等待下一个线程释放其所需要的资源,从而形成死锁。
避免死锁的方法
- 破坏死锁的四个必要条件
- 互斥条件:资源不能同时被多个线程访问。这个条件在大多数场景下是无法破坏的,因为资源本身就需要保证其独占性。
- 占有并等待条件:可以让线程一次性获取所有需要的资源,而不是先获取部分资源,再等待其他资源。例如:
public class DeadlockAvoidance1 {
private static final Object resource1 = new Object();
private static final Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
synchronized (resource2) {
System.out.println("Thread 1 acquired both resources");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource1) {
synchronized (resource2) {
System.out.println("Thread 2 acquired both resources");
}
}
});
thread1.start();
thread2.start();
}
}
- **不可剥夺条件**:当一个线程获取到资源后,不能被其他线程强制剥夺。在一些场景下,可以通过设置超时来破坏这个条件。例如:
import java.util.concurrent.TimeUnit;
public class DeadlockAvoidance2 {
private static final Object resource1 = new Object();
private static final Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
boolean success1 = false;
boolean success2 = false;
try {
success1 = resource1.wait(1000);
success2 = resource2.wait(1000);
if (success1 && success2) {
System.out.println("Thread 1 acquired both resources");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (success1) {
synchronized (resource1) {
resource1.notify();
}
}
if (success2) {
synchronized (resource2) {
resource2.notify();
}
}
}
});
Thread thread2 = new Thread(() -> {
boolean success1 = false;
boolean success2 = false;
try {
success1 = resource1.wait(1000);
success2 = resource2.wait(1000);
if (success1 && success2) {
System.out.println("Thread 2 acquired both resources");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (success1) {
synchronized (resource1) {
resource1.notify();
}
}
if (success2) {
synchronized (resource2) {
resource2.notify();
}
}
}
});
thread1.start();
thread2.start();
}
}
- **循环等待条件**:对资源进行排序,所有线程按照相同的顺序获取资源。例如:
public class DeadlockAvoidance3 {
private static final Object resource1 = new Object();
private static final Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
synchronized (resource2) {
System.out.println("Thread 1 acquired both resources");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource1) {
synchronized (resource2) {
System.out.println("Thread 2 acquired both resources");
}
}
});
thread1.start();
thread2.start();
}
}
- 使用
java.util.concurrent.locks.ReentrantLock
并设置超时:ReentrantLock
提供了比synchronized
更灵活的锁控制。例如:
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockAvoidance4 {
private static final ReentrantLock lock1 = new ReentrantLock();
private static final ReentrantLock lock2 = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
boolean success1 = false;
boolean success2 = false;
try {
success1 = lock1.tryLock(1, TimeUnit.SECONDS);
success2 = lock2.tryLock(1, TimeUnit.SECONDS);
if (success1 && success2) {
System.out.println("Thread 1 acquired both resources");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (success1) {
lock1.unlock();
}
if (success2) {
lock2.unlock();
}
}
});
Thread thread2 = new Thread(() -> {
boolean success1 = false;
boolean success2 = false;
try {
success1 = lock1.tryLock(1, TimeUnit.SECONDS);
success2 = lock2.tryLock(1, TimeUnit.SECONDS);
if (success1 && success2) {
System.out.println("Thread 2 acquired both resources");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (success1) {
lock1.unlock();
}
if (success2) {
lock2.unlock();
}
}
});
thread1.start();
thread2.start();
}
}