ReentrantLock
的非公平锁实现是通过对 AQS(AbstractQueuedSynchronizer)功能的直接利用来实现的。这种锁策略允许线程在请求锁时“插队”,即使有其他线程在等待,这样可以提高系统的吞吐量。下面是 ReentrantLock
如何实现非公平锁的具体机制:
1. 非公平锁的定义
非公平锁并不严格按照线程请求的顺序来获取锁。这意味着,某个线程在锁被释放时,有可能会比那些已经在等待队列中的线程更早地获取锁。这种方式可以减少上下文切换的频率,从而提高吞吐量。
2. 实现机制
a. 继承 AQS
ReentrantLock
会继承 AQS 的子类 NonfairSync
来实现非公平锁。
b. 重写 tryAcquire
方法
在 NonfairSync
中,tryAcquire(int acquires)
方法的实现是关键:
static final class NonfairSync extends Sync {
@Override
protected boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果锁未被占用,线程可以直接获取锁
if (c == 0) {
if (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;
}
}
- 在这个实现中,
tryAcquire
方法首先检查锁的状态:- 如果锁未被占用,线程可以直接通过
compareAndSetState
尝试获取锁。 - 如果锁已经被当前线程占用,它可以增加锁计数(即支持重入)。
- 如果锁被其他线程占用,则不执行任何等待队列的检查,返回
false
,表示获取锁失败。
- 如果锁未被占用,线程可以直接通过
c. “插队”机制
由于非公平锁允许后续请求的线程直接尝试获取锁,因此它可能很容易在锁可用时“插队”成功。具体来说:
- 当一个线程释放锁时,如果此时有其他线程请求锁,可能会直接在释放时被抢夺,而不必考虑当前已经在等待中的线程队列。
3. 获取锁的方法
- 调用
lock()
方法时会调用tryAcquire(1)
来获取锁,由于NonfairSync
中的实现,使得在锁释放时,很可能会有后续请求的线程轻易地获取锁。
4. 总结
ReentrantLock
的非公平锁通过继承 AQS 的 NonfairSync
类来实现。这种锁机制允许线程请求“插队”,即在锁释放时可能会被未来请求的线程抢占,也就是说,非公平锁并不严格遵循 FIFO 原则。非公平锁在高并发场景中通常能提供更高的性能,因为它减少了线程上下文切换的频率,从而提高了系统的总体吞吐量。
如果你有其他问题或需要进一步探讨,请随时在评论区留言!