在多线程编程中,死锁是一个常见且棘手的问题。死锁发生时,两个或多个线程相互等待对方释放锁,从而导致程序无法继续执行。为了提高程序的稳定性和可靠性,了解如何使用 Lock
机制规避死锁至关重要。
1. 理解死锁
什么是死锁?
死锁是一种状态,多个线程在执行过程中,因为争夺资源而造成一种互相等待的现象。比如,线程 A 持有锁 1,等待锁 2,而线程 B 持有锁 2,等待锁 1,这样就形成了死锁。
死锁的产生条件
死锁的产生通常需要满足以下四个条件:
- 互斥条件:至少有一个资源是以排他模式分配的。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:已获得的资源在未使用完之前,不能被其他进程强行剥夺。
- 循环等待条件:存在一种进程资源的循环等待关系。
2. 使用 Lock 规避死锁的策略
2.1 使用定时锁(tryLock)
tryLock
方法允许线程尝试获取锁,如果未能立即获取,可以选择继续执行或稍后重试。这种方式可以有效避免死锁。
示例代码
java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class DeadlockAvoidance {
private final Lock lock1 = new ReentrantLock();
private final Lock lock2 = new ReentrantLock();
public void methodA() {
while (true) {
if (lock1.tryLock()) {
try {
if (lock2.tryLock()) {
try {
System.out.println("方法 A: 成功获取了两个锁。");
break; // 成功获取锁,退出循环
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
// 如果未能获取到锁,稍作等待后重试
try {
Thread.sleep(50); // 或使用其他方式进行等待
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public void methodB() {
while (true) {
if (lock2.tryLock()) {
try {
if (lock1.tryLock()) {
try {
System.out.println("方法 B: 成功获取了两个锁。");
break; // 成功获取锁,退出循环
} finally {
lock1.unlock();
}
}
} finally {
lock2.unlock();
}
}
// 如果未能获取到锁,稍作等待后重试
try {
Thread.sleep(50); // 或使用其他方式进行等待
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
2.2 确保锁的获取顺序
确保所有线程以相同的顺序获取锁,可以有效避免死锁。通过定义一个全局的顺序来获取锁,避免不同线程以不同的顺序获取锁。
示例代码
java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class DeadlockPrevention {
private final Lock lock1 = new ReentrantLock();
private final Lock lock2 = new ReentrantLock();
public void methodA() {
lock1.lock();
try {
lock2.lock();
try {
System.out.println("方法 A: 成功获取了两个锁。");
} finally {
lock2.unlock();
}
} finally {
lock1.unlock();
}
}
public void methodB() {
lock1.lock();
try {
lock2.lock();
try {
System.out.println("方法 B: 成功获取了两个锁。");
} finally {
lock2.unlock();
}
} finally {
lock1.unlock();
}
}
}
2.3 使用单一锁
在某些情况下,可以通过使用单一锁来避免死锁。这种方法将所有需要同步的代码块使用同一个锁,从而避免了多个锁的竞争。
示例代码
java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class SingleLockExample {
private final Lock lock = new ReentrantLock();
public void methodA() {
lock.lock();
try {
System.out.println("方法 A: 成功获取了锁。");
} finally {
lock.unlock();
}
}
public void methodB() {
lock.lock();
try {
System.out.println("方法 B: 成功获取了锁。");
} finally {
lock.unlock();
}
}
}