{"name":"并发工具类","id":"编程语言-JAVA-JAVA并发编程-并发工具类","content":"# JUC\n\n## 并发的问题空间（Why 并发工具存在）\n\n所有 Java 并发工具，本质上都在解决**有限计算资源在多执行主体之间的分配与协调问题**。\n\n竞态源于三条件并存——状态被**共享**、状态**可变**、访问**无序并发**。消解任一条即安全：**不共享**、**不可变**、或给并发访问强加秩序（**协调**）。\n\n该问题空间按来源分为三层，层次越靠上越接近并发的本体：\n\n| 层次 | 问题域 | 核心命题 |\n| --- | --- | --- |\n| 本体问题（任何并发模型固有） | 竞争安全 | 共享状态的变更必须**不可分割地完成**；本质冲突：写-写、读-写 |\n| 本体问题（任何并发模型固有） | 协作同步 | 多个线程需要**在时间或阶段上达成一致**——不是竞争，而是协作 |\n| 基底问题（共享内存模型引入） | 可见性与有序性 | 一个线程的修改，**何时、以何种顺序**被其他线程观察到；源于 JMM，消息传递模型中由通信本身承载 |\n| 工程派生（资源有限性） | 准入控制 | 控制**同时访问者数量**；互斥（N=1）与限流（N>1）是同一问题的两个参数点 |\n| 工程派生（线程模型 + 隐式参数） | 上下文隔离与传播 | 并发执行下，如何**安全地保存、传递线程上下文** |\n| 工程派生（任务生命周期） | 取消与关闭 | 执行中的任务如何**可被安全地停止**，而非强制终结 |\n\n## JUC 同步工具的构建分层（How JUC 求解问题空间）\n\n本文档讨论的同步工具，均可映射回上述问题域之一或其组合；其构建呈四层结构：\n\n```text\n并发问题空间（三层六域，见上节）\n   ↓\n并发抽象模型 ─┬─ 准入控制器：互斥锁（N=1）、信号量（N>1）\n              ├─ 协作同步器：门闩、栅栏、会合点\n              └─ 上下文容器：ThreadLocal 系（落地为线程内私有 Map，不经由以下两层）\n   ↓（准入与协作两支）\n构建路径 ─┬─ 直接扩展 AQS：ReentrantLock / Semaphore / CountDownLatch\n          ├─ 组合 AQS 产物：CyclicBarrier（Lock + Condition + 分代）、ArrayBlockingQueue\n          └─ 绕开框架：StampedLock / Phaser / Exchanger\n   ↓\n公共原语层：CAS + LockSupport.park/unpark\n```\n\n两个横切域内嵌于所有工具：\n\n* **可见性与有序性**：happens-before 保证内建于每个工具的获取/释放语义\n* **取消与关闭**：中断契约贯穿 acquire 变体族\n\n| 层 | 稳定性 | 原因 |\n| --- | --- | --- |\n| 问题空间 | 极稳定 | 并发的本体与基底问题，跨语言不变 |\n| 抽象模型 | 稳定 | 准入 / 协作 / 上下文是跨平台通用抽象 |\n| 构建路径 | 最易变 | JDK 内部实现：AQS 随版本多次调整（Unsafe→VarHandle、节点结构重写），工具仍在增补（StampedLock 为 JDK 8 新增） |\n| 公共原语 | 极稳定 | 直接映射硬件原子指令与 OS 调度原语，数十年不变 |\n\n## AQS：Java 并发的“内核抽象”\n\n问题驱动：每个阻塞型同步器都需要同一套基础设施——原子状态、等待队列、阻塞/唤醒，而无锁等待队列的实现难度远高于工具自身的语义。AQS 把这套共性下沉为统一框架，JUC 主要同步工具因此共享同一个“内核”。\n\n问题域覆盖：state 的原子变更求解**竞争安全**；准入判定与排队求解**准入控制**；共享模式与条件队列承载**协作同步**；中断契约横切**取消与关闭**——三域一横切，对应构建分层图的\"直接扩展\"路径。\n\n### AQS 的设计哲学\n\nAQS（AbstractQueuedSynchronizer）并不是“锁”，而是：\n\n> **同步器不变部分的统一框架**：状态托管、排队、阻塞/唤醒由框架固化，准入语义由子类填入\n\n两条原则构成其哲学：\n\n1. **不变 / 可变分离**：任何阻塞型同步器 = 概念稳定的骨架（状态、队列、阻塞/唤醒）+ 随工具而异的语义（谁能通过）。AQS 以模板方法切分二者——子类只实现 tryAcquire / tryRelease（及 Shared 变体）的准入判定，调度、排队、阻塞统一托管。\n2. **成本只向竞争者收取**：无竞争走快路径，一次 CAS 即过；排队设施懒初始化，不竞争则不存在。慢路径的全部复杂度，只由竞争失败者承担（机制落地见自举约束）。\n\n### AQS 的核心模型：一份状态、两类队列、两种模式\n\n```text\nstate（volatile int）→ 资源状态机：准入判定的依据，语义由子类定义\n同步队列（CLH 变体） → 安置竞争失败线程：排队、阻塞（park）、按序唤醒（unpark）\n条件队列             → 安置协作型等待：await 入队，signal 转移至同步队列重新竞争\n```\n\n同一骨架按唤醒分发分两种模式，排队与阻塞完全复用：\n\n| 模式 | 准入判定 | 唤醒分发 | 典型工具 |\n| --- | --- | --- | --- |\n| 独占 | 成 / 败 | 单点唤醒 | ReentrantLock、写锁 |\n| 共享 | 返回剩余量 | 按余量传播唤醒 | 读锁、Semaphore、CountDownLatch |\n\nstate 只是一个数字，语义由子类解释（重入计数 / 许可数 / 倒数计数）——这是众多工具能共享同一框架的直接原因。\n\n### AQS 的机制设计：三个必答问题与一条自举约束\n\n#### 同步器的三个必答问题\n\n剥离 Java 语境，任何阻塞型同步设施（OS 等待队列、Linux futex、分布式锁）都必须回答三个问题：\n\n| 必答问题 | 设计空间 | AQS 的取舍 | 取舍理由 |\n| ------- | ------- | --------- | ------- |\n| 谁能通过（准入判定） | 状态如何表达、如何原子变更 | volatile state + CAS，判定规则下放子类 | 框架不预设资源语义，换取通用性 |\n| 没通过的怎么办（等待安置） | 自旋（耗 CPU、零唤醒延迟）↔ 阻塞（让出 CPU、有唤醒延迟） | 短暂自旋后 park 的混合策略 | 临界区时长未知，取两端折中 |\n| 等待者何时再试（唤醒分发） | 广播惊群 ↔ 精准定点 | FIFO 队列 + 前驱唤醒后继 | 惊群浪费调度；排队天然回答\"下一个是谁\" |\n\n> 这组取舍与 Linux futex 同构：**快路径用原子指令，慢路径才进等待队列**。AQS 可视为该通用同步架构在用户态、库层面的实例化。\n\n#### 一条自举约束，统摄所有实现细节\n\nAQS 面临一个根本约束：**锁的实现内部不能再用锁**（否则无限递归），等待队列本身必须是无锁结构（CAS + volatile）：\n\n* 入队两步无法原子 → 只有 prev 链始终可靠 → 唤醒时从 tail 回溯\n* 等待者 park 后失去行动能力 → 唤醒责任倒置给前驱（SIGNAL 是写在前驱上的\"唤醒契约\"：契约不成立就不睡）\n* 队列懒初始化，无竞争时不存在 → 快路径只有一次 CAS——哲学原则二的机制落地\n* park 的唤醒来源二义（unpark / 中断）→ 中断契约\"不响应但不吞掉\"：只记录，获取成功后补偿标志\n\n### AQS 的能力边界：acquire/release 范式\n\nAQS 的抽象范式是 **acquire/release**——一切能表达为\"获取 / 释放某种数量的资源\"的同步语义。工具与该范式的匹配度，决定了它的构建层次：\n\n| 构建层次 | 方式 | 代表 | 因果 |\n| ------- | ---- | ---- | ---- |\n| 直接扩展 | 内部 Sync 类继承 AQS | ReentrantLock、ReadWriteLock、Semaphore、CountDownLatch | 语义可归约为 acquire/release |\n| 组合复用 | 用 AQS 的产物拼装 | CyclicBarrier（= Lock + Condition + 分代）、ArrayBlockingQueue | 需要的是\"锁内协作\"，不必发明新同步器 |\n| 绕开框架 | 直接基于 CAS + park | StampedLock（乐观读验证）、Phaser（动态阶段推进）、Exchanger（成对会合） | 语义无法映射到 acquire/release 排队范式 |\n\n### 统一规约：API 各异，契约同一\n\n**归约一：所有同步器都是 acquire/release 双操作的具名化。** JUC 刻意不定义统一接口，名字服务于领域语义，契约完全同源：\n\n| 工具 | acquire 形态 | release 形态 |\n| --- | --- | --- |\n| Lock | lock() | unlock() |\n| Semaphore | acquire() | release() |\n| CountDownLatch | await() | countDown() |\n| FutureTask | get() | 任务完成 |\n\n**归约二：每个阻塞型 acquire 都遵循同一组变体契约**——本质是把失败处理的预算交给调用方：\n\n| 变体 | 语义 | 调用方的预算 |\n| --- | --- | --- |\n| acquire（阻塞） | 等到为止 | 无预算约束 |\n| tryAcquire | 立即返回成败 | 快速失败 / 降级路径 |\n| tryAcquire(timeout) | 有界等待 | 明确的延迟预算 |\n| acquireInterruptibly | 等待中可被取消 | 外部取消权 |\n\n工具 API 中超出该契约的方法，才是它真正的增量语义（如 StampedLock 的 validate、Phaser 的 register）\n\n## 互斥锁：ReentrantLock 与 synchronized\n\n问题域定位：互斥锁是**竞争安全的排他解**，同时是**准入控制的 N=1 特例**。Java 提供同一抽象的两个实现：JVM 内建的 synchronized 与 JDK 库层的 ReentrantLock。\n\n### 抽象模型：一个骨架，两个基底\n\n互斥锁的抽象骨架与实现基底无关：**持有者 + 重入计数 + 等待队列**。两个实现是该骨架在不同层的实例：\n\n| 骨架要素 | synchronized（ObjectMonitor，JVM/C++） | ReentrantLock（AQS，JDK/Java） |\n| --- | --- | --- |\n| 持有者 + 重入计数 | owner + recursions | exclusiveOwnerThread + state |\n| 同步队列 | EntryList | CLH 队列 |\n| 条件队列 | WaitSet（单个） | ConditionObject（可多个） |\n\n骨架之外，设计通则同样跨基底成立：\n\n* **可重入同源**：线程身份绑定 + 计数状态——同一线程再次进入只递增计数，消除“自己等自己”的死锁\n* **成本只向竞争者收取同源**：synchronized 无竞争时仅一次 mark word CAS（轻量级锁），出现竞争才膨胀为 ObjectMonitor——与 AQS 的“快路径 CAS + 队列懒初始化”是同一原则在两个基底的实例\n\n> 同一骨架与同一组通则在 JVM 层与 JDK 层各实现一次：骨架与通则属于概念稳定层，基底只是工程选址——再次印证构建分层的 U 形结论。\n\n演进注脚：偏向锁曾为“单线程反复加锁”场景连 CAS 也省去，JDK 15 起废弃——多核竞争成为常态后，偏向撤销的成本反超收益。**优化的生命周期取决于其环境假设的存续**。\n\n### 根本分歧：语言结构 vs 库对象\n\n两个实现的全部 API 差异，都是一个根本选择的投影——**锁是语法块，还是普通对象**：\n\n| 维度 | synchronized（语言结构） | ReentrantLock（库对象） |\n| --- | ------------ | ------------- |\n| 锁释放 | 块退出自动释放（含异常路径） | 手动，依赖 try/finally 纪律 |\n| acquire 变体 | 仅无限阻塞一种形态 | 完整四变体（阻塞 / try / 限时 / 可中断） |\n| 条件等待集 | 单等待集（wait/notify） | 多 Condition |\n| 公平性 | 不可配置（非公平） | 可配置 |\n| 可优化性 | 锁边界对编译器可见：锁消除、锁粗化 | JIT 视角是普通方法调用，无语义级优化 |\n\n因果链：语法块 → 锁边界对编译器与 JVM 可见 → 释放可自动、优化可施加，但表达力被语法封顶（无法跨作用域、无法定制策略）；普通对象 → 表达力完整（变体、多条件队列、跨方法持锁、策略可配），但正确性退回为调用方纪律。\n\n这组\"安全换表达力\"的交换在工程中反复出现：GC vs 手动内存管理、声明式事务 vs 编程式事务——语言/框架接管得越多，越安全也越不自由。\n\n### 公平性：ReentrantLock 独有的增量\n\nsynchronized 无公平选项（固定非公平），公平策略是 ReentrantLock 在骨架之上唯一的语义增量；机制上只是准入判定时是否检查队列前驱，一行判断分出两种价值取向：\n\n| 策略   | 价值取向 | 代价   |\n| ---- | ---- | ---- |\n| 公平锁  | 顺序正义 | 吞吐下降 |\n| 非公平锁 | 系统效率 | 局部饥饿 |\n\n> 公平性不是技术问题，而是系统价值判断。\n\n非公平锁吞吐更高的机制因果：锁释放瞬间，新到线程可直接 CAS 抢锁，省去\"唤醒队首 → 调度延迟 → 锁空闲窗口\"的代价。公平性买的是顺序，付出的是上下文切换；故 ReentrantLock 默认非公平，公平模式只用于顺序敏感场景（如连接池分配）。\n\n### 选型判据\n\n> 能用 synchronized，就不要用 Lock。\n\n优先 synchronized 的理由源于\"锁是语法块\"一侧的收益：自动释放消灭一类泄漏错误、JVM 可持续优化、不依赖手动纪律。升级到 Lock 的触发条件：需要 acquire 变体（试、限时、可中断）、多条件等待集、公平性，或非块结构持锁（跨方法 / 跨作用域）——任一命中即为\"不能用 synchronized\"。\n\n## 读写分离：ReentrantReadWriteLock 与 StampedLock\n\n问题域定位：仍属竞争安全 + 准入控制，但准入规则从**计数**细化为**按操作类型的兼容性矩阵**。问题空间已指明本质冲突只有写-写、读-写——读-读本无冲突，互斥锁的 N=1 准入把它也串行化了；读写分离即把准入规则对齐到真实冲突结构：\n\n| 兼容性 | 读 | 写 |\n| --- | --- | --- |\n| **读** | 共享 | 互斥 |\n| **写** | 互斥 | 互斥 |\n\n### ReentrantReadWriteLock：悲观范式\n\n* 求解方式：进入临界区**前**声明意图，读 / 写两副 acquire 语义共享一个 state（高低位分段：读计数 / 写重入计数）\n* 范式固有的新问题——**调度政策**：读者按批通过与写者及时进入不可兼得，朴素读优先实现会写饥饿\n* RRWL 的取舍：默认非公平 + 新到读者让位于队首等待的写者，以部分读吞吐换写者不被饿死\n\n### StampedLock：乐观范式\n\n* 核心思想：**先假设没有写冲突**。乐观读不持有任何东西，三步法：取 stamp（版本号）→ 拷贝状态到局部变量 → validate 校验；失败升级为悲观读锁\n* 调度问题被范式消解：读者不持锁即不阻塞写者，\"读者批次 vs 写者及时性\"的取舍整个消失\n* 增量的代价（风险集中在增量上）：\n  * 绕开 AQS 的连带：stamp 票据替代持有者语义，**不可重入、无 Condition**\n  * 乐观的连带：validate 之前读到的可能是**不一致中间态**，必须先拷贝局部、校验通过后才可信任——“忘 validate”是该工具的头号事故源\n\n> 这是从“锁竞争”向“冲突检测”的范式迁移，与数据库乐观并发控制（version 字段提交校验）、CAS 重试同构：**版本号 + 事后校验**替代**进入时排他**。\n\n### 选型判据\n\n| 场景 | 选择 | 原因 |\n| --- | --- | --- |\n| 需要重入或条件等待 | ReentrantReadWriteLock | StampedLock 缺这两项语义 |\n| 读临界区短、状态可拷贝为局部变量 | StampedLock 乐观读 | 读零持有，写不被阻塞 |\n| 写占比高 | 退回互斥锁 | 兼容性矩阵的收益与读占比成正比，写多时分离开销倒挂 |\n\n## 协作而非竞争：CountDownLatch、CyclicBarrier 与 Phaser\n\n问题域定位：本章工具求解**协作同步**域——多个线程在时间或阶段上达成一致。它们与准入域工具共享 acquire/release 契约，分野在 state 的语义：**不再表示资源量，而表示进度**——await 不消耗配额，条件满足时全体放行（共享模式的\"按余量传播\"在此退化为广播）。等待的是事件，不是资源。\n\n### CountDownLatch：一次性门闩\n\n* 模型：**事件完成计数**。state 初始为 N，countDown 递减，归零放行全部 await 者\n* 结构特征：**等待者与计数者分离**——countDown 的线程不等待，await 的线程不计数，单向不对称\n* 不可重置的因果：state 单调递减至终态，acquire/release 范式中没有\"重置\"语义\n* 构建上是 AQS 共享模式的纯契约实例，增量语义≈0——简单性即其价值\n\n### CyclicBarrier：可循环栅栏\n\n* 模型：**参与者互等的会合点**。每个线程既是计数者也是等待者，到齐才走，对称结构；适用于迭代式并行计算\n* 可复用的因果：组合构建（Lock + Condition + **分代**）——每次放行开启新代，\"代\"是可重置的载体\n* 增量语义：barrierAction（最后到达者执行汇总动作）、broken 状态机\n\n### 失败语义：协作与竞争的深层差异\n\n竞争域的失败是**隔离的**——一个线程获取锁失败不影响他人；协作域的失败是**连带的**——互等结构中个体缺席必然波及集体。两个工具代表两种处理策略：\n\n| 策略 | 工具 | 行为 | 工程含义 |\n| --- | --- | --- | --- |\n| 不检测，责任交使用方 | CountDownLatch | 计数缺位则等待者悬挂 | 必须 finally 中 countDown，或 await 带超时兜底 |\n| 检测并传播 | CyclicBarrier | 一人中断/超时即 broken，全组抛 BrokenBarrierException | 框架接管失败一致性，使用方处理集体重试 |\n\n### 两者的本质区别\n\n| 维度 | CountDownLatch | CyclicBarrier |\n| --- | --- | --- |\n| 对称性 | 等待者与计数者分离 | 参与者互等 |\n| 可复用 | 否（state 单调至终态） | 是（分代重置） |\n| 失败语义 | 无内建检测 | broken 传播全组 |\n| 构建路径 | 直接扩展 AQS（共享模式） | 组合复用（Lock + Condition + 分代） |\n\n### 演进：Phaser 解除两个硬限制\n\n| 硬限制 | 受限工具 | Phaser 的解除方式 |\n| --- | --- | --- |\n| 一次性 | CountDownLatch | 多阶段推进：phase 递增，天然循环 |\n| 参与方固定 | CyclicBarrier | register / arriveAndDeregister 动态注册 |\n\n代价：语义无法映射到 acquire/release 排队范式，只能绕开 AQS 自建（见能力边界）——灵活性的获得以失去框架托管为交换。\n\n选型判据：**等待者是否参与计数**——外部观察者等内部事件完成 → CountDownLatch；对等参与者互等 → CyclicBarrier；多轮迭代 → CyclicBarrier；参与方动态增减 → Phaser。协作三件套的第三件\"会合点\"（Exchanger，成对数据交换）见后文。\n\n## 准入控制的一般形式：Semaphore\n\n问题域定位：互斥锁是准入控制的 N=1 特例，**Semaphore 是其一般形式**——以 N 份许可控制同时访问者数量。构建上经 AQS 共享模式直接扩展：state 即许可数，acquire 减、release 加，按余量传播唤醒。\n\n### 抽象模型：配额，而非锁\n\n锁与信号量的机制分界在**所有权**：\n\n| 维度 | 锁 | Semaphore |\n| --- | --- | --- |\n| 持有者 | 有（owner 记录） | 无（只有计数） |\n| 重入 | 可定义（身份绑定计数） | 无此概念 |\n| 释放者约束 | 仅持有者可释放 | 任意线程可释放，甚至先释放后获取 |\n\n边界警告：问题层面\"互斥 = 准入 N=1\"，但工具层面 **Semaphore(1) ≠ 互斥锁**——差异即所有权。无所有权是双刃：跨线程释放成为可能（A 线程获取、B 线程归还，异步与管道场景的刚需），误释放也无任何防护。问题层等价、工具层不等价，再次印证\"问题 ≠ 解法\"。\n\n增量语义（超出 acquire/release 契约的部分）：批量许可 acquire(n) / release(n)、跨线程释放、许可数动态调整（reducePermits）。公平性选项与互斥锁同框架，但价值取向不同——限流场景下公平性防的是**饥饿**而非顺序。\n\n### 工程意义：失效模式的转换\n\n* 原理：把**无界并发收敛为有界并发**。过载时系统的失效模式从\"全体崩溃\"变为\"部分等待或快速失败\"（acquire 排队 / tryAcquire 降级，对应变体契约的预算选择）——本质是**用延迟换可用性**\n* 跨尺度同源：Semaphore 是进程内的限流原语，与分布式限流是同一问题在不同尺度的解——许可即令牌，Semaphore 即无补充速率的退化令牌桶，见[流量控制](/软件工程/架构/系统设计/流量控制.md)\n\n失败模式：忘记 release 导致**许可泄漏**，比锁泄漏更隐蔽——锁泄漏立即死锁，是响亮失败；许可泄漏是池子慢性萎缩，无声衰减。无所有权使框架无法代为检查，release 必须落在 finally。\n\n## 会合点：Exchanger\n\n问题域定位：协作同步域的第三件工具——**两方对齐 + 双向数据交换**，会合与通信合一，即 **rendezvous 语义**。\n\n与栅栏的区分：CyclicBarrier 是 N 方对齐、不带数据；Exchanger 是 2 方会合、各自携带数据并交换。协作章的对称性框架延续：双方互为计数者与等待者。\n\n* 构建路径：成对会合无法映射 acquire/release 排队范式，绕开 AQS、直接基于 CAS + park（见能力边界）——先到者占槽等待，后到者 CAS 配对并互换数据\n* 失败语义：对方缺席则永等——协作连带性在二元场景的极端形式，工程上必须用 exchange(timeout) 兜底\n* 典型场景：流水线两侧交换满 / 空缓冲区——**双缓冲免拷贝**，数据所有权在会合点易手\n\n> 横向同构：Go 无缓冲 channel 的收发、Ada 的 rendezvous——\"会合即通信\"是跨语言的稳定模式；Exchanger 是 Java 中最接近同步 channel 的原语。\n\n## 公共原语：LockSupport 与 park/unpark\n\n问题域定位：构建分层的最底层。一切阻塞型工具最终落到两个原语——**CAS**（原子状态变更，由硬件原子指令支撑，见[基础概念](/编程语言/JAVA/JAVA并发编程/基础概念.md)）与 **park/unpark**（线程阻塞 / 唤醒，本章）。\n\n### 许可模型：丢失唤醒的消解\n\npark/unpark 围绕**二值许可**（0/1，不累积）工作：unpark 发放许可，park 消耗许可，**有许可则立即返回**。因此 unpark 可先于 park 到达而不丢失——\"检查条件 → 阻塞\"之间被唤醒的竞态窗口被许可吸收。这是它取代 wait/notify 成为底层原语的根本原因。\n\n### 对 wait/notify 的三重解放\n\n| 约束 | wait/notify | park/unpark |\n| --- | --- | --- |\n| 锁依赖 | 必须先持有监视器锁 | 无需任何锁 |\n| 时序依赖 | notify 早于 wait 即丢失 | unpark 先行被许可记住 |\n| 唤醒目标 | 无法指定线程 | 精准定点：unpark(thread) |\n\n### park 的返回契约\n\npark 返回有三种原因：unpark、中断、虚假返回——且 park 不报告原因，调用方必须**循环重检条件**。AQS 自举约束中的中断契约（\"不响应但不吞掉\"）正是建立在这一契约之上。\n\n### OS 映射\n\n每个线程挂一个 Parker（互斥量 + 条件变量，Linux 上经 futex）——U 形表\"公共原语直接映射 OS 调度原语\"的具体落点：park/unpark 是 OS 阻塞原语的 JVM 封装，CAS 是硬件 cmpxchg 的封装。上层全部工具的阻塞语义到此为止，再往下即内核调度。\n\n## 上下文隔离与传播：ThreadLocal 及其演进\n\n问题域定位：到此，文档第一次**踏出\"协调\"主线**。前面每件工具都承认共享、管理争用；ThreadLocal 走另一条根本路径——**不共享**：以\"每线程一份副本\"取消共享这个前提，争用从源头不存在。这正是它在构建分层图中唯一\"不经由 CAS+park 原语层\"的原因——无争用可仲裁，便不需要原子原语。本质是**以空间换免同步**。\n\n### 抽象模型：每线程副本\n\nThreadLocal 解决**隐式参数传递**——无需在调用链层层透传的上下文（事务、用户身份、traceId），挂在\"当前线程\"这个隐式坐标上。\n\n关键设计：**ThreadLocalMap 是 Thread 的字段**（threadLocals），不是 ThreadLocal 的字段，以 ThreadLocal 实例为 key、值为 value。一个推论值千金——线程访问的永远是自己 Thread 上的 map，**天然零竞争**，免同步由此而来。\n\n### 机制：一个设计选择，三个后果\n\nmap-on-Thread 这一选择同时派生隔离收益与两个风险，三者同源：\n\n| 后果 | 机制 |\n| --- | --- |\n| 线程隔离（收益） | 每线程读写各自 Thread.threadLocals，无共享、无锁 |\n| 脏数据（风险） | map 随线程而非任务存活 → 线程池复用线程时残留不清，下个任务读到上个任务的值 |\n| 内存泄漏（风险） | Entry 的 key 弱引用 ThreadLocal、value 强引用；ThreadLocal 被回收后 key=null，value 仍被池线程经 Entry 强引用 → 陈旧 Entry 无法释放 |\n\n两个风险的根都是\"map 绑在长生命周期的线程上\"。对策同源：**用完即 remove()**——线程池场景必须在 finally 中清理（set/get 仅机会性清理部分 null-key 槽，不可依赖）。\n\n### 演进：传递边界依次扩张\n\nThreadLocal 的副本严格绑定单线程，跨线程即断。两次演进都在扩张\"传递边界\"，各自付出代价：\n\n| 工具 | 扩张的边界 | 机制 | 局限/代价 |\n| --- | --- | --- | --- |\n| ThreadLocal | 单线程内 | map 挂当前线程 | 跨线程断裂 |\n| InheritableThreadLocal | 父→子线程 | 子线程**构造时** childValue() 从父拷贝 | 池线程预创建复用，捕获不到\"提交时\"上下文，失效 |\n| TransmittableThreadLocal | 跨线程池 | 提交时捕获快照，执行前 replay、执行后 restore | 需装饰 Runnable/Callable 或线程池 |\n\nTTL 的要害是**时机分离**：捕获在**任务提交时**（父线程上下文尚在），应用在**任务执行前**（已是池线程）。InheritableThreadLocal 把捕获绑死在线程创建时，而池线程创建早于任务提交，故必然漏掉提交时刻的上下文——这才是它在线程池失效的精确原因，而非笼统的\"无法适配\"。\n\n### 适用边界：隐式传递是双刃\n\n隐式传递省去层层透参，代价是**数据流不可见**：依赖被隐藏（损可测试性）、来源不显（损可读性）、跨层耦合。故 ThreadLocal 是\"必要的恶\"——框架内部用（事务管理、MDC 日志、SecurityContext），业务代码慎用，能显式传参则优先显式。\n\n> 横向同构：ThreadLocal 是\"每线程的隐式环境变量\"，与 Go `context.Context`、Kotlin `CoroutineContext` 同解一题，分歧在隐式（绑线程）vs 显式（随调用传递）。时间维度的趋势——虚拟线程下\"每线程一副本\"成本随线程数爆炸，JDK 以 **ScopedValue**（不可变、作用域绑定）替代之：隐式上下文正从**线程绑定**走向**作用域绑定**。\n\n## 选型方法：从问题域到工具\n\n工具会朽，方法不朽。下面先给不随 JDK 变的决策路径，再给随之派生的工具表。\n\n### 决策路径\n\n1. **定位问题域**：先判属六域中哪一类，并分清走的是\"协调共享\"主线，还是\"不共享\"支线（仅 ThreadLocal 系）\n2. **域内默认最简**：每个域有一个最简实现作默认（synchronized / Semaphore / RRWL / CountDownLatch / ThreadLocal）\n3. **仅在出现明确增量需求时升级**：没有增量需求就停在默认——\"能用 synchronized 就不用 Lock\"是这条规则的特例\n4. **横切域不单独选**：可见性、取消不挑工具，由所选工具自带（见表下注）\n\n### 判据表\n\n| 问题域 | 默认（最简） | 出现以下增量需求时 | 升级 / 选用 |\n| --- | --- | --- | --- |\n| 竞争安全 · 临界区互斥（准入 N=1） | synchronized | 试锁 / 限时 / 可中断、多条件队列、公平、跨方法持锁 | ReentrantLock |\n| 竞争安全 · 单变量原子 | AtomicXxx（CAS） | 多变量需一致 | 退回锁 |\n| 竞争安全 · 读多写少 | ReentrantReadWriteLock | 读极短且状态可拷贝 → 乐观读；需重入 / Condition → 留 RRWL；写占比高 → 退回互斥 | StampedLock |\n| 准入控制 N>1（配额 / 限流） | Semaphore | 进程外限流 | 令牌桶（见流量控制） |\n| 协作同步 | CountDownLatch（等事件完成） | 需复用 / 对等互等 → 栅栏；动态参与方 / 多阶段 → Phaser；成对换数据 → Exchanger | CyclicBarrier / Phaser / Exchanger |\n| 上下文隔离与传播（不共享支线） | ThreadLocal | 父→子线程 → Inheritable；跨线程池 → 快照回放 | InheritableThreadLocal / TTL |\n\n判据是\"等待者是否参与计数\"\"读临界区是否够短可乐观\"\"上下文要跨多远\"——是可执行的条件，不是\"安全\"\"高效\"这类形容词。\n\n表下注（横切两域，不单独选工具）：\n\n* **可见性与有序性**：所选工具的获取 / 释放自带 happens-before；仅需\"写后即对他人可见\"时用 volatile\n* **取消与关闭**：选 acquire 的可中断变体（lockInterruptibly、可中断 await），中断契约贯穿整个变体族\n\n## 关联内容（自动生成）\n\n- [/编程语言/JAVA/JAVA并发编程/基础概念.md](/编程语言/JAVA/JAVA并发编程/基础概念.md) 涵盖原子性、可见性和有序性等基本概念，这些是所有并发工具的基础理论\n- [/编程语言/JAVA/JAVA并发编程/线程.md](/编程语言/JAVA/JAVA并发编程/线程.md) 详细介绍了线程模型，这是所有并发工具协调的基础\n- [/编程语言/JAVA/JAVA并发编程/线程池.md](/编程语言/JAVA/JAVA并发编程/线程池.md) 讨论了线程池，其中大量使用了诸如CountDownLatch和Semaphore等并发工具\n- [/编程语言/JAVA/JAVA并发编程/并发集合.md](/编程语言/JAVA/JAVA并发编程/并发集合.md) 涵盖并发集合，这些集合通常与并发工具类协同工作\n- [/编程语言/JAVA/JAVA并发编程/JAVA并发编程.md](/编程语言/JAVA/JAVA并发编程/JAVA并发编程.md) 提供Java并发编程的整体框架，展示了并发工具在整个并发编程体系中的位置\n- [/编程语言/JAVA/JVM/JAVA内存模型.md](/编程语言/JAVA/JVM/JAVA内存模型.md) “可见性与有序性”问题域的根——本文中 happens-before 作为每个工具的内建保证，其规则由 JMM 定义\n- [/编程语言/并发模型.md](/编程语言/并发模型.md) 本文只覆盖“协调共享”一条路径，不共享/不可变两条策略与共享内存、消息传递（CSP/Actor）模型由此承载\n- [/操作系统/进程与线程.md](/操作系统/进程与线程.md) 公共原语层 park/unpark 直接映射 OS 调度原语（futex、Parker=互斥量+条件变量），是线程阻塞/唤醒的底层落点\n- [/软件工程/架构/系统设计/流量控制.md](/软件工程/架构/系统设计/流量控制.md) Semaphore 是进程内限流原语，与令牌桶等分布式限流同解一题、只是尺度不同\n- [/中间件/数据库/数据库系统/事务管理/事务.md](/中间件/数据库/数据库系统/事务管理/事务.md) StampedLock 的乐观读（版本号+事后校验）与数据库 MVCC／乐观并发控制同构\n","metadata":"tags: ['编程语言', '并发编程', '数据库']","hasMoreCommit":true,"totalCommits":17,"commitList":[{"date":"2026-06-16T19:28:56+08:00","author":"MY","message":"docs(JAVA): 更新并发工具类文档完善理论框架和实践指南","hash":"274ced39b8425c9b08fd15e077456f21ee8f09b8"},{"date":"2026-06-13T10:28:21+08:00","author":"MY","message":"docs(JAVA): 更新JUC并发工具类文档完善问题空间和构建分层理论","hash":"adf7ede8730e8082e4e5a13d5c6926883cf4ae26"},{"date":"2026-02-12T14:07:03+08:00","author":"MY","message":"doc: 整理标签","hash":"290b3e8ad18f48832ac282290238d020fc030a88"},{"date":"2026-01-14T16:12:53+08:00","author":"MY","message":"docs(JAVA): 更新JUC并发工具类文档并添加相关图片资源","hash":"f676fa1c8153922ad1e2aa621f65c73006af4863"},{"date":"2025-01-16T11:26:35+08:00","author":"MY","message":"📦AQS","hash":"00504fafc1113a39b1f62fbe29065cb755a3e699"},{"date":"2024-08-28T19:28:47+08:00","author":"MY","message":"✏并发工具类","hash":"6c22e5bbff176dc1fb35424a932b6b81d83018df"},{"date":"2024-06-05T14:47:56+08:00","author":"MY","message":"✏并发","hash":"7c6ef9e57ea5c46ce3933e394560d07213d85163"},{"date":"2022-10-19T21:32:18+08:00","author":"MY","message":"✏️Java并发编程","hash":"5ec012b5f65756ede6a0402ff1eb8eba6b398a9e"},{"date":"2022-04-18T16:44:54+08:00","author":"cjiping","message":"✏️更新 并发工具类","hash":"27706afe8d550424a937ed9973bc0ea796246272"},{"date":"2021-09-10T15:44:58+08:00","author":"cjiping","message":"📦整理 Java 并发工具类","hash":"f46e45c586d3c4fd71a48eeb86f687259ca00432"}],"createTime":"2020-06-23T16:24:52+08:00"}