锁机制详解:公平锁与非公平锁
锁的基本概念
非公平锁:线程获取锁时不遵循先来后到的顺序,而是直接尝试获取。若获取失败,则进入等待队列;若成功,则直接持有锁。
公平锁:严格按照线程申请锁的顺序分配锁资源,所有线程必须在队列中排队等候,确保队首线程优先获得锁。
优缺点分析
公平锁
优点:
- 保障所有线程都能公平获取资源
- 避免线程在队列中无限等待(饥饿现象)
缺点:
- 吞吐量显著下降
- 除队首线程外,其余线程均处于阻塞状态
- CPU唤醒阻塞线程的开销较大
非公平锁
优点:
- 减少CPU唤醒线程的开销
- 整体吞吐效率更高
- CPU无需唤醒所有线程
缺点:
- 可能导致队列中的线程长时间无法获取锁
- 存在线程饥饿风险
ReentrantLock 实现
ReentrantLock 提供两种锁实现,通过构造方法指定类型:
// 默认非公平锁
public ReentrantLock() {sync = new NonfairSync();
}// 可选的公平锁
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}
默认情况下使用非公平锁,因其效率和吞吐量明显优于公平锁。
非公平锁实现
核心逻辑位于 NonfairSync 类:
static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;final void lock() {if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}
}
工作机制:
- 执行
lock()时首先尝试 CAS 操作 - 若 CAS 成功(state 从 0 变为 1),表示锁未被占用
- 将当前线程设置为独占线程
- 若获取失败,则进入排队流程
公平锁实现
核心逻辑位于 FairSync 类:
static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;final void lock() {acquire(1);}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;}
}
关键区别:
- 在 CAS 操作前增加了
hasQueuedPredecessors()检查 - 仅当 AQS 队列为空时才会尝试获取锁
- 严格遵循先来后到的排队顺序
