《Java 并发编程·JUC 源码与容器》4.2 CountDownLatch、CyclicBarrier、Phaser
对比 CountDownLatch 的一次性计数、CyclicBarrier 用锁加条件队列维护的“代”与 Phaser 的 long 位域拆分,再用同一份六分片加载场景的三种写法与真实输出,说明一次性、按代重用、按阶段推进各自的边界与选型取舍。
tag
对比 CountDownLatch 的一次性计数、CyclicBarrier 用锁加条件队列维护的“代”与 Phaser 的 long 位域拆分,再用同一份六分片加载场景的三种写法与真实输出,说明一次性、按代重用、按阶段推进各自的边界与选型取舍。
拆开 Semaphore 的 Sync,看许可如何直接复用 AQS 的 state、公平版为何只多一次排队检查、许可为负时 reducePermits 与 drainPermits 的欠账语义,再用下游通知扇出的限流实验验证并发上限确实收敛到 2。
StampedLock 用版本戳把读分成加锁读和乐观读两档。本节按 JDK 25 源码拆解 7 位读者计数、写位与版本位,实测 validate 的语义、不可重入与 tryConvertToWriteLock 的边界,并给出三版缓存的对照数据。
锁降级为什么必须先拿读锁再放写锁,升级为什么会自死锁,非公平模式的两条插队规则各自把谁饿住。附锁降级示例、12 次写者等待分布,以及 96 次与数千次以上的插队计数对比,并说明 JDK 25 的 head-writer 启发式为什么让完整写饥饿难以复现。
读写锁要把读者总数和写者重入数塞进同一个 state。本节按 JDK 25 源码核对 32/32 位布局、SHARED_UNIT 与 EXCLUSIVE_MASK 的位运算、HoldCounter 与 ThreadLocalHoldCounter 的分线程计数,并给出 javap 与反射探测的本机输出。
lockInterruptibly 与 tryLock(timeout) 在 JDK 25 统一走 acquire 大循环,返回值区分成功、超时与中断;Thread.interrupted() 负责在 park 醒来时收走中断标志。实测中断耗时、250ms 超时返回 false 的时刻,并用反射把 state 推到 Integer.MAX_VALUE 验证重入溢出。
JDK 25 的 ConditionObject 用 ConditionNode 串起条件队列:await 先 enableWait 入队并释放全部重入次数,signal 只把节点 enqueue 到同步队列而不唤醒。用双条件 notEmpty/notFull 手写有界队列,并用实测时间线证明 signal 后线程仍在 park。
JDK 25 把公平与非公平逻辑收进 initialTryLock:非公平两次不看队列直接 CAS,公平先用 hasQueuedThreads 再用 hasQueuedPredecessors 拦插队。用 100 轮插队计数实验给出四组真实数据,并解释非公平为何吞吐更高、公平锁的 tryLock 为何仍然会插队。
解释 tryAcquireShared 三种返回值的语义,对照旧版 setHeadAndPropagate、doReleaseShared 与 PROPAGATE 状态的由来,再以 JDK 25 的 signalNextIfShared 为主线,用自写共享门和 CountDownLatch 验证传播链。
对照 JDK 8 与 JDK 25 两套实现,拆解独占模式从 tryAcquire 失败到入队、park、唤醒、取消的完整路径,用一个非重入互斥锁跑出四线程严格 FIFO 的获取与释放顺序,并给出可观测插队次数的 BargingMutex 落地实现。
以 JDK 25 源码为准,拆开 AQS 的 state、head/tail 与 Node 四个字段,给出 javap 与反射实测输出,讲清旧版 waitStatus 四个常量到新版三个状态位的取舍,并解释为什么选择 CLH 变体队列以及它的适用边界。
《Java 并发编程·JUC 源码与容器》全书导航:说明本卷在三卷本中的位置、五部分十五章的依赖关系,给出顺序通读、症状驱动、按类查阅三条路径,并按部分列出 45 节完整索引、章间依赖图、全书实验清单与六个常见误区,便于按症状跳读或按章顺序通读,也便于当作类级查阅手册使用。
《Java 并发编程·JUC 源码与容器》前言:交代 JDK 25 源码的获取方式与包结构、用 unzip 与 javap 定位事实的方法、JMH 与 JCStress 的实验环境、源码摘录的标注约定,并列出本机核实过的 JDK 25 与旧版实现差异清单。
《Java 并发编程·JUC 源码与容器》开篇:从 JDK 25 里 AQS 被重写、经典教材的方法名全部消失这件事说起,说明读源码的三层收益、版本差异为何是本卷的头号风险,交代五部分十五章的结构、libraryd-conc 在本卷的改动范围,以及本书不承诺的内容。
《Java 并发编程·JUC 源码与容器》全 50 篇,以 JDK 25 源码为准,从 AQS 的 state 与队列讲到 ReentrantLock、读写锁、同步器、ConcurrentHashMap、并发集合、阻塞队列、原子类与 Striped64、VarHandle、无锁结构、线程池与 ForkJoin、CompletableFuture 与误用清单,附本机 javap 与实测输出。
把并发问题排查要用的 jcmd 子命令、线程转储读法、JFR 事件与过滤命令整理成一张表,并附本机 Temurin 25.0.5 上真实抓取的 Thread.print、jdk.JavaMonitorEnter、jdk.ThreadPark、jdk.VirtualThreadPinned 输出片段与解读。
把 JDK 25 常用并发 API 按线程与执行、同步器、并发容器、原子类、虚拟线程与结构化并发五组整理成速查表,每条给出核心方法、语义要点与选择边界,并附方法命名规律和「我要做什么 → 用哪个」反查表,以及各 API 的「什么时候不要用」。
把同一段三下游扇出分别用虚拟线程池和 StructuredTaskScope 实现:代码行数几乎相同、token 反而多 8 个,失败到返回却从 828ms 降到 140ms。本节给出迁移的四条硬边界——不可跨作用域、异常要解包、虚拟线程不是重点、preview 风险,并附真实报错。
awaitAll 只等结果不管失败,真正让「一个下游挂了、其余立刻停」的是 Joiner。本节用三个下游扇出实测五个内置 Joiner 的返回类型与取消时机:失败后 2ms 兄弟任务被中断,join 抛 FailedException 并保留原始 cause。
StructuredTaskScope 把一次请求 fork 出的子任务收进词法作用域:关闭前必须 join,关闭后不留活线程。本节用两路查询聚合实测 open/fork/join/close 的契约、子任务三种状态与超时取消,并对照 ExecutorService 说明任务生命周期为什么要绑在代码块上。