Java发生死锁的条件及示例
1. 引言
在并发编程中,死锁是一种常见的问题。当多个线程彼此持有对方需要的资源时,它们可能会陷入一种无法继续执行的状态,这就是死锁。本文将介绍Java中发生死锁的条件,并通过示例代码来说明这些条件如何导致死锁。
2. 死锁的条件
发生死锁的必要条件有四个,也称为死锁的四个必要条件:
- 互斥条件:资源一次只能被一个线程持有。
- 请求与保持条件:一个线程至少要持有一个资源,并请求其他线程持有的资源。
- 不可剥夺条件:资源只能在线程释放之后才能被其他线程获取。
- 循环等待条件:若干线程之间形成一种循环等待资源的关系。
当这四个条件同时满足时,就可能发生死锁。下面通过示例代码来说明这些条件如何导致死锁。
3. 示例代码
3.1. 互斥条件
互斥条件意味着同一时间只能有一个线程持有某个资源。下面的示例代码展示了一个简单的死锁情况:
public class DeadlockExample {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and resource 2");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 1 and resource 2");
}
}
});
thread1.start();
thread2.start();
}
}
在上面的代码中,两个线程分别持有resource1
和resource2
,并尝试获取对方持有的资源。由于互斥条件,当一个线程占用了resource1
后,另一个线程无法获取到该资源,从而发生死锁。
3.2. 请求与保持条件
请求与保持条件指的是一个线程在持有某个资源的同时,又请求其他线程持有的资源。下面的示例代码展示了一个请求与保持条件导致死锁的情况:
public class DeadlockExample {
private static class Resource {
private Resource resource;
public synchronized void setResource(Resource resource) {
this.resource = resource;
}
public synchronized void useResource() {
System.out.println("Using resource");
}
}
public static void main(String[] args) {
Resource resource1 = new Resource();
Resource resource2 = new Resource();
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
resource1.setResource(resource2);
synchronized (resource2) {
resource1.useResource();
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
resource2.setResource(resource1);
synchronized (resource1) {
resource2.useResource();
}
}
});
thread1.start();
thread2.start();
}
}
在上面的代码中,线程1先持有resource1
,然后请求resource2
。同时,线程2先持有resource2
,然后请求resource1
。由于请求与保持条件,两个线程都在持有一个资源的同时请求另一个资源,从而发生死锁。