<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Java on WY 的技术博客</title><link>https://zhouwy.top/categories/java/</link><description>Recent content in Java on WY 的技术博客</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sun, 26 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://zhouwy.top/categories/java/index.xml" rel="self" type="application/rss+xml"/><item><title>CompletableFuture 到底该怎么用：从方法、技巧、踩坑到源码</title><link>https://zhouwy.top/posts/completablefuture-%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%E6%8A%80%E5%B7%A7%E8%B8%A9%E5%9D%91%E5%8E%9F%E7%90%86%E4%B8%8E%E6%BA%90%E7%A0%81/</link><pubDate>Sun, 26 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/completablefuture-%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%E6%8A%80%E5%B7%A7%E8%B8%A9%E5%9D%91%E5%8E%9F%E7%90%86%E4%B8%8E%E6%BA%90%E7%A0%81/</guid><description>&lt;h2 id="completablefuture-到底该怎么用从方法技巧踩坑到源码"&gt;CompletableFuture 到底该怎么用：从方法、技巧、踩坑到源码&lt;/h2&gt;
&lt;p&gt;很多人第一次接触 &lt;code&gt;CompletableFuture&lt;/code&gt;，感觉它很优雅：链式调用、异步编排、异常处理、多个任务聚合，看起来比手写线程池高级得多。&lt;/p&gt;
&lt;p&gt;但真到项目里，问题往往不是“不会用”，而是“以为自己会用”。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为什么有的回调在主线程执行，有的又跑到了线程池里？&lt;/li&gt;
&lt;li&gt;为什么 &lt;code&gt;thenApply()&lt;/code&gt; 和 &lt;code&gt;thenApplyAsync()&lt;/code&gt; 只差一个 &lt;code&gt;Async&lt;/code&gt;，行为却可能完全不同？&lt;/li&gt;
&lt;li&gt;为什么我明明用了异步，结果接口还是卡住了？&lt;/li&gt;
&lt;li&gt;为什么 &lt;code&gt;allOf()&lt;/code&gt; 执行完了，结果还得自己一个个 &lt;code&gt;join()&lt;/code&gt; 去拿？&lt;/li&gt;
&lt;li&gt;为什么异常被包成了 &lt;code&gt;CompletionException&lt;/code&gt;，日志看起来特别恶心？&lt;/li&gt;
&lt;li&gt;为什么线上机器 CPU 很高，最后发现是把阻塞 IO 全扔进了 &lt;code&gt;ForkJoinPool.commonPool()&lt;/code&gt;？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这篇文章不打算只列 API，而是把 &lt;code&gt;CompletableFuture&lt;/code&gt; 真正在工程里怎么用、怎么避坑、JDK 为什么这么设计，以及核心源码怎么运作，一次讲透。&lt;/p&gt;
&lt;h2 id="一先建立一个正确心智模型"&gt;一、先建立一个正确心智模型&lt;/h2&gt;
&lt;p&gt;很多人把 &lt;code&gt;CompletableFuture&lt;/code&gt; 理解成“异步线程工具”，这个说法不算错，但太浅了。&lt;/p&gt;
&lt;p&gt;更准确一点，它其实同时做了两件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;它是一个 &lt;code&gt;Future&lt;/code&gt;，代表“未来某个时刻会完成的结果”。&lt;/li&gt;
&lt;li&gt;它是一个 &lt;code&gt;CompletionStage&lt;/code&gt;，代表“这个结果完成之后，还能继续挂后续动作的可编排节点”。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;也就是说，&lt;code&gt;CompletableFuture&lt;/code&gt; 不只是“拿结果”，更重要的是“描述依赖关系”。&lt;/p&gt;
&lt;p&gt;你可以把它想成一个节点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;节点里最终会有一个结果，或者一个异常。&lt;/li&gt;
&lt;li&gt;节点完成后，会触发依赖它的后续节点。&lt;/li&gt;
&lt;li&gt;节点和节点之间可以串行、并行、汇聚、竞争。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这才是它和传统 &lt;code&gt;Future&lt;/code&gt; 最大的区别。&lt;/p&gt;
&lt;p&gt;传统 &lt;code&gt;Future&lt;/code&gt; 的典型写法是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Future&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Order&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; future &lt;span style="color:#f92672"&gt;=&lt;/span&gt; executor.&lt;span style="color:#a6e22e"&gt;submit&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;::queryOrder);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Order order &lt;span style="color:#f92672"&gt;=&lt;/span&gt; future.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;问题在于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;get()&lt;/code&gt; 是阻塞的。&lt;/li&gt;
&lt;li&gt;不能优雅地声明“拿到订单后再查用户”。&lt;/li&gt;
&lt;li&gt;多个 &lt;code&gt;Future&lt;/code&gt; 的组合很难写。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而 &lt;code&gt;CompletableFuture&lt;/code&gt; 的核心价值，就是把“等待结果”和“结果完成后的动作”拆开。&lt;/p&gt;
&lt;h2 id="二最常用的几类-api到底该怎么分"&gt;二、最常用的几类 API，到底该怎么分&lt;/h2&gt;
&lt;h3 id="1-创建任务"&gt;1. 创建任务&lt;/h3&gt;
&lt;p&gt;最常见的是这两个：&lt;/p&gt;</description></item><item><title>彻底搞懂 Monad：从概念到 Java 实战</title><link>https://zhouwy.top/posts/java-monad-%E4%B8%8E-stream-%E7%9A%84-flatmapmapmulti-%E6%8E%A2%E7%A7%98%E4%BB%8E%E6%A6%82%E5%BF%B5%E5%88%B0%E6%BA%90%E7%A0%81/</link><pubDate>Sun, 26 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/java-monad-%E4%B8%8E-stream-%E7%9A%84-flatmapmapmulti-%E6%8E%A2%E7%A7%98%E4%BB%8E%E6%A6%82%E5%BF%B5%E5%88%B0%E6%BA%90%E7%A0%81/</guid><description>&lt;h1 id="彻底搞懂-monad从概念到-java-实战"&gt;彻底搞懂 Monad：从概念到 Java 实战&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;你可能每天都在用 &lt;code&gt;Optional.flatMap&lt;/code&gt;、&lt;code&gt;Stream.flatMap&lt;/code&gt;、&lt;code&gt;CompletableFuture.thenCompose&lt;/code&gt;，却不知道它们背后的统一模型——&lt;strong&gt;Monad&lt;/strong&gt;。本文带你一次性搞懂 Monad 是什么、为什么需要它、在 Java 中如何应用，以及如何自己设计 Monad。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="1-从问题出发为什么需要-monad"&gt;1. 从问题出发：为什么需要 Monad？&lt;/h2&gt;
&lt;p&gt;先看一段典型的代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;User user &lt;span style="color:#f92672"&gt;=&lt;/span&gt; findUserById(id);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (user &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Address addr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; user.&lt;span style="color:#a6e22e"&gt;getAddress&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (addr &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; String city &lt;span style="color:#f92672"&gt;=&lt;/span&gt; addr.&lt;span style="color:#a6e22e"&gt;getCity&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; city;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Unknown&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这段代码里有 &lt;strong&gt;两个判空&lt;/strong&gt;，如果还有更多步骤，&lt;code&gt;if&lt;/code&gt; 会层层嵌套，难以阅读和维护。&lt;br&gt;
这种 &lt;strong&gt;“带有空值可能性的计算”&lt;/strong&gt; 非常常见，每次我们都要手动检查，写一堆样板代码。&lt;/p&gt;
&lt;p&gt;类似的场景还有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;异步计算&lt;/strong&gt;：拿到 &lt;code&gt;Future&lt;/code&gt; 后必须等它完成才能继续，还要处理异常。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多值计算&lt;/strong&gt;：处理集合时，每个元素返回一个集合，然后要手动 &lt;code&gt;addAll&lt;/code&gt; 合并。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可失败计算&lt;/strong&gt;：方法可能抛出异常，每一步都要 &lt;code&gt;try-catch&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Monad 的使命就是 &lt;strong&gt;把这些重复的控制逻辑抽离出来&lt;/strong&gt;，让你只关心“下一步做什么”，而不是“上一步是否成功/有值/已完成”。&lt;/p&gt;
&lt;h2 id="2-monad-是什么"&gt;2. Monad 是什么？&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Monad 是一种设计模式&lt;/strong&gt;，它定义了一个容器类型（比如 &lt;code&gt;Optional&amp;lt;T&amp;gt;&lt;/code&gt;），并提供两个操作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;unit&lt;/code&gt;（也叫 &lt;code&gt;return&lt;/code&gt;、&lt;code&gt;of&lt;/code&gt;、&lt;code&gt;pure&lt;/code&gt;）&lt;/strong&gt;：把一个普通值包装进 Monad。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;flatMap&lt;/code&gt;（也叫 &lt;code&gt;bind&lt;/code&gt;、&lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;、&lt;code&gt;thenCompose&lt;/code&gt;）&lt;/strong&gt;：把容器里的值取出来，应用一个函数（这个函数返回同类型容器），然后自动 &lt;strong&gt;展平&lt;/strong&gt; 结果，避免嵌套。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用公式表达（Haskell 风格）：&lt;/p&gt;</description></item><item><title>JDK线程、虚拟线程、Go协程与Netty Event Loop：调度模型和阻塞队列全景解析</title><link>https://zhouwy.top/posts/jdk%E7%BA%BF%E7%A8%8B%E8%99%9A%E6%8B%9F%E7%BA%BF%E7%A8%8Bgo%E5%8D%8F%E7%A8%8B%E4%B8%8Enetty-event-loop%E8%B0%83%E5%BA%A6%E6%A8%A1%E5%9E%8B%E5%92%8C%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97%E5%85%A8%E6%99%AF%E8%A7%A3%E6%9E%90/</link><pubDate>Wed, 22 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/jdk%E7%BA%BF%E7%A8%8B%E8%99%9A%E6%8B%9F%E7%BA%BF%E7%A8%8Bgo%E5%8D%8F%E7%A8%8B%E4%B8%8Enetty-event-loop%E8%B0%83%E5%BA%A6%E6%A8%A1%E5%9E%8B%E5%92%8C%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97%E5%85%A8%E6%99%AF%E8%A7%A3%E6%9E%90/</guid><description>&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E7%9B%AE%E5%BD%95"&gt;目录&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%BC%95%E5%AD%90%E8%BF%99%E5%87%A0%E4%B8%AA%E4%B8%9C%E8%A5%BF%E4%B8%BA%E4%BB%80%E4%B9%88%E6%80%BB%E8%A2%AB%E6%8B%BF%E6%9D%A5%E4%B8%80%E8%B5%B7%E6%AF%94%E8%BE%83"&gt;引子：这几个东西为什么总被拿来一起比较&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%85%88%E7%BB%9F%E4%B8%80%E4%B8%80%E4%B8%AA%E8%A7%86%E8%A7%92%E5%AE%83%E4%BB%AC%E6%9C%AC%E8%B4%A8%E4%B8%8A%E9%83%BD%E5%9C%A8%E8%A7%A3%E5%86%B3%E8%B0%83%E5%BA%A6%E9%97%AE%E9%A2%98"&gt;先统一一个视角：它们本质上都在解决调度问题&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#jdk-%E5%B9%B3%E5%8F%B0%E7%BA%BF%E7%A8%8B11-%E6%98%A0%E5%B0%84%E5%86%85%E6%A0%B8%E7%BA%BF%E7%A8%8B"&gt;JDK 平台线程：1:1 映射内核线程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#jdk-%E8%99%9A%E6%8B%9F%E7%BA%BF%E7%A8%8Bmn-%E8%B0%83%E5%BA%A6%E4%BD%86%E8%AF%AD%E4%B9%89%E4%BB%8D%E7%84%B6%E6%98%AF-thread"&gt;JDK 虚拟线程：M:N 调度，但语义仍然是 Thread&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#go-%E5%8D%8F%E7%A8%8B%E8%AF%AD%E8%A8%80%E8%BF%90%E8%A1%8C%E6%97%B6%E4%B8%BB%E5%AF%BC%E7%9A%84-gmp-%E8%B0%83%E5%BA%A6%E6%A8%A1%E5%9E%8B"&gt;Go 协程：语言运行时主导的 GMP 调度模型&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#netty-event-loop%E5%B0%91%E9%87%8F%E7%BA%BF%E7%A8%8B%E9%A9%B1%E5%8A%A8%E5%A4%A7%E9%87%8F%E8%BF%9E%E6%8E%A5"&gt;Netty Event Loop：少量线程驱动大量连接&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%8A%8A%E5%9B%9B%E8%80%85%E6%94%BE%E5%9C%A8%E4%B8%80%E5%BC%A0%E5%9B%BE%E9%87%8C%E7%9C%8B"&gt;把四者放在一张图里看&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%AE%83%E4%BB%AC%E4%B9%8B%E9%97%B4%E5%88%B0%E5%BA%95%E6%98%AF%E4%BB%80%E4%B9%88%E5%85%B3%E7%B3%BB"&gt;它们之间到底是什么关系&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E4%B8%80%E4%B8%AA%E5%85%B3%E9%94%AE%E9%97%AE%E9%A2%98%E9%98%BB%E5%A1%9E%E5%88%B0%E5%BA%95%E6%84%8F%E5%91%B3%E7%9D%80%E4%BB%80%E4%B9%88"&gt;一个关键问题：阻塞到底意味着什么&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#jdk-%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97%E5%85%A8%E6%99%AF%E6%A2%B3%E7%90%86"&gt;JDK 阻塞队列全景梳理&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#arrayblockingqueue"&gt;ArrayBlockingQueue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#linkedblockingqueue"&gt;LinkedBlockingQueue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#synchronousqueue"&gt;SynchronousQueue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#priorityblockingqueue"&gt;PriorityBlockingQueue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#delayqueue"&gt;DelayQueue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#linkedtransferqueue"&gt;LinkedTransferQueue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#linkedblockingdeque"&gt;LinkedBlockingDeque&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97%E4%B9%8B%E9%97%B4%E7%9A%84%E6%A0%B8%E5%BF%83%E5%8C%BA%E5%88%AB"&gt;阻塞队列之间的核心区别&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E8%BF%99%E4%BA%9B%E9%98%9F%E5%88%97%E5%92%8C%E7%BA%BF%E7%A8%8B%E6%A8%A1%E5%9E%8B%E6%80%8E%E4%B9%88%E9%85%8D%E5%90%88"&gt;这些队列和线程模型怎么配合&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%80%8E%E4%B9%88%E9%80%89%E6%8C%89%E5%9C%BA%E6%99%AF%E7%BB%99%E7%BB%93%E8%AE%BA"&gt;怎么选：按场景给结论&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%9C%80%E5%90%8E%E6%80%BB%E7%BB%93"&gt;最后总结&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="引子这几个东西为什么总被拿来一起比较"&gt;引子：这几个东西为什么总被拿来一起比较&lt;/h2&gt;
&lt;p&gt;很多人第一次接触这几个概念时，会感觉它们像是同一层面的东西：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JDK 线程&lt;/li&gt;
&lt;li&gt;JDK 虚拟线程&lt;/li&gt;
&lt;li&gt;Go 协程&lt;/li&gt;
&lt;li&gt;Netty Event Loop&lt;/li&gt;
&lt;li&gt;JDK 阻塞队列&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但它们其实分布在不同层次：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;线程 / 协程 / 虚拟线程&lt;/code&gt; 解决的是&lt;strong&gt;执行单元&lt;/strong&gt;怎么表示&lt;/li&gt;
&lt;li&gt;&lt;code&gt;调度器&lt;/code&gt; 解决的是&lt;strong&gt;谁来跑、什么时候跑&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Event Loop&lt;/code&gt; 解决的是&lt;strong&gt;I/O 事件如何复用少量线程&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;阻塞队列&lt;/code&gt; 解决的是&lt;strong&gt;任务或数据如何在并发单元之间传递&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;把这些层次混在一起，就很容易得出错误结论，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“虚拟线程就是 Java 版协程”&lt;/li&gt;
&lt;li&gt;“Netty loop 就是协程调度器”&lt;/li&gt;
&lt;li&gt;“用了虚拟线程，就不需要线程池和队列了”&lt;/li&gt;
&lt;li&gt;“所有生产者消费者问题都用 LinkedBlockingQueue 就行”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这篇文章的目标，就是把这些东西放到一张统一地图里。&lt;/p&gt;</description></item><item><title>Java引用类型详解：从源码到实战</title><link>https://zhouwy.top/posts/java%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B%E8%AF%A6%E8%A7%A3%E4%BB%8E%E6%BA%90%E7%A0%81%E5%88%B0%E5%AE%9E%E6%88%98/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/java%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B%E8%AF%A6%E8%A7%A3%E4%BB%8E%E6%BA%90%E7%A0%81%E5%88%B0%E5%AE%9E%E6%88%98/</guid><description>&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E7%9B%AE%E5%BD%95"&gt;目录&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%BC%95%E8%A8%80"&gt;引言&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%85%88%E6%90%9E%E6%B8%85%E6%A5%9A%E4%B8%80%E4%B8%AA%E5%89%8D%E6%8F%90%E5%8F%AF%E8%BE%BE%E6%80%A7%E5%88%86%E6%9E%90"&gt;先搞清楚一个前提：可达性分析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%BC%BA%E5%BC%95%E7%94%A8strong-reference"&gt;强引用（Strong Reference）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E8%BD%AF%E5%BC%95%E7%94%A8soft-reference"&gt;软引用（Soft Reference）&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95"&gt;基本用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%BA%90%E7%A0%81%E7%BA%A7%E5%88%86%E6%9E%90mybatis%E7%9A%84softcache"&gt;源码级分析：MyBatis的SoftCache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%AE%9E%E9%99%85%E5%BC%80%E5%8F%91%E7%94%A8%E8%BD%AF%E5%BC%95%E7%94%A8%E5%81%9A%E6%9C%AC%E5%9C%B0%E7%BC%93%E5%AD%98"&gt;实际开发：用软引用做本地缓存&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%BC%B1%E5%BC%95%E7%94%A8weak-reference"&gt;弱引用（Weak Reference）&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95-1"&gt;基本用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%BA%90%E7%A0%81%E7%BA%A7%E5%88%86%E6%9E%90threadlocal%E4%B8%BA%E4%BB%80%E4%B9%88%E7%94%A8%E5%BC%B1%E5%BC%95%E7%94%A8"&gt;源码级分析：ThreadLocal为什么用弱引用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%BA%90%E7%A0%81%E7%BA%A7%E5%88%86%E6%9E%90weakhashmap%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86"&gt;源码级分析：WeakHashMap的工作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%AE%9E%E9%99%85%E5%BC%80%E5%8F%91%E7%94%A8weakhashmap%E9%81%BF%E5%85%8D%E7%9B%91%E5%90%AC%E5%99%A8%E6%B3%84%E6%BC%8F"&gt;实际开发：用WeakHashMap避免监听器泄漏&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%AE%9E%E9%99%85%E5%BC%80%E5%8F%91tomcat%E7%9A%84concurrentcache"&gt;实际开发：Tomcat的ConcurrentCache&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E8%99%9A%E5%BC%95%E7%94%A8phantom-reference"&gt;虚引用（Phantom Reference）&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95-2"&gt;基本用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E8%99%9A%E5%BC%95%E7%94%A8-vs-finalize%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E7%94%A8%E8%99%9A%E5%BC%95%E7%94%A8"&gt;虚引用 vs finalize()：为什么要用虚引用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%BA%90%E7%A0%81%E7%BA%A7%E5%88%86%E6%9E%90jdk%E7%9A%84cleaner%E6%9C%BA%E5%88%B6"&gt;源码级分析：JDK的Cleaner机制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%BA%90%E7%A0%81%E7%BA%A7%E5%88%86%E6%9E%90directbytebuffer%E5%A6%82%E4%BD%95%E9%87%8A%E6%94%BE%E5%A0%86%E5%A4%96%E5%86%85%E5%AD%98"&gt;源码级分析：DirectByteBuffer如何释放堆外内存&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%AE%9E%E9%99%85%E5%BC%80%E5%8F%91%E7%94%A8%E8%99%9A%E5%BC%95%E7%94%A8%E7%9B%91%E6%8E%A7%E8%B5%84%E6%BA%90%E6%B3%84%E6%BC%8F"&gt;实际开发：用虚引用监控资源泄漏&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#referencequeue%E5%BC%95%E7%94%A8%E5%9B%9E%E6%94%B6%E7%9A%84%E9%80%9A%E7%9F%A5%E6%9C%BA%E5%88%B6"&gt;ReferenceQueue：引用回收的通知机制&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E6%A0%B8%E5%BF%83%E6%9C%BA%E5%88%B6"&gt;核心机制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%BA%90%E7%A0%81%E7%BA%A7%E5%88%86%E6%9E%90referencehandler%E7%BA%BF%E7%A8%8B"&gt;源码级分析：ReferenceHandler线程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%AE%9E%E9%99%85%E5%BC%80%E5%8F%91%E7%94%A8referencequeue%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%8A%A8%E6%B8%85%E7%90%86%E7%9A%84%E7%BC%93%E5%AD%98"&gt;实际开发：用ReferenceQueue实现自动清理的缓存&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E4%B8%80%E5%BC%A0%E5%9B%BE%E6%80%BB%E7%BB%93"&gt;一张图总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E8%A7%A3%E7%AD%94"&gt;常见问题与解答&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#q1threadlocal%E7%94%A8%E5%BC%B1%E5%BC%95%E7%94%A8%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%98%E4%BC%9A%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F"&gt;Q1：ThreadLocal用弱引用为什么还会内存泄漏？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#q2%E8%BD%AF%E5%BC%95%E7%94%A8%E5%92%8C%E5%BC%B1%E5%BC%95%E7%94%A8%E5%81%9A%E7%BC%93%E5%AD%98%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB"&gt;Q2：软引用和弱引用做缓存有什么区别？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#q3%E8%99%9A%E5%BC%95%E7%94%A8%E7%9A%84get%E4%B8%BA%E4%BB%80%E4%B9%88%E6%B0%B8%E8%BF%9C%E8%BF%94%E5%9B%9Enull"&gt;Q3：虚引用的get()为什么永远返回null？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#q4referencequeue%E7%9A%84poll%E5%92%8Cremove%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB"&gt;Q4：ReferenceQueue的poll和remove有什么区别？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#q5%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E8%AF%A5%E7%94%A8weakhashmap"&gt;Q5：什么时候该用WeakHashMap？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="引言"&gt;引言&lt;/h2&gt;
&lt;p&gt;提到Java的引用类型，很多人脑子里只有一句&amp;quot;强软弱虚&amp;quot;，但真到面试或者写代码的时候，往往一问就懵。这篇文章我打算换个思路——不搞概念罗列，直接从JDK源码和实际开发场景出发，把每种引用类型到底解决了什么问题、怎么用的，掰开了讲清楚。&lt;/p&gt;
&lt;h2 id="先搞清楚一个前提可达性分析"&gt;先搞清楚一个前提：可达性分析&lt;/h2&gt;
&lt;p&gt;在讲引用类型之前，得先明白JVM是怎么判断一个对象该不该回收的。JVM使用的是&lt;strong&gt;可达性分析算法&lt;/strong&gt;：从GC Roots出发，沿着引用链往下找，如果某个对象到GC Roots没有任何引用链可达，那这个对象就是可回收的。&lt;/p&gt;
&lt;p&gt;四种引用类型的区别，本质上就是：&lt;strong&gt;GC Roots到对象的这条引用链，有多&amp;quot;硬&amp;quot;&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;强引用：铁链子，GC死也不断&lt;/li&gt;
&lt;li&gt;软引用：橡皮筋，内存不够就断&lt;/li&gt;
&lt;li&gt;弱引用：纸糊的，GC一来就断&lt;/li&gt;
&lt;li&gt;虚引用：根本不算链子，只是个事后通知&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="强引用strong-reference"&gt;强引用（Strong Reference）&lt;/h2&gt;
&lt;p&gt;强引用没什么好说的，就是平时写的 &lt;code&gt;Object obj = new Object()&lt;/code&gt;。只要引用还在，GC宁可抛OOM也不会回收这个对象。&lt;/p&gt;
&lt;p&gt;但强引用有个容易踩的坑——&lt;strong&gt;长生命周期对象持有短生命周期对象的引用&lt;/strong&gt;。比如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;UserController&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 这是一个长生命周期的缓存，value是强引用&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; Map&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Long, User&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; userCache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; HashMap&lt;span style="color:#f92672"&gt;&amp;lt;&amp;gt;&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; User &lt;span style="color:#a6e22e"&gt;getUser&lt;/span&gt;(Long userId) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; userCache.&lt;span style="color:#a6e22e"&gt;computeIfAbsent&lt;/span&gt;(userId, id &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; loadFromDb(id));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个 &lt;code&gt;userCache&lt;/code&gt; 是 static 的，生命周期跟类一样长。放进去的 User 对象永远不会被回收，哪怕已经没有任何业务代码在使用它了。这就是典型的&lt;strong&gt;内存泄漏&lt;/strong&gt;——对象已经没用了，但因为强引用还在，GC回收不了。&lt;/p&gt;</description></item><item><title>线程池里用 InheritableThreadLocal 传值，怎么就丢了？</title><link>https://zhouwy.top/posts/inheritablethreadlocal-%E4%BC%A0%E5%80%BC%E4%B8%A2%E5%A4%B1%E9%97%AE%E9%A2%98/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/inheritablethreadlocal-%E4%BC%A0%E5%80%BC%E4%B8%A2%E5%A4%B1%E9%97%AE%E9%A2%98/</guid><description>&lt;h2 id="线程池里用-inheritablethreadlocal-传值怎么就丢了"&gt;线程池里用 InheritableThreadLocal 传值，怎么就丢了？&lt;/h2&gt;
&lt;p&gt;你肯定遇到过这种怪事：代码里明明用了 &lt;code&gt;InheritableThreadLocal&lt;/code&gt;，信心满满地觉得子线程能自动继承主线程的变量，结果一跑，值没了，或者串了。&lt;/p&gt;
&lt;p&gt;我第一次碰到这问题时， debug 了一下午，最后发现是线程池的锅。今天我就把当时挖出来的东西从头捋一遍，包括 Java 源码里到底是怎么拷贝的，还有阿里那个 &lt;code&gt;TransmittableThreadLocal&lt;/code&gt; 到底是怎么救场的。&lt;/p&gt;
&lt;h3 id="先看-inheritablethreadlocal-是怎么继承的"&gt;先看 InheritableThreadLocal 是怎么“继承”的&lt;/h3&gt;
&lt;p&gt;每个 Java 线程，也就是 &lt;code&gt;Thread&lt;/code&gt; 类的实例，肚子里都揣着两个 &lt;code&gt;ThreadLocalMap&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个叫 &lt;code&gt;threadLocals&lt;/code&gt;，专门给普通的 &lt;code&gt;ThreadLocal&lt;/code&gt; 存变量。&lt;/li&gt;
&lt;li&gt;另一个叫 &lt;code&gt;inheritableThreadLocals&lt;/code&gt;，给 &lt;code&gt;InheritableThreadLocal&lt;/code&gt; 用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这两个 map 平时都是懒加载的，你第一次 &lt;code&gt;set&lt;/code&gt; 的时候才会创建。&lt;/p&gt;
&lt;p&gt;当我们 &lt;code&gt;new&lt;/code&gt; 一个子线程的时候，事情就发生在 &lt;code&gt;Thread&lt;/code&gt; 的 &lt;code&gt;init()&lt;/code&gt; 方法里。这个方法会在子线程的构造函数中被调用，关键的一段逻辑是这样的（我摘了简化版的源码）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (parent.&lt;span style="color:#a6e22e"&gt;inheritableThreadLocals&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;inhertableThreadLocals&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ThreadLocal.&lt;span style="color:#a6e22e"&gt;createInheritedMap&lt;/span&gt;(parent.&lt;span style="color:#a6e22e"&gt;inheritableThreadLocals&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是说，如果父线程（也就是创建子线程的那个线程）的 &lt;code&gt;inheritableThreadLocals&lt;/code&gt; 里面有东西，就把整个 map 拷贝一份，作为子线程的 &lt;code&gt;inheritableThreadLocals&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;那 &lt;code&gt;createInheritedMap&lt;/code&gt; 是怎么拷贝的呢？它实际上是调用了 &lt;code&gt;ThreadLocalMap&lt;/code&gt; 的一个特殊构造方法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ThreadLocalMap&lt;/span&gt;(ThreadLocalMap parentMap) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ... 一些初始化代码 ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; 0; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; len; i&lt;span style="color:#f92672"&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Entry e &lt;span style="color:#f92672"&gt;=&lt;/span&gt; parentMap.&lt;span style="color:#a6e22e"&gt;table&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;i&lt;span style="color:#f92672"&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (e &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ThreadLocal&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (ThreadLocal&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;) e.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (key &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 注意这一行：调用了 childValue 方法&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Object value &lt;span style="color:#f92672"&gt;=&lt;/span&gt; key.&lt;span style="color:#a6e22e"&gt;childValue&lt;/span&gt;(e.&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Entry c &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Entry(key, value);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ... 把新 entry 放到子线程的 map 里 ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重点就是这个 &lt;code&gt;childValue&lt;/code&gt; 方法。它是 &lt;code&gt;InheritableThreadLocal&lt;/code&gt; 里定义的一个 protected 方法，默认实现就是直接返回传进来的值：&lt;/p&gt;</description></item></channel></rss>