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 小时前
字节跳动震撼开源Coze平台!手把手教你本地搭建AI智能体开发环境
前端·人工智能·后端
不过普通话一乙不改名6 小时前
第一章:Go语言基础入门之函数
开发语言·后端·golang
豌豆花下猫7 小时前
Python 潮流周刊#112:欢迎 AI 时代的编程新人
后端·python·ai
Electrolux7 小时前
你敢信,不会点算法没准你赛尔号都玩不明白
前端·后端·算法
whhhhhhhhhw7 小时前
Go语言-fmt包中Print、Println与Printf的区别
开发语言·后端·golang
ん贤8 小时前
Zap日志库指南
后端·go
Spliceㅤ8 小时前
Spring框架
java·服务器·后端·spring·servlet·java-ee·tomcat
IguoChan8 小时前
10. Redis Operator (3) —— 监控配置
后端
Micro麦可乐10 小时前
前端与 Spring Boot 后端无感 Token 刷新 - 从原理到全栈实践
前端·spring boot·后端·jwt·refresh token·无感token刷新
方块海绵10 小时前
浅析 MongoDB
后端