Java进阶篇--可重入锁 & 不可重入锁
deepseek:介绍Java不可重入锁与可重入锁,并比较优缺点
在Java中,锁(Lock)是多线程编程中用于控制对共享资源访问的重要机制。锁可以分为不可重入锁和可重入锁,它们在实现和特性上有显著区别。下面详细介绍这两种锁,并比较它们的优缺点。
1. 不可重入锁
不可重入锁是指同一个线程在持有锁的情况下,如果再次尝试获取该锁,会导致线程被阻塞(死锁)。换句话说,不可重入锁不允许同一个线程多次获取同一把锁。
特点:
- 如果一个线程已经持有锁,再次尝试获取锁时会被阻塞。
- 实现简单,但容易导致死锁。
- 需要开发者手动管理锁的获取和释放,避免重复获取。
示例:
public class NonReentrantLock {
private boolean isLocked = false;
public synchronized void lock() throws InterruptedException {
while (isLocked) {
wait(); // 如果锁已被持有,当前线程等待
}
isLocked = true; // 获取锁
}
public synchronized void unlock() {
isLocked = false; // 释放锁
notify(); // 唤醒等待的线程
}
}
问题:
如果一个线程在持有锁的情况下,再次调用 lock() 方法,会导致线程永久阻塞(死锁)。
2. 可重入锁
可重入锁是指同一个线程可以多次获取同一把锁,而不会导致死锁。每次获取锁后,必须相应地释放锁,锁的持有计数器会递增和递减。
特点:
- 允许同一个线程多次获取同一把锁。
- 避免了因递归调用或嵌套锁导致的死锁问题。
- 实现相对复杂,但使用更方便。
Java 中的 ReentrantLock 和 synchronized 关键字都是可重入锁的实现。
示例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final Lock lock = new ReentrantLock();
public void outer() {
lock.lock(); // 获取锁
try {
System.out.println("Outer method: Lock acquired");
inner(); // 调用内部方法
} finally {
lock.unlock(); // 释放锁
System.out.println("Outer method: Lock released");
}
}
public void inner() {
lock.lock(); // 再次获取锁(可重入)
try {
System.out.println("Inner method: Lock acquired");
} finally {
lock.unlock(); // 释放锁
System.out.println("Inner method: Lock released");
}
}
public static void main(String[] args) {
ReentrantLockExample example = new ReentrantLockExample();
example.outer();
}
}
优点:
- 避免死锁:支持递归调用和嵌套锁。
- 灵活性更高:
ReentrantLock提供了更多功能,如公平锁、超时获取锁、条件变量等。
3. 不可重入锁 vs 可重入锁
| 特性 | 不可重入锁 | 可重入锁 |
|---|---|---|
| 重入性 | 不支持,同一线程重复获取锁会死锁 | 支持,同一线程可以多次获取锁 |
| 实现复杂度 | 简单 | 复杂 |
| 死锁风险 | 高(递归调用或嵌套锁会导致死锁) | 低(支持重入,避免死锁) |
| 灵活性 | 低 | 高(如 ReentrantLock 支持超时、公平锁等) |
| 性能 | 较高(实现简单,无额外开销) | 较低(需要维护锁的持有计数器) |
| 适用场景 | 简单场景,无需嵌套锁 | 复杂场景,如递归、嵌套锁 |
4. 选择建议
-
使用不可重入锁:
- 适用于简单的同步场景,且确保不会出现递归调用或嵌套锁。
- 对性能要求较高,且不需要复杂的锁机制。
-
使用可重入锁:
- 适用于复杂的多线程场景,如递归调用、嵌套锁。
- 需要更高的灵活性和功能(如公平锁、超时机制)。
5. 总结
- 不可重入锁 实现简单,但容易导致死锁,适用于简单场景。
- 可重入锁 功能强大,支持重入,避免了死锁问题,适用于复杂场景。
- 在实际开发中,推荐使用可重入锁(如
ReentrantLock或synchronized),因为它们更安全、更灵活。