在Java编程中,死锁是一个常见但严重的问题,它会导致程序无法继续执行。本文将深入分析Java死锁的案例,并提供相应的解锁策略。
死锁的概念
首先,我们需要明确什么是死锁。死锁是指两个或多个线程在执行过程中,因争夺资源而造成的一种互相等待的现象。在这些线程中,每个线程至少持有对方需要的资源,而且都不愿意先释放自己的资源,从而导致系统处于等待状态。
死锁案例分析
案例一:两个线程分别持有不同的锁
假设我们有两个线程A和B,线程A持有锁L1,线程B持有锁L2。线程A想要获取锁L2,而线程B想要获取锁L1。由于线程A和B都持有对方需要的资源,并且都不愿意先释放自己的资源,因此这两个线程会陷入死锁状态。
public class DeadlockDemo {
public static void main(String[] args) {
Object L1 = new Object();
Object L2 = new Object();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
synchronized (L1) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (L2) {
System.out.println("Thread A acquired both locks.");
}
}
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
synchronized (L2) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (L1) {
System.out.println("Thread B acquired both locks.");
}
}
}
});
A.start();
B.start();
}
}
案例二:多个线程共享一个锁
在这个案例中,有四个线程A、B、C和D,它们都需要一个锁L。线程A和C首先获取锁L,然后线程B和D尝试获取锁L。由于线程A和C不释放锁L,线程B和D将无法继续执行,从而导致死锁。
public class DeadlockDemo {
public static void main(String[] args) {
Object L = new Object();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
synchronized (L) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread A acquired lock.");
}
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
synchronized (L) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread B acquired lock.");
}
}
});
Thread C = new Thread(new Runnable() {
@Override
public void run() {
synchronized (L) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread C acquired lock.");
}
}
});
Thread D = new Thread(new Runnable() {
@Override
public void run() {
synchronized (L) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread D acquired lock.");
}
}
});
A.start();
B.start();
C.start();
D.start();
}
}
死锁解锁策略
为了解决死锁问题,我们可以采取以下策略:
- 锁顺序策略:确保所有线程按照相同的顺序获取锁,从而避免死锁。
- 锁超时策略:为线程获取锁设置超时时间,如果超时则放弃锁,并重新尝试。
- 锁检测与恢复策略:使用
java.util.concurrent包中的Lock和ReentrantLock等锁实现,这些锁具有检测死锁的能力,并能够尝试恢复死锁。
锁顺序策略示例
public class DeadlockDemo {
public static void main(String[] args) {
Object L1 = new Object();
Object L2 = new Object();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
synchronized (L1) {
synchronized (L2) {
System.out.println("Thread A acquired both locks.");
}
}
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
synchronized (L2) {
synchronized (L1) {
System.out.println("Thread B acquired both locks.");
}
}
}
});
A.start();
B.start();
}
}
锁超时策略示例
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockDemo {
public static void main(String[] args) {
Lock lock1 = new ReentrantLock();
Lock lock2 = new ReentrantLock();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
boolean isLocked1 = false;
boolean isLocked2 = false;
try {
isLocked1 = lock1.tryLock(100, TimeUnit.MILLISECONDS);
isLocked2 = lock2.tryLock(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (isLocked1) lock1.unlock();
if (isLocked2) lock2.unlock();
}
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
boolean isLocked1 = false;
boolean isLocked2 = false;
try {
isLocked1 = lock1.tryLock(100, TimeUnit.MILLISECONDS);
isLocked2 = lock2.tryLock(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (isLocked1) lock1.unlock();
if (isLocked2) lock2.unlock();
}
}
});
A.start();
B.start();
}
}
锁检测与恢复策略示例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockDemo {
public static void main(String[] args) {
Lock lock1 = new ReentrantLock();
Lock lock2 = new ReentrantLock();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
lock1.lock();
try {
System.out.println("Thread A acquired lock1.");
Thread.sleep(100);
lock2.lock();
try {
System.out.println("Thread A acquired lock2.");
} finally {
lock2.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock1.unlock();
}
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
lock2.lock();
try {
System.out.println("Thread B acquired lock2.");
Thread.sleep(100);
lock1.lock();
try {
System.out.println("Thread B acquired lock1.");
} finally {
lock1.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock2.unlock();
}
}
});
A.start();
B.start();
}
}
总结
死锁是Java编程中常见但严重的问题。本文通过案例分析,详细介绍了死锁的概念、常见的死锁案例,并提供了相应的解锁策略。在实际编程中,我们需要注意锁的使用,避免死锁的发生。
