AQS揭秘:为什么它既不是公平锁也不是非公平锁?

你真的了解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的设计智慧。如果觉得有收获,别忘了点赞收藏哦

相关推荐
码事漫谈3 小时前
黄仁勋被裁第一天,就收到了橄榄枝
后端
金銀銅鐵4 小时前
带您看懂字节码指令(二):空循环里有什么
java·后端·ai编程
皮皮林5515 小时前
比 Xshell 还好用的 SSH 客户端神器,MobaXterm 太爱了!
后端
我不会起名字3226 小时前
会话缓存失效排查:区分 Redis 失效与节点广播
redis·后端·go·即时通讯·im·故障排查
深入云栈8 小时前
Netty 4.2.x 源码深度解析 (二十三):ByteBuf 核心设计 —— 索引、类型与零拷贝
java·后端·架构
JackJiang8 小时前
IM踩坑:聊天消息ID怎么设计?一个UUID搞不定,我用双ID解决
后端·架构
开心猴爷8 小时前
iOS 上架 App 流程
后端·ios
code_Bo8 小时前
不只命令行:我把跨 Agent 会话同步做成了桌面应用
前端·javascript·后端
用户8356290780519 小时前
使用 Python 控制 Excel 工作表的分页位置
后端·python
用户8356290780519 小时前
Python 如何在 Word 中添加和管理文档变量
后端·python