并发编程:可重入锁、不可重入锁、公平锁、非公平锁
一、可重入锁(Reentrant Lock)
概念
同一个线程在外层方法获取锁之后,在进入该锁保护的内层方法时,可以再次获得锁,不会被自己阻塞。
特点:
- 锁维护一个持有线程的引用和重入计数器
- 每
lock()一次,计数 +1,unlock()一次,计数 -1
Java 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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)
概念
一个线程再次请求自己持有的锁时会被阻塞,容易导致死锁。
示例(自定义一个不可重入锁):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();
}
}
}
风险:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 实现
1
ReentrantLock lock = new ReentrantLock(true); // 公平锁

特点
- 所有等待线程会排队
- 每个线程轮到时才能拿到锁
- 避免线程饥饿
四、非公平锁(Non-Fair Lock)
概念
线程在请求锁时,可以插队,如果锁空闲就立即获取,不管有没有其他等待线程。
🔍 Java 实现
1
ReentrantLock lock = new ReentrantLock(false); // 非公平锁,默认

特点
- 性能高,吞吐量大
- 可能出现饥饿现象
- 减少线程上下文切换
五、总结
| 分类 | 可重入锁 | 不可重入锁 |
|---|---|---|
| 定义 | 同一线程可以重复获取已持有的锁 | 同一线程重复获取锁会阻塞自己 |
| 例子 | synchronized, ReentrantLock |
自定义锁或极简实现 |
| 是否阻塞自身 | 否 | 是 |
| 死锁风险 | 小 | 大 |
| 分类 | 公平锁 | 非公平锁 |
|---|---|---|
| 加锁顺序 | 先进先出 | 插队,谁抢到谁执行 |
| 实现方式 | ReentrantLock(true) |
ReentrantLock(false) |
| 性能 | 较低,避免饥饿 | 较高,但可能造成饥饿 |
| 使用场景 | 任务调度敏感、公平性重要 | 性能优先,锁竞争激烈 |
六、建议使用方式
| 场景 | 建议使用锁类型 |
|---|---|
| 方法嵌套、自调用 | 可重入锁(默认即可) |
| 锁竞争不激烈、性能优先 | 非公平锁(默认) |
| 对延迟和公平性要求高(如限流) | 公平锁 |
| 自定义锁类 | 谨慎,需考虑可重入性 |
