在Java编程中,死锁是一种常见且复杂的问题,它会导致程序无法继续执行。本文将深入分析Java死锁的案例,通过源码解析来揭示死锁的成因,并提供一些破解之道。
死锁的定义与成因
定义
死锁是指两个或多个线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法继续执行。
成因
- 互斥条件:资源不能被多个线程同时使用。
- 持有和等待条件:线程至少持有一个资源,并正在等待获取其他资源。
- 非抢占条件:线程所获得的资源在未使用完之前,不能被其他线程强行抢占。
- 循环等待条件:多个线程形成一种头尾相连的循环等待资源关系。
案例分析
以下是一个简单的Java死锁案例:
public class DeadlockDemo {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
public void run() {
synchronized (resource1) {
System.out.println("Thread 1: locked resource 1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource2) {
System.out.println("Thread 1: locked resource 2");
}
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
synchronized (resource2) {
System.out.println("Thread 2: locked resource 2");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource1) {
System.out.println("Thread 2: locked resource 1");
}
}
}
});
t1.start();
t2.start();
}
}
在这个案例中,两个线程t1和t2分别尝试获取resource1和resource2。由于线程t1先获取了resource1,而线程t2先获取了resource2,它们在尝试获取对方持有的资源时,就形成了死锁。
源码深度解析
通过分析上述案例的源码,我们可以看到死锁的形成原因:
- 资源获取顺序不一致:线程
t1先获取resource1,而线程t2先获取resource2。 - 循环等待条件:线程
t1在获取resource2时,线程t2正在等待获取resource1。
破解之道
1. 资源获取顺序统一
确保所有线程按照相同的顺序获取资源,可以避免循环等待条件。
public class DeadlockDemo {
private static Object resource1 = new Object();
private static Object resource2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
public void run() {
synchronized (resource1) {
System.out.println("Thread 1: locked resource 1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource2) {
System.out.println("Thread 1: locked resource 2");
}
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
synchronized (resource1) {
System.out.println("Thread 2: locked resource 1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource2) {
System.out.println("Thread 2: locked resource 2");
}
}
}
});
t1.start();
t2.start();
}
}
2. 使用锁顺序
在Java 5及更高版本中,可以使用ReentrantLock的lockOrder方法来指定锁的顺序。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockDemo {
private static Lock lock1 = new ReentrantLock();
private static Lock lock2 = new ReentrantLock();
public static void main(String[] args) {
lock1.lockOrder(lock2);
Thread t1 = new Thread(new Runnable() {
public void run() {
lock1.lock();
System.out.println("Thread 1: locked resource 1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock2.lock();
System.out.println("Thread 1: locked resource 2");
lock2.unlock();
lock1.unlock();
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
lock2.lock();
System.out.println("Thread 2: locked resource 2");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock1.lock();
System.out.println("Thread 2: locked resource 1");
lock1.unlock();
lock2.unlock();
}
});
t1.start();
t2.start();
}
}
3. 使用超时机制
在尝试获取锁时,可以设置超时时间,以避免死锁的发生。
import java.util.concurrent.TimeUnit;
public class DeadlockDemo {
private static Lock lock1 = new ReentrantLock();
private static Lock lock2 = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
public void run() {
boolean isLocked1 = lock1.tryLock(1, TimeUnit.SECONDS);
if (isLocked1) {
try {
System.out.println("Thread 1: locked resource 1");
boolean isLocked2 = lock2.tryLock(1, TimeUnit.SECONDS);
if (isLocked2) {
try {
System.out.println("Thread 1: locked resource 2");
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
boolean isLocked2 = lock2.tryLock(1, TimeUnit.SECONDS);
if (isLocked2) {
try {
System.out.println("Thread 2: locked resource 2");
boolean isLocked1 = lock1.tryLock(1, TimeUnit.SECONDS);
if (isLocked1) {
try {
System.out.println("Thread 2: locked resource 1");
} finally {
lock1.unlock();
}
}
} finally {
lock2.unlock();
}
}
}
});
t1.start();
t2.start();
}
}
4. 使用乐观锁
乐观锁可以减少锁的竞争,从而降低死锁的概率。
import java.util.concurrent.atomic.AtomicInteger;
public class DeadlockDemo {
private static AtomicInteger resource1 = new AtomicInteger(0);
private static AtomicInteger resource2 = new AtomicInteger(0);
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
public void run() {
while (!resource1.compareAndSet(0, 1)) {
// 等待
}
System.out.println("Thread 1: locked resource 1");
while (!resource2.compareAndSet(0, 1)) {
// 等待
}
System.out.println("Thread 1: locked resource 2");
resource2.set(0);
resource1.set(0);
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
while (!resource2.compareAndSet(0, 1)) {
// 等待
}
System.out.println("Thread 2: locked resource 2");
while (!resource1.compareAndSet(0, 1)) {
// 等待
}
System.out.println("Thread 2: locked resource 1");
resource1.set(0);
resource2.set(0);
}
});
t1.start();
t2.start();
}
}
总结
死锁是Java编程中常见且复杂的问题,了解其成因和破解之道对于编写高效、稳定的程序至关重要。本文通过案例分析、源码解析和破解之道,帮助读者更好地理解和解决Java死锁问题。
