连载中 10/21

AQS(下):共享模式与一次完整源码走读

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

共享模式:负数排队,非负通行

独占模式一山不容二虎;共享模式的判定交给 tryAcquireShared,返回值本身就是语言:负数——失败,去排队;零或正数——成功,剩余额度就是返回值。妙在成功之后的传播:setHeadAndPropagate 会在「还有剩余额度、或队列里还有人等」时继续唤醒下一个排队者。Semaphore release 一个许可、队列连环放行;CountDownLatch 最后一个 countDown 把等门闩的线程一波全放完——都是这条传播链在干活。

源码走读:ReentrantLock 非公平锁的 lock()

选它当标本,因为十几行源码就能看懂非公平的本质:

final void lock() {
    if (compareAndSetState(0, 1))          // 上来就抢,不管队列有没有人排着
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);                         // 抢不到,走 AQS 排队流程
}

acquire 展开是三步:tryAcquire 再试一次 → 失败 addWaiter 入队 → acquireQueued 里自旋加 park。tryAcquire 的判定逻辑:state 为 0 就 CAS 抢(公平锁会先调 hasQueuedPredecessors 问一句「队列里有没有人在我前面」,有就不抢);state 非 0 就看持有人是不是自己,是则 state 加一完成重入。公平与非公平的全部差异,就是要不要先看队列——非公平省去唤醒排队线程的开销,吞吐更高,但极端情况下有人饿死;公平人人有份,代价是慢。

挂起唤醒的开关:waitStatus

Node 上的 waitStatus 是队列运转的开关位,四个值各有分工:SIGNAL(-1)——「我马上要 park 了,释放锁时记得叫醒我」,线程入队后先把前驱标成它,才安心挂起;CANCELLED(1)——等锁途中被中断或超时的废票,出队;CONDITION(-2)——排在 Condition 条件队列上的节点(下篇讲);PROPAGATE(-3)——服务共享模式的唤醒传播。理解它,jstack 里 Lock 相关的栈和 AQS 源码就都对上了。

重入的账本

走读下来重入也清楚了:state 就是重入次数的账本——获取时加一,释放时减一,减到零才真正让锁、唤醒后继。所以 unlock 必须和 lock 严格配对:多解一次直接抛 IllegalMonitorStateException,少解一次锁永不释放,后继全体干等。

AQS 的架子和走读都完成了。下一篇从源码抬头看 API:ReentrantLock 比 synchronized 多出来的四样能力——可中断、可超时、可公平、多条件队列——分别怎么用、什么时候用。

☕
503

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

#Java#AQS#ReentrantLock#源码#JUC

评论 (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 赞