连载中 9/21

AQS(上):一个双向队列,管住天下所有的锁

2026-10-27 · 39 阅读 · 0 评论 · 0 赞

JUC 里最值得读的一个类

ReentrantLock、CountDownLatch、Semaphore、ReentrantReadWriteLock——API 长得完全不同的四兄弟,翻开源码全是同一个内核:AQS(AbstractQueuedSynchronizer)。读懂它,JUC 的锁和协作工具一通百通。AQS 的全部秘密浓缩成三件套:一个 volatile int state、一条双向等待队列、独占与共享两种模式。

state:一个字段,各自表述

state 是同步状态,语义完全由子类定义:在 ReentrantLock 里是重入次数(0 空闲、1 首次获取、2 重入一层);在 Semaphore 里是剩余许可数;在 CountDownLatch 里是还剩几个门闩,归零放行;在 ReentrantReadWriteLock 里一拆二——高 16 位是读锁计数、低 16 位是写锁计数。state 用 volatile 修饰保证可见,所有修改都走 CAS——上一篇讲的原子能力,在这里成为基建。

模板方法:排队归我,抢锁归你

AQS 是教科书级的模板方法模式。它把活分成两半:排队、挂起、唤醒这些脏活全包了;抢锁的判定只留几个钩子给子类——tryAcquire / tryRelease(独占),tryAcquireShared / tryReleaseShared(共享)。子类只需回答一个问题:此刻 state 允许我拿到锁吗?允许就改 state 返回 true,不允许返回 false。排队调度是天下的公理,判定规则是各家的私法——这就是一个 AQS 能支撑几十个协作工具的结构原因。

抢不到的线程去哪:双向队列

tryAcquire 失败的线程被打包成 Node,追加到一条 CLH 变体的双向 FIFO 队列。排队中的线程不死循环硬抢,而是自旋加挂起的混合策略:先看一眼自己的前驱是不是队头(head),是才有资格再试一次;不是,或者试了又失败,就 park() 挂起,把 CPU 让出去,等前驱释放锁时被 unpark 唤醒。对比 synchronized 把线程挂在对象 monitor 的暗队列上,AQS 把队列做成了明牌——公平、超时、可中断这些高级能力,全是有了明牌队列才玩得起来的。

独占模式(一把锁一个人)的骨架到这就齐了。但 CountDownLatch 是「等多个事件」、Semaphore 是「N 个人同时进」——它们走的是另一条共享通道,源码里 setHeadAndPropagate 的唤醒传播相当精妙。下一篇接着拆。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#Java#AQS#JUC#并发编程#ReentrantLock

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10113 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9876 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9306 阅读 · 21 评论 · 287 赞