ReentrantLock 如何设置公平锁¶
一、构造函数¶
public ReentrantLock() {
sync = new NonfairSync(); // 默认非公平
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
二、公平锁的 tryAcquire¶
static final class FairSync extends Sync {
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 关键:检查队列里有没有前驱
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
hasQueuedPredecessors()¶
public final boolean hasQueuedPredecessors() {
Node h = head;
Node s;
return h != null &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
意思是:队列里有人在我前面排队,我就不抢。
三、公平 vs 非公平对比¶
| 公平 | 非公平 | |
|---|---|---|
| lock() | 直接 acquire | 先 CAS 抢 |
| tryAcquire | 检查 hasQueuedPredecessors | 不检查 |
| 新线程行为 | 排队 | 直接抢 |
| 性能 | 低 | 高 |
| 饥饿 | 不会 | 可能 |
四、为什么默认非公平¶
- 线程挂起/唤醒有开销(微秒级)。
- 非公平让新线程在队列线程唤醒间隙抢锁,减少切换。
- 大多数场景吞吐优先。
五、什么时候用公平¶
- 要求严格 FIFO。
- 持有锁时间长,队列等待明显。
- 对公平性要求高于性能。
注意
公平锁不保证线程按调用顺序拿锁——如果队列里第一个线程被中断,它会退出队列,第二个线程顶上。