你真的了解AQS吗?
在Java并发编程的世界里,AQS(AbstractQueuedSynchronizer)就像一位"幕后英雄",默默支撑着ReentrantLock、CountDownLatch等众多并发工具的运作。但你有没有想过:AQS本身到底是公平锁还是非公平锁?
答案可能会让你惊讶------AQS两者都不是!它更像是一个"万能工具箱",提供了构建锁和同步器的基础零件,而具体是组装成公平锁还是非公平锁,则完全取决于使用者(也就是基于AQS的子类)的选择。
AQS的"中立"智慧:把选择权交给你
AQS的设计哲学非常精妙:它只负责最基础的工作(比如维护线程等待队列、管理同步状态),而把关键的决策逻辑(比如谁能获得锁、什么时候获得锁)通过"钩子方法"留给子类来实现。
想象一下,AQS就像一家提供场地和基础设备的咖啡馆,而子类(如ReentrantLock)则是具体的经营者------可以选择做成安静的书店咖啡吧(公平锁),也可以做成热闹的快节奏咖啡店(非公平锁),AQS并不干涉具体的经营策略。
关键的"钩子"在哪里?
AQS通过以下几个核心方法将公平性的决定权交给子类:
- tryAcquire:尝试获取锁的逻辑
- tryRelease:尝试释放锁的逻辑 正是这些方法的实现,决定了最终是公平还是非公平的行为。
实例:ReentrantLock如何选择公平性
ReentrantLock是AQS最典型的应用,它通过构造函数的fair参数,轻松实现了两种锁模式。
非公平锁:"先到先得?我先试试插队!"
ReentrantLock默认是非公平锁,就像排队买奶茶时总有人想先问问"还需要等多久"------新线程会先"插队"尝试获取锁,成功了就直接拿走,失败了才乖乖去排队。
java
// 非公平锁的获取逻辑
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 不管队列有没有人,先CAS抢一波再说
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// ...
return false;
}
公平锁:"排队是基本礼仪"
当fair参数为true时,ReentrantLock就变成了公平锁------新线程会先看看队伍里有没有人,有人就自觉排队,没人再尝试获取锁。
java
// 公平锁的获取逻辑
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;
}
}
// ...
return false;
}
关键区别就在于hasQueuedPredecessors()方法------它会检查是否有线程在排队,就像在问"我前面还有人吗?"
为什么AQS要这样设计?
AQS的这种"中立"设计,体现了Java并发框架的高明之处:
- 不做一刀切:不同场景对公平性的需求不同,AQS不强制选择
- 性能与公平的平衡:非公平锁吞吐量更高,公平锁保证顺序,用户可以按需选择
- 灵活性至上:同一个AQS框架,既能实现ReentrantLock,也能实现CountDownLatch等完全不同的同步器
一句话总结
AQS就像一张白纸,本身没有公平或非公平的属性,而是由使用者(子类)通过实现特定方法来描绘出不同的"公平性图景"。理解了这一点,你就真正抓住了Java并发编程的精髓!
希望这篇文章能帮你更轻松地理解AQS的设计智慧。如果觉得有收获,别忘了点赞收藏哦