并发编程:可重入锁、不可重入锁、公平锁、非公平锁
一、可重入锁(Reentrant Lock)
✅ 概念
同一个线程在外层方法获取锁之后,在进入该锁保护的内层方法时,可以再次获得锁,不会被自己阻塞。
📌 特点:
- 锁维护一个持有线程的引用和重入计数器
- 每
lock()
一次,计数 +1,unlock()
一次,计数 -1
💻 Java 示例
public class ReentrantExample {
private final ReentrantLock lock = new ReentrantLock();
public void outer() {
lock.lock();
try {
System.out.println("Entered outer");
inner(); // 可重入:这里不会死锁
} finally {
lock.unlock();
}
}
public void inner() {
lock.lock(); // 同一线程再次获得锁
try {
System.out.println("Entered inner");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
new ReentrantExample().outer();
}
}
🧠 本质:
JDK 提供的 synchronized
和 ReentrantLock
默认就是可重入锁。
二、不可重入锁(Non-Reentrant Lock)
❌ 概念
一个线程再次请求自己持有的锁时会被阻塞,容易导致死锁。
🧪 示例(自定义一个不可重入锁):
public class NonReentrantLock {
private boolean isLocked = false;
private Thread lockedBy = null;
public synchronized void lock() throws InterruptedException {
while (isLocked) {
wait();
}
isLocked = true;
lockedBy = Thread.currentThread();
}
public synchronized void unlock() {
if (Thread.currentThread() == lockedBy) {
isLocked = false;
lockedBy = null;
notify();
}
}
}
📌 风险:
public class Test {
private final NonReentrantLock lock = new NonReentrantLock();
public void methodA() throws InterruptedException {
lock.lock();
System.out.println("methodA");
methodB(); // 死锁:同线程再次加锁会阻塞
lock.unlock();
}
public void methodB() throws InterruptedException {
lock.lock(); // 被阻塞
System.out.println("methodB");
lock.unlock();
}
}
三、公平锁(Fair Lock)
✅ 概念
线程获取锁的顺序与它们请求锁的顺序一致,先来先得。
🔍 Java 实现
ReentrantLock lock = new ReentrantLock(true); // 公平锁
📌 特点
- 所有等待线程会排队
- 每个线程轮到时才能拿到锁
- 避免线程饥饿
四、非公平锁(Non-Fair Lock)
❌ 概念
线程在请求锁时,可以插队,如果锁空闲就立即获取,不管有没有其他等待线程。
🔍 Java 实现
ReentrantLock lock = new ReentrantLock(false); // 非公平锁,默认
📌 特点
- 性能高,吞吐量大
- 可能出现饥饿现象
- 减少线程上下文切换
五、总结
分类 | 可重入锁 | 不可重入锁 |
---|---|---|
定义 | 同一线程可以重复获取已持有的锁 | 同一线程重复获取锁会阻塞自己 |
例子 | synchronized , ReentrantLock | 自定义锁或极简实现 |
是否阻塞自身 | 否 | 是 |
死锁风险 | 小 | 大 |
分类 | 公平锁 | 非公平锁 |
---|---|---|
加锁顺序 | 先进先出 | 插队,谁抢到谁执行 |
实现方式 | ReentrantLock(true) | ReentrantLock(false) |
性能 | 较低,避免饥饿 | 较高,但可能造成饥饿 |
使用场景 | 任务调度敏感、公平性重要 | 性能优先,锁竞争激烈 |
六、建议使用方式
场景 | 建议使用锁类型 |
---|---|
方法嵌套、自调用 | 可重入锁(默认即可) |
锁竞争不激烈、性能优先 | 非公平锁(默认) |
对延迟和公平性要求高(如限流) | 公平锁 |
自定义锁类 | 谨慎,需考虑可重入性 |