{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "JavaGuide",
  "home_page_url": "https://javaguide.cn/",
  "feed_url": "https://javaguide.cn/feed.json",
  "description": "JavaGuide 是一份面向后端开发/后端面试的学习与复习指南，覆盖 Java、数据库/MySQL、Redis、分布式、高并发、高可用、系统设计等核心知识。",
  "favicon": "https://javaguide.cn/favicon.ico",
  "items": [
    {
      "title": "一文搞懂 Harness Engineering：六层架构、上下文管理与一线团队实战",
      "url": "https://javaguide.cn/ai/agent/harness-engineering.html",
      "id": "https://javaguide.cn/ai/agent/harness-engineering.html",
      "summary": "深度解析 Harness Engineering，梳理 Agent = Model + Harness 的核心定义，拆解 OpenAI、Anthropic、Stripe 等一线团队的实战经验与踩坑教训。",
      "content_html": "<p>最近大半年，很多开发者都有同感：明明用的是最贵的模型，Agent 跑起来还是各种拉胯——重复犯错、做到一半放弃、越跑越蠢。换了更强的模型，效果也没好到哪去。</p>\n<p>原因不在模型。<a href=\"http://Can.ac\" target=\"_blank\" rel=\"noopener noreferrer\">Can.ac</a> 做了个实验直接证明了这一点：同一个模型，只换了文件编辑接口的调用方式，编码基准分数从 6.7% 直接跳到 68.3%。模型没变，变的是外围的那套系统。</p>\n<p><strong>Harness Engineering</strong> 正在成为 AI Agent 开发圈的高频词。Mitchell Hashimoto 在博客里用了这个说法（他原话是“我不知道业界有没有公认的术语，我自己管这叫 harness engineering”），OpenAI 几天后发了一篇百万行代码的实验报告，Birgitta Böckeler 在 Martin Fowler 网站上写了深度分析，Anthropic 在三月份又放出了全新的多智能体架构设计。几周之内，Harness 成了讨论 AI Agent 开发绕不开的概念。</p>\n<p>今天这篇文章就来系统梳理 Harness Engineering 的核心概念和工程方法，帮你搞清楚：<strong>决定 Agent 表现的天花板，到底在哪里。</strong> 本文接近 1.3w 字，建议收藏，你将搞懂：</p>\n<ol>\n<li><strong>Harness 到底是什么</strong>：为什么说“你不是模型，那你就是 Harness”？Agent = Model + Harness 这个公式怎么理解？和 Prompt Engineering、Context Engineering 是什么关系？六层架构长什么样？</li>\n<li>⭐ <strong>为什么瓶颈不在模型而在 Harness</strong>：同一个模型只换了接口格式，分数从 6.7% 跳到 68.3%？上下文用到 40% Agent 就开始变蠢？</li>\n<li>⭐ <strong>从零搭建 Harness 的行动清单</strong>：P0/P1/P2 三个优先级，按需取用。</li>\n<li>⭐ <strong>一线团队实战案例</strong>（附录）：OpenAI 三人五月百万行零手写、Anthropic 的 GAN 式三智能体架构和 context resets 交接棒策略、Stripe 每周 1300+ 无人值守 PR、Mitchell Hashimoto 的六步进阶。</li>\n</ol>\n<blockquote>\n<p><strong>📌 系列阅读</strong>：本文是 AI Agent 系列的一部分，相关文章：</p>\n<ul>\n<li><a href=\"https://javaguide.cn/ai/agent/agent-basis.html\" target=\"_blank\" rel=\"noopener noreferrer\">AI Agent 核心概念：Agent Loop、Context Engineering、Tools 注册</a></li>\n<li><a href=\"https://javaguide.cn/ai/agent/skills.html\" target=\"_blank\" rel=\"noopener noreferrer\">Agent Skills 详解：是什么？怎么用？和 Prompt、MCP 有什么区别？</a></li>\n<li><a href=\"https://javaguide.cn/ai/agent/mcp.html\" target=\"_blank\" rel=\"noopener noreferrer\">万字拆解 MCP，附带工程实践</a></li>\n</ul>\n</blockquote>\n<h2>⭐️ Harness 核心概念</h2>\n<h3>Harness 到底是什么？</h3>\n<p>一句话：<strong>Agent = Model + Harness。你不是模型，那你就是 Harness。</strong></p>\n<p>听起来有点绝对？但仔细想想，它确实抓住了关键。</p>\n<p><strong>Harness 就是模型之外的一切</strong>——系统提示词、工具调用、文件系统、沙箱环境、编排逻辑、钩子中间件、反馈回路、约束机制。模型本身只是能力的来源，只有通过 Harness 把状态、工具、反馈和约束串起来，它才真正变成一个 Agent。</p>\n<p>LangChain 的 Vivek Trivedi 在《The Anatomy of an Agent Harness》里把这个定义讲得很清楚：<strong>先搞清楚模型负责什么，剩下的系统要补什么，用这条线把整个系统切开。</strong></p>\n<p>打个比方：模型是 CPU，Harness 是操作系统。CPU 再强，OS 拉胯也白搭。你买了最新款 M5 芯片，装了个崩溃不断的系统，体验还不如老芯片配稳定的 OS。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/harness/harness-agent-equals-model-harness-arch.png\" alt=\"Agent = Model + Harness\"></p>\n<h3>Harness 和 Prompt/Context Engineering 是什么关系？</h3>\n<p>三者不是并列关系，而是嵌套关系。更重要的是，<strong>每一层解决的是完全不同的问题</strong>：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/harness/harness-engineering-layers-arch.png\" alt=\"Harness 和 Prompt/Context Engineering 的关系\"></p>\n<p>| 层级                    | 解决的核心问题                                 | 关注点                                       | 典型工作                                   |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/ai/harness/harness-agent-equals-model-harness-arch.png",
      "date_published": "2026-04-08T16:06:36.000Z",
      "date_modified": "2026-04-10T08:31:20.000Z",
      "authors": [],
      "tags": [
        "AI 应用开发"
      ]
    },
    {
      "title": "Claude Code 接入第三方模型实战：JVM 智能诊断与慢查询治理",
      "url": "https://javaguide.cn/ai/ai-coding/cc-glm5.1.html",
      "id": "https://javaguide.cn/ai/ai-coding/cc-glm5.1.html",
      "summary": "通过 Claude Code 接入 GLM-5.1 模型，完成 JVM 智能诊断助手从零搭建和百万级数据量慢查询治理两个实战任务，分享 AI 辅助编程的工作方法与踩坑经验。",
      "content_html": "<p>大家好，我是 Guide。前面分享过 <a href=\"/ai/ai-coding/idea-qoder-plugin.html\" target=\"_blank\">IDEA 搭配 Qoder 插件的实战</a>和 <a href=\"/ai/ai-coding/trae-m2.7.html\" target=\"_blank\">Trae 接入大模型的实战</a>，分别覆盖了 JetBrains 体系和 VS Code 体系下的 AI 辅助编码。这篇换个角度，聊聊 <strong>Claude Code 接入第三方模型</strong> 的实战体验。</p>\n<p>Claude Code 本身是 Anthropic 官方的 CLI 编码工具，但它支持通过环境变量切换底层模型。这意味着你不必局限于 Claude 系列，完全可以接入其他模型来使用。本文以 GLM-5.1 作为示例，但接入方式是通用的——换成其他兼容模型，流程基本一致。</p>\n<p>我选了两个比较有代表性的复杂场景来验证：</p>\n<ul>\n<li><strong>场景一</strong>：从零搭建一个基于 Arthas 的 JVM 智能诊断 Agent，涵盖技术选型、架构设计、编码落地的完整流程</li>\n<li><strong>场景二</strong>：在百万级数据量的既有订单系统中定位并治理慢查询，考验 AI 对现有代码库的理解和增量优化能力</li>\n</ul>\n<p>一个是从零开始的工程交付，另一个是面对既有系统的性能治理，正好覆盖 AI 辅助编程的两种典型工作模式。</p>\n<h2>环境准备：Claude Code 接入第三方模型</h2>\n<p>在正式开始之前，需要完成 Claude Code 与第三方模型的对接。整个配置过程分三步：</p>\n<p><strong>第一步</strong>：安装 Claude Code</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">npm</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> i</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> -g</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> @anthropic-ai/claude-code@latest</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p><strong>第二步</strong>：安装 cc-switch 完成模型切换（macOS 用户可通过 homebrew 安装，详情参考 cc-switch 官方文档：<a href=\"https://github.com/farion1231/cc-switch/blob/main/README_ZH.md\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/farion1231/cc-switch/blob/main/README_ZH.md</a>）</p>\n<p><strong>第三步</strong>：按照模型提供方的说明，完成 Claude Code 内部模型环境变量与目标模型的对应关系配置。以 GLM-5.1 为例，参考：<a href=\"https://docs.bigmodel.cn/cn/coding-plan/tool/claude\" target=\"_blank\" rel=\"noopener noreferrer\">https://docs.bigmodel.cn/cn/coding-plan/tool/claude</a></p>\n<p>配置过程截图如下：</p>\n<p>点击加号添加模型：</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/add-model-entry.png\" alt=\"点击添加模型\"></p>\n<p>选择对应的模型：</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/select-model.png\" alt=\"选择模型\"></p>\n<p>配置参数：</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/config-params.png\" alt=\"配置参数\"></p>\n<p>Claude Code 内部模型环境变量与目标模型对应关系的 JSON 配置：</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/model-env-json-config.png\" alt=\"Claude Code 内部模型环境变量与模型对应关系 JSON 配置\"></p>\n<p>如果你更偏向页面开发，推荐通过 VSCode + Claude Code for VS Code 方式进行交互和编码验收。完成插件安装之后，可以直接在 IDE 中与模型对话和代码审查，相对于 CLI 界面会更直观一些：</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/vscode-claude-code.png\" alt=\"VSCode + Claude Code for VS Code\"></p>\n<h2>场景一：从零搭建 JVM 智能诊断 Agent</h2>\n<h3>为什么需要 JVM 智能诊断助手？</h3>\n<p>JVM 线上诊断一直以来都是 Java 开发最棘手的问题。在传统开发模式下，面对性能瓶颈或线上故障，研发人员的排查路径基本固定：</p>\n<ol>\n<li>查看 Grafana 监控面板，初步定位异常方向</li>\n<li>登录线上服务器，排查 CPU、内存、GC 等各项指标</li>\n<li>明确 Java 应用层面的问题后，启动 Arthas 执行一系列诊断指令，逐步缩小问题范围</li>\n<li>定位到具体代码段，分析根因并制定修复方案</li>\n</ol>\n<p>在 AI 出现以前，这套流程虽然繁琐，但确实是最直接有效的手段。但随着业务越来越复杂，故障响应时效要求也越来越高，传统模式的弊端越来越明显：</p>\n<ul>\n<li><strong>监控指标过于主观</strong>：面对 CPU 飙升、内存泄漏、OOM 等千奇百怪的问题，监控面板上的指标繁多，研发人员往往依赖经验做主观推断，缺乏系统化的诊断方法论</li>\n<li><strong>诊断链路过于冗长</strong>：从 Grafana 面板到线上服务器再到 Arthas 诊断，整个排查链路涉及多个工具的切换和衔接，不仅耗时，对于紧急的线上故障止血来说显得非常低效</li>\n<li><strong>高度依赖工程师经验</strong>：Arthas 确实是一款强大的 JVM 诊断利器，内置各种增强指令可以深入字节码查看运行时细节。但代价是开发人员必须熟悉各种指令参数和推理路径，才能准确完成问题定位</li>\n</ul>\n<p>随着 AI 技术的演进，特别是 Agent 和 Skill 等概念的成熟，笔者就有了一个工程化的构想：能否借助 AI 将诊断经验沉淀复用，让 AI 根据既有经验构建明确的决策路径？同时结合它的决策方案赋予对应的工具，使其基于用户给定的服务名和故障表象，自动化连接线上服务器完成诊断，定位具体代码段，最终输出问题根因和解决方案。</p>\n<h3>需求交付与架构设计</h3>\n<p>有了构想之后，接下来就是技术选型和方案落地。笔者将完整的需求描述交给 AI：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">研发一款基于Arthas的智能体诊断工具，该工具需实现以下核心功能：</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">1.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 当用户输入线上故障服务名称及具体故障现象后，系统能够自动定位至目标故障服务器，主动对目标服务进行实时监控与深度分析。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">2.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 通过集成Arthas的反编译功能，精准定位到引发故障的具体代码段</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">3.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 基于分析结果生成包含问题根因、代码修复建议及实施步骤的完整解决思路。</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">请提供该工具的技术选型方案，包括但不限于开发语言（优先考虑Java技术栈）、核心框架、数据库表设计、部署架构等，并设计详细的系统实现方案，涵盖功能模块划分、数据流程设计、关键技术难点及解决方案等内容。</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>AI 收到需求后，没有立刻开始写代码，而是先结合项目上下文（完全空的文件夹）进行推理分析，自主完成了一份包含十几个阶段的完整技术方案。”给一个目标，AI 自己拆出整条路径”——这是 AI 辅助编程的一大优势，你可以把精力放在需求描述和方案评审上，让 AI 负责路径规划。</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/ai-tech-plan.png\" alt=\"AI 自主完成技术方案规划\"></p>\n<p>AI 结合需求，针对 Agent 拆解出技术选型和 Arthas 集成方案的检索。从检索关键字可以看出，它在方案选取上优先考虑成熟稳定的解决方案：</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/agent-arthas-integration-research.png\" alt=\"AI 检索 Agent 技术选型和 Arthas 集成方案\"></p>\n<p>AI 检索了大量资料和 Arthas 官方文档后，输出了下面这份系统架构设计图。从上到下分三层：用户层输入服务名和故障现象，Agent 层由 Skill 引擎、Arthas HTTP Client 和 AI 分析引擎三大核心模块协同工作，最底层通过 Arthas 内置 HTTP API 对接多个目标服务实例。架构的模块划分和职责边界清晰，从故障输入到定位代码再到生成报告的完整链路设计到位：</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/system-architecture-design.png\" alt=\"AI 输出的系统架构设计图\"></p>\n<p>AI 给出了架构图之后，还进一步拆解了 6 个核心组件的职责分工——从 AI Agent Server 的流程编排，到 Arthas HTTP Client 的会话管理，到 Skill 引擎的诊断步骤链定义，再到 AI 分析引擎的报告生成，每个组件的边界和协作关系都交代得比较清楚：</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/core-component-roles.png\" alt=\"AI 输出的核心角色分工表\"></p>\n<p>最后来看最重要的数据流设计。架构设计明确之后，只要数据流链路完整清晰，基本就可以着手开发了。AI 结合一个常见的 RT 超时场景，给出了完整的诊断链路——从 Skill 匹配、诊断步骤执行、问题追踪、根因定位，到 Arthas 反编译和最终的诊断报告输出。AI 针对 Arthas HTTP API 设计了完整的会话模式交互流程（init_session → async_exec → pull_results → interrupt_job → close_session），连<code>watch</code>、<code>trace</code>这类持续监听型命令的异步轮询机制都考虑到了。这一点在评审时需要重点关注——如果 AI 对底层工具的通信模型理解有偏差，后续编码阶段就会出现问题：</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/data-flow-design.png\" alt=\"AI 输出的数据流设计\"></p>\n<p>其他细节就不多做赘述了。整体来说，架构和数据流链路都比较到位。AI 不仅针对既有需求给出了方案，还主动输出了 6 个后续扩展方向——WebSocket 实时推送、诊断知识库向量化存储、已知 Pattern 的自动修复补丁、告警联动自动触发诊断、自定义 Skill 市场、多语言支持。这些扩展方向都紧扣当前架构的技术延伸：知识库基于现有的诊断报告数据，自动修复基于已有的 Skill 引擎，告警联动基于现有的服务实例查询机制。</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/extension-suggestions.png\" alt=\"AI 给出的后续扩展建议\"></p>\n<h3>编码交付与工程结构</h3>\n<p>确认方案没有问题后，笔者直接下达开发指令：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">整体方案没有问题，请完成开发工作吧</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>AI 收到指令后，开始自主编码。按照之前的架构设计，逐模块推进——从父 POM 和 Maven 多模块骨架搭建，到通用工具类、数据模型、数据访问层、Arthas 客户端封装、Skill 引擎、AI 分析引擎、业务逻辑层、Web 控制器，直到启动模块和部署配置，11 个子步骤全部完成：</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/ai-coding-process.png\" alt=\"AI 自主编码过程\"></p>\n<p>片刻之后，AI 完成了全部编码工作，并输出了一份详细的交付清单。9 个模块、46 个文件全部到位——从通用工具类到 7 个内置诊断 Skill，从 Arthas HTTP API 的 exec+session 双模式封装到 Spring AI Alibaba 诊断分析器，一个不少：</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/delivery-checklist.png\" alt=\"AI 完成编码后输出的交付清单\"></p>\n<p>先看整体模块结构，AI 按照 Java 多模块的标准规范完成了工程划分，从上到下严格遵循 common→model→dal→client→skill→ai→service→web→bootstrap 的依赖层级，命名规范统一。</p>\n<p>agent-skill 模块值得关注，AI 设计了 Skill 引擎的抽象接口，并内置了 7 个覆盖常见 JVM 故障场景的诊断技能（CPU 飙高、OOM、死锁、慢接口、GC 异常、线程泄漏、类找不到），每个 Skill 都定义了完整的诊断步骤链。这种”框架 + 内置实现”的设计思路，扩展性不错：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">jvm-ai-agent/</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">├──</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> jvm-ai-agent-server/</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                 # 智能体服务端（核心）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">│</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">   ├──</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> agent-common/</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                    # 通用模块：工具类、常量、DTO</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">│</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">   ├──</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> agent-model/</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                     # 数据模型：实体、数据库映射</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">│</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">   ├──</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> agent-dal/</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                       # 数据访问层：Mapper、Repository</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">│</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">   ├──</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> agent-arthas-client/</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">             # Arthas HTTP API 客户端封装</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">│</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">   ├──</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> agent-skill/</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                     # Skill 引擎（诊断方法论）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">│</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">   ├──</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> agent-ai/</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                        # AI 分析引擎</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">│</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">   ├──</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> agent-service/</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                   # 业务逻辑层（含服务实例查询）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">│</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">   ├──</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> agent-web/</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                       # Web 层：REST API、WebSocket</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">│</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">   └──</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> agent-server-bootstrap/</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">          # 启动模块</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">│</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">└──</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> pom.xml</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                              # 父 POM</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>再看诊断核心逻辑，AI 严格按照架构设计中定义的数据流完成了完整的诊断业务链开发。整个 <code>executeDiagnosis</code> 方法按照 Skill 匹配、实例定位、诊断链执行、动态命令解析、AI 分析、报告生成的流程推进，异常处理也考虑到了非关键步骤失败时继续执行的容错策略：</p>\n<ol>\n<li><strong>Skill 匹配</strong>：通过<code>DefaultSkillMatcher</code>根据故障现象关键词匹配最佳诊断技能</li>\n<li><strong>实例定位</strong>：通过<code>ServiceInstanceLocator</code>根据服务名解析目标实例 IP 和 Arthas 端口</li>\n<li><strong>诊断链执行</strong>：遍历 Skill 定义的诊断步骤链，依次执行 Arthas 命令并收集结果</li>\n<li><strong>动态命令解析</strong>：从 Arthas 输出中提取类名、方法名等上下文变量，注入后续步骤的动态命令模板</li>\n<li><strong>AI 分析报告</strong>：将全部诊断数据交给 AI 分析引擎，生成包含根因、修复建议、严重程度的结构化报告</li>\n</ol>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> executeDiagnosis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">DiagnosisRecord</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> record</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> DiagnosisRequest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> request) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 1. 匹配 Skill</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Optional</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">SkillDefinition</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> skillOpt </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> skillMatcher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">findBestMatch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">request</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getSymptom</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">skillOpt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isEmpty</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">            failDiagnosis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(record</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"无法匹配到合适的诊断技能\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        SkillDefinition</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> skill </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> skillOpt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ......</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 2. 定位目标实例</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        ServiceRegistry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> instance </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> instanceLocator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">resolveInstance</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                request</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getServiceName</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(), </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">request</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getInstanceIp</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ......</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 3. 执行诊断步骤链</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        List</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">DiagnosticStep</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> chain </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> skill</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getDiagnosticChain</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        StringBuilder</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> allDiagnosticData </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> StringBuilder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> decompiledCode </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Map</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> contextVars </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> HashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> chain</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            DiagnosticStep</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> step </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> chain</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // ...... 初始化步骤实体</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                // 解析动态命令（支持上下文变量注入）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">                String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> command </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> resolveCommand</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(step</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> contextVars)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                // ......</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                // 执行Arthas命令并记录耗时</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">                String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> result </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> executeStep</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(host</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> port</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> step</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> command)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                // 如果是 jad 结果，记录为反编译代码</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"jad\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">equals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">step</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getResultType</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">())</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                    decompiledCode </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> result</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                // 从结果中提取上下文变量供后续步骤使用</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">                extractContextVars</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(result</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> contextVars)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Exception</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                // 非关键步骤失败时继续执行</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                // ......</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 4. AI 分析</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> report </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> diagnosisAnalyzer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">analyze</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                request</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getSymptom</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(), </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">allDiagnosticData</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(), decompiledCode, skill);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 5. 保存报告（从Markdown报告中提取根因、严重程度等结构化字段）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ......</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 6. 更新诊断记录状态</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        record</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setStatus</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">DiagnosisStatus</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">COMPLETED</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getCode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ......</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Exception</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        failDiagnosis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(record</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getMessage</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>Agent 交互页面集成</h3>\n<p>在 AI 编码期间，笔者查阅了 Spring AI Alibaba 的官方文档，发现它提供了现成的 Agent Chat UI。与其让 AI 从头生成前端页面，不如直接集成这个交互组件，实现 SSE 流式输出的诊断体验。于是笔者给了一条简短的指令：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">根据Spring</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> AI</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Alibaba官方文档（参考链接https://java2ai.com/docs/frameworks/studio/quick-start：），实现agent智能体交互页面开发工作</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>只给了一个文档链接和一句话，AI 就自己去读官方文档、理解集成步骤、完成了页面开发。这也是使用 AI 辅助编程的一个实用技巧：当你只需要集成某个现成组件时，直接给出文档链接往往比详细描述需求更高效。</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/agent-chat-ui-integration.png\" alt=\"AI 完成 Agent Chat UI 页面集成\"></p>\n<p>到这里，一个完整的智能诊断 Agent 就构建完成了。为了验收功能，笔者在本地起了一个 CPU 飙升的测试接口：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Slf4j</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">RestController</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> TestController</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">RequestMapping</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"cpu-100\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> cpu</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">){</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>启动 Agent 服务，访问 <code>http://localhost:{应用端口}/chatui/index.html</code>，在聊天框输入：<code>order-service 程序CPU飙升,请协助排查</code>。Agent 在收到故障表象后，完成了完整的诊断链路——先通过 Dashboard 获取概览定位到 CPU 占用最高的线程 ID，再基于线程栈帧信息定位到问题代码段，最后通过 Arthas 反编译（jad）输出热点代码并生成包含根因分析和修复建议的完整诊断报告。整个过程 Agent 全程自主完成，SSE 流式输出让每一步诊断进度都清晰可见：</p>\n<p><img src=\"https://oss.javaguide.cn/ai/coding/glm5.1-cc/agent-diagnosis-demo.png\" alt=\"Agent 诊断效果演示\"></p>\n<h2>场景二：百万级数据量下的慢查询治理</h2>\n<p>场景一验证的是 AI”从 0 到 1 的规划与交付能力”，那场景二要验证的就是另一个维度：<strong>在一个已有一定复杂度的代码库中，AI 能否准确理解既有架构、定位问题、并完成增量优化。</strong></p>\n<h3>问题定位：搜索接口耗时 18 秒</h3>\n<p>这是一个基于 Spring Boot + MyBatis 的订单查询服务（glm-testing-service），核心业务围绕订单的查询和分析展开，包含四个接口：</p>\n<p>| 接口         | 路径                           | 说明                                 |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/ai/coding/glm5.1-cc/add-model-entry.png",
      "date_published": "2026-04-08T15:41:57.000Z",
      "date_modified": "2026-04-10T08:31:20.000Z",
      "authors": [],
      "tags": [
        "AI 编程实战"
      ]
    },
    {
      "title": "IDEA + Qoder 插件多场景实战：接口优化与代码重构",
      "url": "https://javaguide.cn/ai/ai-coding/idea-qoder-plugin.html",
      "id": "https://javaguide.cn/ai/ai-coding/idea-qoder-plugin.html",
      "summary": "通过两个真实实战案例，展示 IDEA 搭配 Qoder 插件在深分页优化、祖传代码重构等场景下的实际效果，分享从执行者到指挥者的工作模式转变。",
      "content_html": "<p>大家好，我是 Guide。如果你是 JetBrains IDE 的重度用户，大概率有过这样的纠结：想用 AI 辅助编程，但主流工具——Cursor、Trae、Qoder——大多基于 VS Code。切过去？舍不得 JetBrains 调试和重构体验。不切？又感觉错过了 AI 的效率红利。</p>\n<p>有朋友会说：Claude Code、Gemini CLI 这些终端工具不是挺香的吗？确实香，但说实话，CLI 模式也有明显的短板：没有原生 UI 交互，看代码、审 diff 都不够直观。虽然可以通过一些开源项目（如 vibe kanban、1Code）来缓解，但在做复杂项目时，还是存在一些局限性。</p>\n<p>现在的后端开发者，大致分成了四大阵营：</p>\n<p>| 阵营           | 工具组合                                        | 特点                         |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/ai/coding/qoder/idea-plugin/plugin-install-interface.png",
      "date_published": "2026-03-29T06:07:23.000Z",
      "date_modified": "2026-04-10T08:31:20.000Z",
      "authors": [],
      "tags": [
        "AI 编程实战"
      ]
    },
    {
      "title": "Trae + MiniMax 多场景实战：Redis 故障排查与跨语言重构",
      "url": "https://javaguide.cn/ai/ai-coding/trae-m2.7.html",
      "id": "https://javaguide.cn/ai/ai-coding/trae-m2.7.html",
      "summary": "使用 Trae IDE 接入 MiniMax 大模型，通过 Redis 连接池故障排查和 Redis C 源码到 Go 跨语言重构两个真实场景，分享 AI 辅助编程的实战经验与工作技巧。",
      "content_html": "<p>大家好，我是 Guide。前面分享过一篇 <a href=\"/ai/ai-coding/idea-qoder-plugin.html\" target=\"_blank\">IDEA 搭配 Qoder 插件的实战</a>，那篇主要讲在 JetBrains 体系内用 AI 辅助编码。这篇换个角度，聊聊 <strong>Trae IDE 接入大模型</strong> 的实战体验。</p>\n<p>Trae 是字节跳动推出的 AI 编程 IDE，基于 VS Code 生态，支持接入多种大模型。本文使用 MiniMax M2.7 作为示例，但 Trae 的接入方式是通用的——换成 Claude、GPT 等其他模型，流程基本一致。</p>\n<p>我这里使用 MiniMax 是因为我刚好订阅了 MiniMax Code Plan 想要实际测试一些，并非广告，你可以换成其他模型，思路都是一样的。</p>\n<p>我选了两个比较有代表性的复杂场景来实际验证：</p>\n<ul>\n<li><strong>场景一</strong>：接口突然大量超时，日志只指向 Redis，但项目里多处都在用 Redis，很难快速定位根因。</li>\n<li><strong>场景二</strong>：把 Redis 的慢查询指令从 C 语言源码完整复刻到 Go 实现，考验跨语言重构和上下文理解能力。</li>\n</ul>\n<h2>快速上手：Trae 接入大模型</h2>\n<p>Trae 支持接入多种大模型，下面以接入自定义模型为例，演示通用配置流程。</p>\n<p><strong>第一步</strong>：到 Trae 官网下载安装并完成初始化，同时到对应模型平台完成注册和 API Key 创建（本文示例使用 MiniMax 平台）：</p>\n<p><a href=\"https://platform.minimaxi.com/subscribe/token-plan\" target=\"_blank\" rel=\"noopener noreferrer\">https://platform.minimaxi.com/subscribe/token-plan</a></p>\n<p><strong>第二步</strong>：在 Trae 中点击&quot;Add Model&quot;添加自定义模型：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/trae-add-model-entry.png\" alt=\"Trae添加模型入口\"></p>\n<p><strong>第三步</strong>：选择&quot;Other Models&quot;并手动输入模型 ID 和 API Key：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/select-other-models.png\" alt=\"选择Other Models\"></p>\n<p><strong>第四步</strong>：输入模型 ID（如 <code>MiniMax-M2.7</code>）和申请的 API Key，点击&quot;Add Model&quot;。若无报错提示，即表示接入成功：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/input-minimax-m2.7-api-key.png\" alt=\"输入模型ID和API Key\"></p>\n<p>接入完成后，就可以在 Trae 中使用该模型进行 AI 辅助编程了。接下来通过两个实战场景，分享具体的使用方式和技巧。</p>\n<h2>场景一：接口超时问题快速止血与根因定位</h2>\n<h3>问题定位</h3>\n<p>第一个案例是某次真实线上故障的复现（已脱敏）。当时部门同学反馈某列表查询接口报错，页面无数据。线上监控系统定位到接口信息如下：</p>\n<p>接口：<code>GET http://localhost:8080/api/rbac/user/list</code></p>\n<p>返回结果：</p>\n<div class=\"language- line-numbers-mode\" data-highlighter=\"shiki\" data-ext style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-\"><span class=\"line\"><span>{</span></span>\n<span class=\"line\"><span>    \"code\": 500,</span></span>\n<span class=\"line\"><span>    \"message\": \"系统繁忙，请稍后重试\",</span></span>\n<span class=\"line\"><span>    \"data\": null,</span></span>\n<span class=\"line\"><span>    \"timestamp\": \"2026-03-19T10:11:02.632242\"</span></span>\n<span class=\"line\"><span>}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>结合异常堆栈信息关键字<code>Read timed out</code>，以及对应代码段的<code>get(key)</code>操作，我们可以初步认为该报错只是表象并非根因。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getConfigValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> configKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> environment) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> cacheKey </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> CONFIG_CACHE_PREFIX </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> configKey </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \":\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> environment</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> value </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> stringRedisTemplate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">opsForValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(cacheKey);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (value </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 后续逻辑省略</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>按照常规处理流程，我们需要快速定位问题根因、完成止血，再联系运维深入排查。但项目中多处用到Redis，逐一排查耗时长，期间可能影响业务稳定性。</p>\n<p>为了验证 AI 辅助排查的实际效果，笔者复刻了该故障场景（已脱敏），让模型接手处理。按照企业级线上故障处理流程，首先需要定位根因并完成止血。于是向模型下达了第一条指令：</p>\n<div class=\"language- line-numbers-mode\" data-highlighter=\"shiki\" data-ext style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-\"><span class=\"line\"><span>针对访问 http://localhost:8080/api/rbac/user/list 接口时出现的500错误（错误信息：\"系统繁忙，请稍后重试\"），请执行以下操作：</span></span>\n<span class=\"line\"><span>1. 分析提供的异常堆栈信息，准确定位导致服务器内部错误的根本原因；</span></span>\n<span class=\"line\"><span>2. 提供详细的线上紧急止血方案，包括但不限于：临时回滚策略、流量限制措施、服务降级方案或紧急重启流程；</span></span>\n<span class=\"line\"><span>3. 解释错误产生的技术原因，指出具体的代码模块或配置问题；</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>...... 异常堆栈关键信息：`java.net.SocketTimeoutException: Read timed out`</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-diagnostic-instruction.png\" alt=\"向M2.7下达的诊断指令截图\"></p>\n<p>模型收到请求后，很快定位到指定代码的上下文，并推理出4种可能的根因：</p>\n<ul>\n<li>Redis 服务器宕机或无响应</li>\n<li>连接池配置太小，高并发下耗尽</li>\n<li>Redis 连接泄漏（连接未正确关闭）</li>\n<li>Redis 服务器负载过高</li>\n</ul>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-inference-result.png\" alt=\"M2.7推理结果截图\"></p>\n<p>到这一步，模型已经把问题空间从&quot;N处Redis调用&quot;压缩到了&quot;4种可能根因&quot;——这种<strong>快速收敛问题范围</strong>的能力，是 AI 辅助排查的核心价值。接下来看它的止血思路。</p>\n<h3>止血</h3>\n<p>模型针对既定异常栈帧快速梳理了代码调用逻辑，准确地指出：列表查询接口被切面拦截，连接池耗尽是500错误的根因。另外一个关键点，它指出了这段代码缺乏降级策略——这一点笔者是在复盘会上才意识到的。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-call-chain-analysis.png\" alt=\"M2.7代码调用链路分析截图\"></p>\n<p>针对线上问题，止血策略是最关键的环节。模型给出了几个解决方案，第一个就是临时关闭权限校验开关——原因在于方案一需要清除Redis缓存数据。虽然方案有些激进，不过，它详细指出了代码的调用链路和表结构信息，这也能很好地辅助我通过业务语义猜测可能的场景和原因。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-call-chain-analysis-2.png\" alt=\"M2.7调用链路分析\"></p>\n<p>基于模型提供的调用链路信息，笔者进一步询问方案一的技术依据，确保业务理解上快速对齐：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">结合代码开发的完整工作流程，详细阐述方案一的技术依据、设计思路及实施合理性。</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>这也是让笔者比较满意的地方，模型给出了问题代码的调用链路图，让我快速了解到列表查询期间所经过的完整切面和具体故障所处位置，帮助理解当前问题的影响面以及本次异常的直接原因。</p>\n<p>经过不到10分钟的交互，笔者不仅迅速获得一个宏观的架构视角，理解了当前复杂架构的故障和各解决方案的依据，例如方案一：通过修改数据库配置重启刷新缓存来规避权限校验。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-call-chain-diagram.png\" alt=\"M2.7调用链路图截图\"></p>\n<p>我们再来看看方案三的思路：当Redis不可用时，使用本地缓存或默认值，避免级联失败。模型结合当前工程代码段给出了修改建议：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-solution-3-code.png\" alt=\"M2.7方案三代码片段\"></p>\n<p>模型分析后，我们对问题有了初步的判断：Redis客户端连接池耗尽，导致日常业务接口基于缓存开关查询逻辑崩溃，进而引发雪崩效应。综合模型的多个建议，本着保守、快速止血、业务高峰期不压垮数据库的原则，得出以下hotfix方案：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">根据提供的方案，创建一个hotfix止血分支，用于紧急修复Redis异常问题。具体实施步骤如下：</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">1.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 基于当前生产环境代码创建hotfix分支，命名规范为\"hotfix/redis-exception-handler\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">2.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 按照方案三实现Redis异常捕获机制，在所有Redis操作处添加try-catch块</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">3.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 当捕获到Redis异常时，自动降级为直接查询数据库获取数据</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">4.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 实现JVM本地缓存机制，将查询结果缓存至内存中，设置合理的缓存过期时间</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">5.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 完成单元测试和集成测试，覆盖率需达到80%以上</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">6.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 准备回滚方案，确保在紧急情况下能够快速恢复到上一版本</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/hotfix-instruction.png\" alt=\"hotfix方案指令\"></p>\n<p>模型收到指令后，准确理解了问题，完成任务拆解并逐步执行：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-task-breakdown.png\" alt=\"M2.7任务拆解过程\"></p>\n<p>最终输出的代码结果如下：模型在原有权限校验逻辑中整合了数据库降级查询，对权限校验逻辑的理解和复杂设计的整合做得比较到位。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Around</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"permissionCheck()\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> checkPermission</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ProceedingJoinPoint</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> joinPoint) throws Throwable {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 从配置中心读取权限校验开关</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> checkEnabled </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> configService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getConfigValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"permission.check.enabled\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"PROD\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"true\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">equalsIgnoreCase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(checkEnabled)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> joinPoint</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">proceed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ... 原有权限校验逻辑 ...</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 尝试从Redis缓存获取权限信息</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Boolean</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> hasPermission </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> checkPermissionFromCache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(redisKey)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (hasPermission </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // ... 命中缓存处理 ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 降级：从数据库查询权限</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        boolean</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> hasPermissionFromDB </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> checkPermissionFromDatabase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(userId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> apiPath</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> httpMethod)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ... 降级逻辑处理 ...</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Exception</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (e </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> RuntimeException </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x26;&#x26;</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"无权限访问\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">equals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getMessage</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">())</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            throw</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 发生异常时，触发监控告警并采用保守策略放行</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        AlertManager</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">notify</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"PERMISSION_CHECK_ERROR\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getMessage</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> joinPoint</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">proceed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>getConfigValue同样补充了本地缓存逻辑，多级缓存设计在容错处理上做得不错。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 获取配置值（指定环境）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getConfigValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> configKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> environment) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> cacheKey </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> CONFIG_CACHE_PREFIX </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> configKey </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \":\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> environment</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 【第一步：尝试从本地缓存获取】</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> localValue </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> localCacheManager</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(cacheKey);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (localValue </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> localValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 【第二步：尝试从Redis获取】</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isRedisAvailable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> value </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> stringRedisTemplate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">opsForValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(cacheKey);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (value </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                localCacheManager</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(cacheKey, value, LOCAL_CACHE_TTL);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Exception</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // Redis异常，降级到数据库</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        handleRedisFailure</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 【第三步：降级到数据库】</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // ... 其他逻辑 ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getConfigValueFromDatabaseWithFallback</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(configKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> environment)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这其中值得注意的一个细节是本地缓存的设计：模型采用开闭原则，基于ConcurrentHashMap完成了本地缓存工具类的封装，考虑到了堆内存溢出风险，配合LRU算法实现缓存清理：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Component</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LocalCacheManager</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 核心存储：ConcurrentHashMap保证线程安全</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Map</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CacheEntry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> cache </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ConcurrentHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ScheduledExecutorService</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> cleanupExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 缓存配置</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> DEFAULT_TTL_MILLIS </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 300000</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 5分钟</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> MAX_CACHE_SIZE </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 10000</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> LocalCacheManager</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 守护线程执行定时清理</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">cleanupExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Executors</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newSingleThreadScheduledExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(r </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> t</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(r, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"local-cache-cleanup\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            t</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setDaemon</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> t;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        });</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">cleanupExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">scheduleAtFixedRate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">::</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">cleanupExpiredEntries, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">MINUTES</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(key, value, DEFAULT_TTL_MILLIS);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ttlMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 容量满时触发LRU清理</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">cache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> MAX_CACHE_SIZE) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">            cleanupExpiredEntries</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">cache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> MAX_CACHE_SIZE) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">                evictOldestHalf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        cache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(key, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> CacheEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(value, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ttlMillis));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        CacheEntry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> cache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(key);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (entry </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ||</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isExpired</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            cache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">remove</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(key);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // ... 其他方法省略 ...</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // LRU清理：删除最老的50%数据</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> evictOldestHalf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ...... 省略排序和清理逻辑 ......</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 缓存条目</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CacheEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> expirationTime</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> CacheEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> expirationTime</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> value;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">expirationTime</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> expirationTime;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> value;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> isExpired</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> expirationTime;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>根因定位</h3>\n<p>通过hotfix分支针对线上故障止血之后，我们再来深入排查Redis连接池耗尽的原因。按照模型的输出结果和推断，一个常规的get指令操作按照Redis 10w qps的性能表现来看，10个连接（平均每个指令1~2ms），理想情况下每秒处理约6600条指令，远低于Redis的极限处理能力，所以问题可能出在代码层面，我们需要进一步推断项目中是否存在不合理的Redis操作：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">结合本次发生的具体故障现象和表现特征，对项目进行全面的系统性全局分析。分析范围应覆盖项目架构、代码实现、依赖管理、环境配置、数据交互等多个维度，重点识别并输出可能导致生产故障的直接原因。</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-global-analysis-instruction.png\" alt=\"M2.7全局分析指令\"></p>\n<p>此时模型开始基于全局项目结构和上下文进行详细的阅读和推理分析：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-project-structure-analysis.png\" alt=\"M2.7项目结构分析\"></p>\n<p>最终模型给出了详细的故障分析报告，指出根因：不当的Redis数据结构设计使用scan操作导致连接池夯死。同时，还结合上下文给出了该操作的业务流程，便于我们迅速理解这条故障链路：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-root-cause-analysis.png\" alt=\"M2.7故障根因分析\"></p>\n<p>而解决方案也是非常干净利落，通过优化数据结构的方式降低Redis读写操作的时间复杂度，避免连接池夯死：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-optimization-suggestion.png\" alt=\"M2.7优化方案建议\"></p>\n<p>场景一整体体验不错。从N处Redis调用中精准定位根因，到给出完整止血方案，整个推理链条清晰完整。</p>\n<p>不过也发现了一些问题：它给出的方案一（清除Redis缓存）略显激进，实际生产环境可能需要更保守的策略。另外，部分边界条件的防御性代码还是需要人工补充——AI能帮你走到90%，剩下的10%还得靠自己。</p>\n<h2>场景2：从Redis C源码到Go实现的跨语言重构</h2>\n<h3>背景说明</h3>\n<p>接下来我们再来一个高难度场景——复刻Redis慢查询指令。mini-redis是采用Go语言goroutine-per-connection理念提升吞吐量，并以C语言的风格实现符合RESP协议的缓存中间件，由于语言在设计理念上存在偏差，涉及复杂逻辑梳理和异构方案落地。用于验证大模型的跨语言架构设计能力再合适不过。</p>\n<h3>需求梳理与方案设计</h3>\n<p>针对项目重构类需求，按传统开发流程，我们需要大量时间阅读源代码梳理逻辑，期间因历史原因代码无注释，需结合上下文推理调试。了解原有逻辑后，还需结合新项目架构制定实施步骤，并设计单元测试确保既有逻辑稳定运行。整个流程（研发、测试到发布）保守估计需要3个工作日。抱着试试看的心态，笔者将源代码阅读和技术文档整理工作交给 AI 负责。</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">我现在需要通过Go语言复刻Redis慢查询指令的实现。请你详细阅读Redis源代码，深入理解慢查询功能的完整实现原理、数据结构设计、处理流程和关键步骤。具体包括但不限于：慢查询日志的存储机制、慢查询阈值的配置与调整、慢查询命令的收集与记录流程、相关API接口的设计与实现，以及慢查询信息的查询与展示方式。请基于这些理解，整理出清晰的技术文档，包括核心原理说明、关键数据结构分析、实现步骤分解以及可能的性能优化考量。</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>等待片刻后，模型明确指出技术要求，自底向上地介绍数据结构到执行链路，进行了详尽的分析和介绍：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-slowlog-data-structure.png\" alt=\"M2.7慢查询数据结构分析\"></p>\n<p>查看其对慢查询切面逻辑的定位非常准确，在主流程上输出了必要的注释，让我快速了解慢查询的整体处理流程：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-slowlog-aspect-logic.png\" alt=\"M2.7慢查询切面逻辑\"></p>\n<p>再看其对slot get指令的理解，也非常到位，思路和资深开发一样，抓大放小，明确核心逻辑，在主流程上输出必要的注释：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-slot-get-instruction.png\" alt=\"M2.7 slot get指令分析\"></p>\n<p>确认模型对慢查询有了准确的理解后，接下来让它以开发专家的视角进行功能拆解、落地、测试回归的完整设计文档：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">按照测试驱动开发(TDD</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)方法论，使用Go语言创建一个全面详细的开发教程文档，指导复刻Redis的实现。该教程必须符合以下规范：</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">1.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 开发方法：</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 严格执行测试驱动开发工作流程：先编写会失败的测试，然后实现最简代码以通过测试，最后进行重构</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 采用类似于原始Redis</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> C语言实现的面向过程的编程风格</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 尽可能使用纯Go语法和标准库</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">2.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 教程结构：</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 从项目设置和环境配置说明开始</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 按Redis功能拆分为逻辑模块进行开发</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 针对每个模块/特性，提供：</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">     a.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 明确的测试用例定义，包含预期输入和输出</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">     b.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 逐步的代码实现，附带逐行解释</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">     c.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 明确的测试命令和验证流程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">     d.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 预期测试结果和成功标准</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">3.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 技术要求：</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 包含所有组件的完整代码片段</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 指定确切的文件结构和命名规范</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 详细说明编译和测试命令</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 解释常见问题的调试流程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 在适用时参考相关的Redis</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> C源代码模式</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">4.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 实现细节：</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 从核心数据结构（字符串、列表、哈希等）开始</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 逐步推进到命令处理和协议实现</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 包含网络层和客户端-服务器通信</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 涵盖持久化机制（RDB/AOF）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 按照相同的行为模式实现基本的Redis命令</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">5.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 测试要求：</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 为每个组件提供完整的测试代码</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 解释测试断言和验证方法</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 包含单元测试和集成测试</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 指定如何运行测试并解读结果</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">   -</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 详细说明如何根据Redis规范验证正确行为</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">该教程应足够全面，让具备中级Go知识的开发者能够按照指定方法成功构建一个功能类似的Redis系统。</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>等待片刻后，我们收到一份设计文档。模型结合Redis源代码上下文，梳理出慢查询的核心脉络和关键定义，并规划出完整的开发步骤：<br>\n<img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-slowlog-design-doc.png\" alt=\"慢查询设计文档\"></p>\n<h3>编码实现</h3>\n<p>我们从Redis源代码中抽取设计文档后，为确保C语言工程的设计思路能在个人Go语言项目工程规范中准确落地，将其复制到mini-redis项目，让模型分析方案的可行性和修改建议：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-feasibility-analysis.png\" alt=\"M2.7可行性分析\"></p>\n<p>等待片刻后模型完成文档最后的可行性分析和整理，我们开始对其设计方案进行进一步的复核确认。从项目概述上可以看到，模型针对mini-redis项目结构进行了分析，准确地定位到慢查询可以直接复用的链表结构体并完成文档微调：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-linked-list-structure.png\" alt=\"M2.7链表结构体分析\"></p>\n<p>再来看看最关键的数据结构实现思路，模型也结合mini-redis的编码规范，生成了Go语言风格的结构体：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-go-style-struct.png\" alt=\"M2.7 Go风格结构体\"></p>\n<p>针对慢查询时间测量，有个细节值得提一下。个人实现的指令处理入口和原生Redis有些设计上的出入：由于Go语言语法糖特性，笔者对指针、指针函数以及文件编排做了特殊处理。模型准确地基于笔者的协程模型定位到时间测量的切面，完成前置计时和后置统计，实现慢查询监控。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-time-measurement-aspect.png\" alt=\"M2.7时间测量切面\"></p>\n<p>最后就是核心的慢查询指令实现，无论是参数解析还是指令查询和响应处理函数，模型都结合笔者的当前项目封装的逻辑给出了明确的编码方案：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-slowlog-command-implementation.png\" alt=\"M2.7慢查询指令实现\"></p>\n<p>经过仔细复核设计文档，整体开发思路基本一致，但在代码组织细节上仍有调优空间——例如模型将<code>slowlog</code>指令独立成文件，而未遵循项目惯例统一放入<code>command.go</code>。考虑到慢查询功能并非核心内存读写指令，且其日志管理逻辑相对独立，这一处理也算合理折中。权衡之后，我们决定保留模型的实现方式，同时手动调整部分文件布局以符合既有工程规范，随后推进剩余开发工作。</p>\n<p>这一细节也说明：AI生成的代码架构虽然合理，但与既有工程规范的适配仍然需要人工把关。</p>\n<p>另外提一句，整个慢查询功能的实现过程中，模型有两次生成了不符合项目风格的代码（比如错误处理方式），需要手动调整。这不是大问题，但说明完全依赖AI生成还是不行的。</p>\n<h3>验收</h3>\n<p>因为笔者明确指定了TDD的开发模型，所以模型在这期间结合输出反馈和文档说明完成自循环修复，最终结合mini-redis的项目风格完成了慢查询指令的复刻。</p>\n<p>得益于 AI 的推理和重构能力，在验收过程中我们有了更多的构思空间。之前一直因为源代码梳理总结和技术验收成本过大，导致 redis.conf 配置加载逻辑一直没有实现。</p>\n<p>因为笔者需要将慢查询时间设置为0，方便对慢查询指令做最后的验收工作，所以笔者索性再次对其提出加载配置的需求：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/m2.7-config-loading.png\" alt=\"M2.7配置加载实现\"></p>\n<p>整个逻辑梳理和开发工作不到1小时，笔者顺利完成了慢查询指令复刻和验收，为了演示慢查询功能，将mini-redis的慢查询阈值设置为0：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"># 慢查询阈值（微秒）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"># 执行时间超过此值的命令会被记录到慢查询日志中</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"># 负值表示禁用慢查询日志，0 表示记录所有命令</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"># 默认值：10000（10毫秒）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">slowlog-log-slower-than</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>启动mini-redis服务端后，键入slowlog get 默认返回空：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/slowlog-get-initial-state.png\" alt=\"slowlog get初始状态\"></p>\n<p>执行简单的set操作后，键入slowlog get，这条指令如预期被判定为慢查询指令并输出：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/slowlog-get-record-set-command.png\" alt=\"slowlog get记录set命令\"></p>\n<p>同理，我们依次键入后续几条指令，也都准确按照链表头插法入队，实现按照时间降序排列输出：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/slowlog-get-multiple-records.png\" alt=\"slowlog get多条记录\"></p>\n<h2>实战总结：AI 辅助编程的工作流思考</h2>\n<p>通过两个典型场景的实战，总结一下使用 Trae + 大模型辅助编程的一些经验和思考。</p>\n<h3>AI 辅助编程能做什么</h3>\n<p>在上述两个场景中，AI 辅助编程体现了几个核心能力：</p>\n<p>| 能力维度       | 场景表现                                 | 说明                                     |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/ai/coding/m2.7/trae-add-model-entry.png",
      "date_published": "2026-03-29T06:07:23.000Z",
      "date_modified": "2026-04-10T08:31:20.000Z",
      "authors": [],
      "tags": [
        "AI 编程实战"
      ]
    },
    {
      "title": "AI 应用开发面试指南",
      "url": "https://javaguide.cn/ai/",
      "id": "https://javaguide.cn/ai/",
      "summary": "深入浅出掌握 AI 应用开发核心知识，涵盖大模型基础、Agent、RAG、MCP 协议、AI 编程实战等高频面试考点，适合校招/社招 AI 应用开发岗位面试复习。",
      "content_html": "<div class=\"hint-container tip\">\n<p class=\"hint-container-title\">写在前面</p>\n<p>现在网上有很多所谓”AI 技术文章”，点进去一看，满篇空洞的套话，逻辑混乱，读起来千篇一律。</p>\n<p>这类文章有几个共同特点：</p>\n<ul>\n<li><strong>内容堆砌</strong>：大量概念罗列，但没有真正讲清楚原理，读完云里雾里。</li>\n<li><strong>缺乏实战视角</strong>：纸上谈兵，没有真实的项目踩坑经验。</li>\n<li><strong>没有配图</strong>：全是文字，读者很难建立直观的认知。</li>\n<li><strong>正确性存疑</strong>：很多技术细节经不起推敲，甚至存在明显错误。</li>\n</ul>\n<p>我在写这一系列 AI 文章的时候，坚持一个原则：<strong>要么不写，要写就写透</strong>。每一篇文章我都投入了大量时间：</p>\n<ul>\n<li><strong>深度调研</strong>：查阅官方文档、技术博客、学术论文，确保内容准确。</li>\n<li><strong>精心配图</strong>：绘制了几十张配图帮助理解。</li>\n<li><strong>实战导向</strong>：内容都来自真实项目的踩坑经验，不是纸上谈兵。</li>\n<li><strong>反复打磨</strong>：每篇文章都修改了十几遍，确保逻辑清晰、表达准确。</li>\n</ul>\n<p>希望这些文章能真正帮到你。</p>\n</div>\n<div class=\"hint-container warning\">\n<p class=\"hint-container-title\">持续更新中</p>\n<p>AI 面试系列目前正在<strong>持续更新中</strong>，后续会陆续补充更多高频面试考点。</p>\n<p>当前内容可能还不够完善，如果你有想要了解的主题或任何建议，欢迎在项目 issue 区留言反馈。</p>\n</div>\n<h2>这个专栏能帮你解决什么问题？</h2>\n<p>如果你正在准备 AI 应用开发相关的面试，或者想要系统学习 AI 应用开发的核心知识，这个专栏就是为你准备的。</p>\n<p>通过这个专栏，你将获得：</p>\n<h3>1. 扎实的大模型基础知识</h3>\n<p>很多开发者在构建 Agent 工作流或调优 RAG 检索时，往往会在最底层的 LLM 参数上踩坑。比如：</p>\n<ul>\n<li>为什么明明设置了温度为 0，结构化输出还是偶尔崩溃？</li>\n<li>为什么往模型里塞了长文档后，它好像失忆了，忽略了 System Prompt 里的关键指令？</li>\n<li>Token 到底怎么算的？为什么中文和英文的消耗不一样？</li>\n</ul>\n<p>这些问题，如果你不理解 LLM 的底层原理，就永远只能“知其然不知其所以然”。在<a href=\"/ai/llm-basis/llm-operation-mechanism.html\" target=\"_blank\">《万字拆解 LLM 运行机制》</a>中，我会带你扒开 LLM 的黑盒，把 Token、上下文窗口、Temperature 等概念还原为清晰、可控的工程概念。</p>\n<h3>2. 系统的 AI Agent 知识体系</h3>\n<p>AI Agent 是当下 AI 应用开发最热门的方向。但网上的资料要么太浅，要么太散，很难形成系统的认知。</p>\n<p>在<a href=\"/ai/agent/agent-basis.html\" target=\"_blank\">《一文搞懂 AI Agent 核心概念》</a>中，我会带你：</p>\n<ul>\n<li>梳理 AI Agent 从 2022 年到 2025 年的六代进化史</li>\n<li>理解 Agent、传统编程、Workflow 三者的本质区别</li>\n<li>掌握 Agent Loop、Context Engineering、Tools 注册等核心概念</li>\n</ul>\n<p>在<a href=\"/ai/agent/prompt-engineering.html\" target=\"_blank\">《大模型提示词工程实践指南》</a>中，我会带你：</p>\n<ul>\n<li>掌握 Prompt 四要素框架（Role + Task + Context + Format）</li>\n<li>学会六大核心技巧：角色扮演、思维链、少样本学习、任务分解、结构化输出、XML 标签与预填充</li>\n<li>了解 Prompt 注入攻击原理与三层防护体系</li>\n</ul>\n<p>在<a href=\"/ai/agent/context-engineering.html\" target=\"_blank\">《上下文工程实战指南》</a>中，我会带你：</p>\n<ul>\n<li>理解 Context Engineering 和 Prompt Engineering 的本质区别</li>\n<li>掌握静态规则编排、动态信息挂载、Token 预算降级三大核心技术</li>\n<li>学会 Compaction、结构化笔记、Sub-agent 三种长任务上下文持久化方案</li>\n</ul>\n<h3>3. 深入理解 RAG 检索增强生成</h3>\n<p>RAG 是企业级 AI 应用的核心技术。但很多开发者只知道“把文档切成块，转成向量，然后检索”这个流程，却不理解背后的原理。</p>\n<p>在 RAG 系列文章中，我会带你深入理解：</p>\n<ul>\n<li><a href=\"/ai/rag/rag-basis.html\" target=\"_blank\">《万字详解 RAG 基础概念》</a>：RAG 是什么？为什么需要 RAG？RAG 的核心优势和局限性是什么？</li>\n<li><a href=\"/ai/rag/rag-vector-store.html\" target=\"_blank\">《万字详解 RAG 向量索引算法和向量数据库》</a>：HNSW、IVFFLAT 等索引算法的原理是什么？如何选择合适的向量数据库？</li>\n</ul>\n<h3>4. 掌握工具与协议</h3>\n<p>在 AI 应用开发中，工具接入的碎片化是一个大问题。MCP 协议的出现，就是要解决这个问题。</p>\n<p>在<a href=\"/ai/agent/mcp.html\" target=\"_blank\">《万字拆解 MCP 协议》</a>中，我会带你理解：</p>\n<ul>\n<li>MCP 是什么？为什么被称为“AI 领域的 USB-C 接口”？</li>\n<li>MCP 的四大核心能力和四层分层架构</li>\n<li>生产环境下开发 MCP Server 的最佳实践</li>\n</ul>\n<p>在<a href=\"/ai/agent/skills.html\" target=\"_blank\">《万字详解 Agent Skills》</a>中，我会带你理解：</p>\n<ul>\n<li>Skills 是什么？为什么说它是“延迟加载”的 sub-agent？</li>\n<li>Skills 和 Prompt、MCP、Function Calling 的本质区别</li>\n<li>如何在实战中设计优秀的 Skill</li>\n</ul>\n<p>在<a href=\"/ai/agent/harness-engineering.html\" target=\"_blank\">《一文搞懂 Harness Engineering》</a>（六层架构、上下文管理与一线团队实战）中，我会带你理解：</p>\n<ul>\n<li>Agent = Model + Harness，为什么说决定 Agent 天花板的是 Harness 而不是模型？</li>\n<li>Harness 六层架构、上下文管理的 40% 阈值现象</li>\n<li>OpenAI、Anthropic、Stripe 等一线团队的 Harness 工程化实战经验</li>\n</ul>\n<h3>5. AI 编程面试准备</h3>\n<p>AI 编程工具正在深刻改变开发者的工作方式。在面试中，你可能会被问到：</p>\n<ul>\n<li>用过什么 AI 编程 IDE？有什么使用技巧？</li>\n<li>如何看待 AI 对后端开发的影响？AI 会淘汰程序员吗？</li>\n<li>未来程序员的核心竞争力是什么？</li>\n</ul>\n<p>在<a href=\"/ai/llm-basis/ai-ide.html\" target=\"_blank\">《AI 编程开放性面试题》</a>中，我会分享 7 道高频开放性面试问题的回答思路。</p>\n<h3>6. AI 编程实战</h3>\n<p>纸上得来终觉浅。只有亲手用过 AI 编程工具，才能真正理解它的工作边界和使用技巧。在 AI 编程实战系列中，我会通过真实场景的实战案例，分享 AI 辅助编程的使用经验：</p>\n<ul>\n<li><a href=\"/ai/ai-coding/idea-qoder-plugin.html\" target=\"_blank\">《IDEA 搭配 Qoder 插件实战》</a>：从接口优化到代码重构，展示如何在 JetBrains IDE 中利用 AI 完成从分析到落地的完整闭环</li>\n<li><a href=\"/ai/ai-coding/trae-m2.7.html\" target=\"_blank\">《Trae + MiniMax 多场景实战》</a>：使用 Trae IDE 接入 MiniMax 大模型，通过 Redis 故障排查和跨语言重构场景，分享 AI 辅助编程的实战经验与踩坑心得</li>\n<li><a href=\"/ai/ai-coding/cc-glm5.1.html\" target=\"_blank\">《Claude Code 接入第三方模型实战》</a>：通过 Claude Code 接入 GLM-5.1，完成 JVM 智能诊断助手搭建和百万级数据量慢查询治理，分享 AI 辅助编程的工作方法与踩坑经验</li>\n</ul>\n<h2>文章列表</h2>\n<h3>大模型基础</h3>\n<ul>\n<li><a href=\"/ai/llm-basis/llm-operation-mechanism.html\" target=\"_blank\">万字拆解 LLM 运行机制：Token、上下文与采样参数</a> - 深入剖析大模型底层原理，把 Token、上下文窗口、Temperature 等概念还原为清晰、可控的工程概念</li>\n<li><a href=\"/ai/llm-basis/ai-ide.html\" target=\"_blank\">AI 编程开放性面试题</a> - 7 道高频开放性面试问题，涵盖 AI 编程 IDE 使用技巧、AI 对后端开发的影响等</li>\n</ul>\n<h3>AI Agent</h3>\n<ul>\n<li><a href=\"/ai/agent/agent-basis.html\" target=\"_blank\">一文搞懂 AI Agent 核心概念</a> - 梳理 AI Agent 六代进化史，掌握 Agent Loop、Context Engineering、Tools 注册等核心概念</li>\n<li><a href=\"/ai/agent/prompt-engineering.html\" target=\"_blank\">大模型提示词工程实践指南</a> - 掌握 Prompt 四要素框架、六大核心技巧及企业级安全实践</li>\n<li><a href=\"/ai/agent/context-engineering.html\" target=\"_blank\">上下文工程实战指南</a> - 深入理解 Context Engineering 核心概念，掌握静态规则编排、动态信息挂载、Token 预算降级等关键技术</li>\n<li><a href=\"/ai/agent/skills.html\" target=\"_blank\">万字详解 Agent Skills</a> - 深入理解 Skills 的设计理念，掌握 Skills 与 Prompt、MCP、Function Calling 的本质区别</li>\n<li><a href=\"/ai/agent/mcp.html\" target=\"_blank\">万字拆解 MCP 协议，附带工程实践</a> - 理解 MCP 协议的核心概念、架构设计和生产级最佳实践</li>\n<li><a href=\"/ai/agent/harness-engineering.html\" target=\"_blank\">一文搞懂 Harness Engineering：六层架构、上下文管理与一线团队实战</a> - 深度解析 Harness Engineering，拆解 OpenAI、Anthropic、Stripe 等一线团队的 Agent 工程化实战经验</li>\n</ul>\n<h3>RAG（检索增强生成）</h3>\n<ul>\n<li><a href=\"/ai/rag/rag-basis.html\" target=\"_blank\">万字详解 RAG 基础概念</a> - 深入理解 RAG 的工作原理、核心优势和局限性</li>\n<li><a href=\"/ai/rag/rag-vector-store.html\" target=\"_blank\">万字详解 RAG 向量索引算法和向量数据库</a> - 掌握 HNSW、IVFFLAT 等索引算法原理，学会选择合适的向量数据库</li>\n</ul>\n<h3>AI 编程实战</h3>\n<ul>\n<li><a href=\"/ai/ai-coding/idea-qoder-plugin.html\" target=\"_blank\">IDEA + Qoder 插件多场景实战：接口优化与代码重构</a> - 通过深分页优化、祖传代码重构两个真实案例，展示 AI 辅助编程的实战效果</li>\n<li><a href=\"/ai/ai-coding/trae-m2.7.html\" target=\"_blank\">Trae + MiniMax 多场景实战：Redis 故障排查与跨语言重构</a> - 使用 Trae IDE 接入 MiniMax 大模型，通过 Redis 故障排查和跨语言重构场景，分享 AI 辅助编程的实战经验</li>\n<li><a href=\"/ai/ai-coding/cc-glm5.1.html\" target=\"_blank\">Claude Code 接入第三方模型实战：JVM 智能诊断与慢查询治理</a> - 通过 Claude Code 接入 GLM-5.1，完成 JVM 智能诊断助手搭建和百万级数据量慢查询治理</li>\n</ul>\n<h2>配图预览</h2>\n<p>为了帮助读者更好地理解抽象的技术概念，我在每篇文章中都绘制了大量配图。这里展示几张：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/llm/llm-context-window.png\" alt=\"上下文窗口示意图\"></p>\n<p><em>上下文窗口是 LLM 的“工作记忆”，决定了模型能处理的最大文本量</em></p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/rag/rag-simplified-architecture-diagram.jpeg\" alt=\"RAG 架构示意图\"></p>\n<p><em>RAG 的核心思想：先检索相关上下文，再让 LLM 基于上下文生成回答</em></p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/skills/mcp-simple-diagram.png\" alt=\"MCP 图解\"></p>\n<p><em>MCP 被称为“AI 领域的 USB-C 接口”，统一了 LLM 与外部工具的通信规范</em></p>\n<h2>写在最后</h2>\n<p>这个专栏我会持续更新。如果觉得有帮助，欢迎分享给身边的朋友。有问题或建议，直接在项目 issue 区留言就行。</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/ai/llm/llm-context-window.png",
      "date_published": "2026-03-26T12:44:11.000Z",
      "date_modified": "2026-04-08T16:06:36.000Z",
      "authors": [],
      "tags": []
    },
    {
      "title": "万字拆解 MCP，附带工程实践",
      "url": "https://javaguide.cn/ai/agent/mcp.html",
      "id": "https://javaguide.cn/ai/agent/mcp.html",
      "summary": "深入解析 MCP 协议核心概念，涵盖 MCP 四大核心能力、四层分层架构、JSON-RPC 2.0 通信机制及生产级 MCP Server 开发最佳实践。",
      "content_html": "<p>在 LLM 应用开发从”单体调用”向”复杂 Agent”演进的当下，开发者最头疼的其实不是换模型——框架早把不同模型的 API 差异给封装好了。<strong>真正让人抓狂的是工具接入的碎片化</strong>：每次想让 AI 用上 GitHub、本地文件或者 MySQL，就得为 Claude、GPT、DeepSeek 分别写一套适配代码。改一个工具接口，得同步维护好几套代码，又烦又容易出错。</p>\n<p><strong>MCP (Model Context Protocol)</strong> 的出现，就是要终结这种混乱。它被形象地称为 <strong>“AI 领域的 USB-C 接口”</strong>，通过统一的通信协议，让工具开发者<strong>一次开发 MCP Server</strong>，之后所有支持 MCP 的 AI 应用都能直接复用，真正实现模型与外部数据源、工具的高效解耦。</p>\n<p>今天 Guide 就来分享几道 MCP 基础概念相关的问题，希望对大家有帮助。本文接近 1.6w 字，建议收藏，通过本文你讲搞懂：</p>\n<ol>\n<li>⭐ 什么是 MCP？它解决了什么核心问题？</li>\n<li>⭐ MCP、Function Calling 和 Agent 有什么区别与联系？</li>\n<li>MCP v1.0 的四大核心能力是什么？</li>\n<li>⭐ MCP 的四层分层架构是如何运行的？</li>\n<li>为什么 MCP 选择了 JSON-RPC 2.0 而非 RESTful？</li>\n<li>⭐️ MCP 支持哪些传输方式？（stdio、Streamable HTTP）</li>\n<li>⭐ 生产环境下开发 MCP Server 有哪些必知的最佳实践？</li>\n</ol>\n<h2>MCP 基础概念</h2>\n<h3>⭐️ 什么是 MCP？它解决了什么问题？</h3>\n<p><strong>MCP (Model Context Protocol)</strong> 是 Anthropic 于 2024 年提出的开放协议，被誉为 <strong>&quot;AI 领域的 USB-C 接口标准&quot;</strong>。它通过 JSON-RPC 2.0 统一了 LLM 与外部数据源/工具的通信规范，解决了 AI 应用开发中的<strong>复杂性和碎片化</strong>问题。</p>\n<p>它允许 AI 接入数据源（如本地文件、数据库）、工具（如搜索引擎、计算器）以及工作流（如特定提示词），使其能够获取关键信息并执行具体任务。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/skills/mcp-simple-diagram.png\" alt=\"MCP 图解\"></p>\n<p>在 MCP 出现之前，开发者为不同 LLM（OpenAI GPT、Claude、文心一言等）和不同后端系统集成工具时，需要编写大量<strong>定制化的适配代码</strong>。这导致了：</p>\n<ul>\n<li><strong>重复工作</strong>：同一功能需要为每个 LLM 重新实现。</li>\n<li><strong>高昂维护成本</strong>：API 变更需要多处同步修改。</li>\n<li><strong>生态碎片化</strong>：缺乏统一的工具接口标准。</li>\n</ul>\n<p>MCP 通过定义<strong>统一的通信协议</strong>，让一次开发的工具可以跨多个 LLM 平台使用，就像 USB-C 接口让不同设备可以通用充电线一样。</p>\n<blockquote>\n<p>🌈 <strong>拓展一下</strong>：</p>\n<p>MCP 的核心价值在于<strong>解耦和标准化</strong>。就像 HTTP 统一了网页传输、RESTful API 统一了服务接口一样，MCP 统一了 AI 与外部世界的交互方式。没有这一层标准化，每接一个新工具就得适配一遍各家的 API，规模化基本无从谈起。</p>\n</blockquote>\n<h3>MCP 的四大核心能力是什么？</h3>\n<p>MCP v1.0 定义了四种核心能力类型，覆盖了 LLM 与外部交互的主要场景：</p>\n<p>| <strong>能力</strong>               | <strong>核心作用</strong>                                                                                                                                                             | <strong>实际场景举例</strong>                                                                                                                                          | <strong>失败路径与边界</strong>                                                                              |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/ai/skills/mcp-simple-diagram.png",
      "date_published": "2026-03-26T09:55:47.000Z",
      "date_modified": "2026-04-10T08:31:20.000Z",
      "authors": [],
      "tags": [
        "AI 应用开发"
      ]
    },
    {
      "title": "万字详解 Agent Skills：是什么？怎么用？和 Prompt、MCP 有什么区别？",
      "url": "https://javaguide.cn/ai/agent/skills.html",
      "id": "https://javaguide.cn/ai/agent/skills.html",
      "summary": "深入解析 Agent Skills 概念，探讨 Skills 与 Prompt、MCP、Function Calling 的本质区别，以及如何在实战中设计优秀的 Skill 固化代码规范。",
      "content_html": "<p>2025 年初，Anthropic 在推出 <strong>MCP（Model Context Protocol）</strong> 之后，进一步提出了 <strong>Agent Skills</strong> 的概念。背后的思路其实很清楚：<strong>连接性（Connectivity）与能力（Capability）应该分离</strong>。</p>\n<p>很多开发者认为”只要提示词写得好，AI 就能帮我做一切”。但事实是：<strong>Prompt 适合单次任务，Skills 才是构建可复用 AI 能力的正确做法</strong>。</p>\n<p>Skills 把 AI 应用从”个人技巧”拉到了”工程化”的层面。今天 Guide 就带大家彻底搞懂这个概念，聊清楚 Skills 的设计理念、与相关技术的本质区别，以及如何在实战中用好这个能力。本文接近 1.2w 字，建议收藏，通过本文你将搞懂：</p>\n<ol>\n<li>⭐ <strong>Skills 是什么</strong>：为什么说 Skill 是”延迟加载”的 sub-agent？它的核心机制——上下文注入和延迟加载是如何工作的？</li>\n<li>⭐ <strong>Skills vs Prompt vs MCP vs Function Calling</strong>：这四者的本质区别是什么？它们分别适用于什么场景？这是面试中的高频盲区。</li>\n<li>⭐ <strong>优秀的 Skill 长什么样</strong>：一个设计良好的 Skill 应该包含哪些要素？元数据、触发条件、执行流程如何设计？</li>\n<li>⭐ <strong>项目实战</strong>：如何在真实开发中用 Skills 固化代码规范、排查流程、Review 标准？如何把团队中的”隐性知识”变成可复用的 AI 能力？</li>\n</ol>\n<h2>Skills 是什么？</h2>\n<p>用一句话概括：<strong>Skill 是一个用自然语言定义的、具有特定领域上下文（Domain Context）的逻辑指令集，本质上是通过延迟加载（Lazy Loading）优化 Token 消耗的 Sub-Agent（子智能体）</strong>。</p>\n<p>在团队协作中，很多&quot;隐性知识&quot;都在老员工脑子里，比如代码规范、排查流程、Review 标准。Skills 的核心价值，就是<strong>把这些隐性规则变成显性的文档（SOP），让 AI 能够自主阅读、理解并执行</strong>。</p>\n<p>与传统编程不同，Skills 不强制规定每一步的代码逻辑，而是<strong>用自然语言将决策权下放给模型</strong>——模型通过 <code>load_skill()</code> 动态加载 <code>SKILL.md</code> 后，将其中定义的规则、流程和约束<strong>实时注入到推理上下文</strong>中，指导后续的工具调用和决策。这既保留了 Agent 处理不确定性的优势，又避免了纯代码编排的僵化。</p>\n<blockquote>\n<p>为什么不用&quot;基于 Function Calling 封装&quot;？这个表述容易让人误以为 Skill 是某种 Function Calling 的语法糖。实际上，Skill 的核心机制是<strong>上下文注入</strong>——Agent 读取 Markdown 文档，把其中的规则和流程纳入推理上下文。Function Calling 只是 Agent 执行某些动作（如调脚本、查资源）时可能用到的底层手段，不是 Skills 本身的定义层。</p>\n<p>注意：<code>load_skill()</code> 是对&quot;Agent 读取并激活 <a href=\"http://SKILL.md\" target=\"_blank\" rel=\"noopener noreferrer\">SKILL.md</a>&quot;这一过程的概念性描述，不同工具（Claude Code、Cursor 等）的实际触发方式会有差异。</p>\n</blockquote>\n<p><strong>关键机制</strong>：</p>\n<ul>\n<li><strong>延迟加载（Lazy Loading）</strong>：元数据保持简短（通常远少于正文）常驻上下文，正文仅在触发时动态注入，避免挤占 Token</li>\n<li><strong>动态上下文注入</strong>：不同于静态文档的&quot;阅读&quot;，Skills 是将规则实时注入推理上下文，直接影响模型决策</li>\n</ul>\n<h2>Skills 和 Prompt、MCP、Function Calling有什么区别？</h2>\n<p>这也是面试中常被问到的点，容易混淆：</p>\n<p><strong>1. Skills vs Prompt</strong></p>\n<p>| 维度         | Prompt                     | Skills                         |<br>\n| :</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/ai/skills/mcp-simple-diagram.png",
      "date_published": "2026-03-26T09:55:47.000Z",
      "date_modified": "2026-04-10T08:31:20.000Z",
      "authors": [],
      "tags": [
        "AI 应用开发"
      ]
    },
    {
      "title": "9 道 AI 编程相关的开放性面试问题",
      "url": "https://javaguide.cn/ai/llm-basis/ai-ide.html",
      "id": "https://javaguide.cn/ai/llm-basis/ai-ide.html",
      "summary": "涵盖 Cursor、Claude Code、Trae 等 AI 编程 IDE 使用技巧，Spec Coding 与 Vibe Coding 区别，以及 AI 对后端开发影响等高频面试问题。",
      "content_html": "<p>腾讯面试的时候，面试官问我：“用过什么 AI 编程工具？”。我说：“Trae。”</p>\n<p>空气突然安静了两秒。我搞不清楚为什么面试官沉默了，当时我还在想：“是不是我回答得不够高级？”。</p>\n<p>面试被挂后才意识到：Trae 是字节的，腾讯家的是 CodeBuddy，阿里家的是 Qoder。</p>\n<p>段子归段子！今天 Guide 分享 7 道当下校招和社招技术面试中经常会被问到的 AI 编程开放性问题，希望对你有帮助。通过本文你将搞懂：</p>\n<ol>\n<li>⭐ <strong>AI 编程 IDE</strong>：Cursor、Claude Code 等 AI 编程工具有什么使用技巧？如何建立自己的使用方法论？</li>\n<li>⭐ <strong>AI 对后端开发的影响</strong>：你如何看待 AI 对后端开发的影响？AI 会淘汰初级程序员吗？AI 带来的最大风险是什么？</li>\n<li>⭐ <strong>未来核心竞争力</strong>：你觉得未来 3 年后端工程师的核心竞争力是什么？</li>\n</ol>\n<h2>AI 编程 IDE 和使用技巧</h2>\n<h3>用过什么 AI 编程 IDE 吗？什么感觉？</h3>\n<p>我用过几款 AI 编程工具，例如 Cursor、Trae、Claude Code，其中我日常开发中主要用的是 Cursor（根据你自己的使用去说就好，我这里以国内用的比较多的 Cursor 为例）。</p>\n<p>目前整体感觉是：AI 编程能力进步真的太快了！它已经从几年前简单的代码补全，进化成了一个可以深度协作的工程助手。</p>\n<p>我总结了一套自己的使用方法论：</p>\n<ol>\n<li>在接手复杂项目或模块时，我不会直接让 AI 写代码，而是先让 Cursor 分析整个代码库，生成一份包含核心架构、模块职责和数据流的文档。这一步非常关键，因为它决定了后续协作的质量。只有当我和 AI 对项目有一致理解时，后续产出才会稳定、高质量。</li>\n<li>对于每个独立的开发任务，我都会开启一个新的对话，并提供必要的上下文，包括需求背景、涉及模块和约束条件。这种方式能显著减少上下文污染，让 AI 生成的代码更加精准，基本不需要大幅返工。</li>\n<li>我也会定期删除冗余实现和废弃代码。旧代码会误导 AI 的判断，增加上下文噪音，长期不清理会直接影响协作效率。</li>\n</ol>\n<p>AI 是一个强大的知识库和辅助工具，可以帮我们快速实现功能、学习新知识。但如果完全依赖 AI 写代码而不理解其原理，个人技术能力可能会退化。</p>\n<p>因此我会坚持几个原则：</p>\n<ul>\n<li>AI 生成代码之后必须人工 Review。</li>\n<li>关键逻辑必要时自己重写。</li>\n<li>核心路径必须做压测和边界测试。</li>\n</ul>\n<p>我希望效率提升，但不以牺牲技术能力为代价。</p>\n<h3>⭐知道哪些 Cursor 使用技巧？</h3>\n<blockquote>\n<p>这里是以 Cursor 为例，其他 AI IDE 都是类似的。</p>\n</blockquote>\n<ol>\n<li><strong>先理架构再动手</strong>：无论是自己写代码还是让 AI 生成代码，都必须先明确需求、整体架构和模块边界。如果在架构模糊的情况下直接编码，很容易出现重复实现或职责冲突，后期修改成本反而更高。</li>\n<li><strong>单 Chat 专注单功能</strong>：新功能或大改动开启新的 Chat，并在开头引入项目结构说明或关键文档作为上下文基础。这样可以避免历史对话干扰，提高输出质量。</li>\n<li><strong>功能落地后写指南</strong>：让 AI 总结实现过程，抽象出通用步骤，形成“操作指南”。比如新增接口的标准流程、文件导出的统一实现方式等。这些沉淀下来的内容，可以在后续类似需求中快速复用。</li>\n<li><strong>不依赖 AI，主动复盘</strong>：AI 仅作辅助，代码生成后需认真 Review，理解原理、优化不合理处，避免技术停滞。</li>\n<li><strong>定期删无用代码</strong>：清理冗余代码，减少对 AI 的误导和上下文干扰，提升开发效率。</li>\n<li><strong>用好配置文件</strong>：<code>.cursorrules</code> 定义 AI 生成代码的规则、风格和常用片段；<code>.cursorignore</code> 指定不允许 AI 修改的文件 / 目录，保护核心代码。</li>\n<li><strong>持续维护文档</strong>：项目重大变更后，让 AI 同步更新文档、记录 &quot;踩坑&quot; 经验，积累团队知识库。</li>\n<li><strong>让 AI 先 &quot;学&quot; 项目</strong>：大型项目先让 Cursor 分析代码库，生成含架构、目录职责、核心类等的结构文档，作为后续开发的基础上下文。</li>\n</ol>\n<h3>知道那些 Claude Code 使用技巧？</h3>\n<p>和上一个问题其实是有重合的，我单独分享过一篇：<a href=\"https://t.zsxq.com/9rSZM\" target=\"_blank\" rel=\"noopener noreferrer\">⭐Claude Code使用技巧总结</a>。</p>\n<h2>AI 对后端开发的影响</h2>\n<h3>⭐你如何看待 AI 对后端开发影响？</h3>\n<p>我认为 AI 不会取代后端工程师，但会<strong>显著改变后端工程师的工作方式和能力结构</strong>。</p>\n<p>AI 将我们从重复的、模式化的工作中解放出来，成为我们最强的帮手：</p>\n<ul>\n<li><strong>在编码层面</strong>：AI 工具在生成<strong>模式化代码（Boilerplate）</strong>方面表现卓越，CRUD、单元测试、胶水代码的编写效率可提升 50%~70%。但在<strong>分布式约束</strong>（如分布式锁的超时续租、消息队列的 Exactly-once 语义、接口幂等性设计）上，AI 存在显著的<strong>&quot;幻觉&quot;风险</strong>——它往往只给出 Happy Path 代码，忽略了生产环境中的异常补偿逻辑、竞态条件处理和分布式事务边界控制。</li>\n<li><strong>在架构层面</strong>：AI 正在催生新的应用范式，比如智能体（Agent）驱动的自动化业务流程，后端需要提供更灵活、更原子化的能力接口。传统的&quot;大而全&quot;接口正逐步拆解为可被 AI 调用的原子化能力。</li>\n<li><strong>在运维与排障层面</strong>：AI 可以辅助分析日志、监控告警，甚至预测系统瓶颈，让问题排查更智能。例如，基于 AIOps（智能运维）的工具可以自动分析异常日志模式，定位根因。</li>\n</ul>\n<p>AI 让后端工程师能更专注于业务建模、复杂系统设计和架构决策这些更具创造性的核心工作。并且，AI 同样能够辅助我们更好地完成这些事情。</p>\n<p>拿我自己来说，我经常会和 AI 讨论业务和技术方案，它总能给我不错的启发——尤其是在需求拆解和技术选型时，AI 能提供多角度的思考。</p>\n<h3>你觉得 AI 会淘汰初级程序员吗？</h3>\n<p>短期内不会淘汰，但会彻底改变初级程序员的能力结构。</p>\n<p>以前初级工程师的价值在于：</p>\n<ul>\n<li>写 CRUD 增删改查</li>\n<li>写基础接口</li>\n<li>写 SQL 查询语句</li>\n<li>写基础工具类/配置</li>\n</ul>\n<p>现在这些工作 AI 都能做得很好，甚至更高效、更少出错。但这不意味着初级程序员会被淘汰，只是他们的价值创造点发生了迁移。</p>\n<p>未来初级工程师需要具备：</p>\n<ul>\n<li><strong>需求拆解能力</strong>：将模糊的业务需求转化为清晰的技术任务。</li>\n<li><strong>业务理解能力</strong>：理解领域模型和业务规则，而不仅是&quot;翻译需求&quot;。</li>\n<li><strong>架构感知能力</strong>：理解系统整体架构，知道自己代码在系统中的位置。</li>\n<li><strong>Prompt 表达能力</strong>：能精准地描述问题，从 AI 获取高质量答案。</li>\n</ul>\n<p>AI 让编程门槛变低，但对&quot;理解能力&quot;的要求反而更高。未来的初级工程师更像是一个&quot;AI 协调者&quot;，而非单纯的&quot;代码编写者&quot;。</p>\n<p>从企业招聘角度看，纯编码能力的需求会减少，但对&quot;能利用 AI 快速交付业务价值&quot;的工程师需求会增加。</p>\n<h3>AI 带来的最大风险是什么？</h3>\n<p>我认为主要有三个层面：</p>\n<p><strong>1. 技术能力退化</strong></p>\n<p>过度依赖 AI 会导致工程师自身技术能力的退化，尤其是：</p>\n<ul>\n<li><strong>调试能力下降</strong>：习惯让 AI 排查问题，自身对底层原理的理解变浅。</li>\n<li><strong>代码敏感度下降</strong>：对&quot;好代码&quot;和&quot;坏代码&quot;的判断能力变弱，甚至不知道什么是好代码。</li>\n<li><strong>架构思维退化</strong>：长期只关注功能实现，忽视架构设计和扩展性。</li>\n</ul>\n<p><strong>2. 架构失控</strong></p>\n<p>AI 生成的代码往往关注&quot;当前功能可用&quot;，容易忽视长期架构健康度。这很大程度上源于 <strong>Vibe Coding（氛围编程）</strong>——依赖模糊意图让 AI&quot;自由发挥&quot;。</p>\n<ul>\n<li>\n<p><strong>模块边界模糊</strong>：AI 倾向于&quot;快速完成功能&quot;，可能将多个职责混入同一模块。建议在编码前明确模块职责（DDD 风格的 Context Boundary），通过预先定义的接口契约约束 AI 生成范围。</p>\n</li>\n<li>\n<p><strong>技术债务累积</strong>：为快速实现功能，AI 可能使用硬编码、绕过标准异常处理、引入不必要的循环依赖等反模式。这些债务在项目规模增长后会显著增加重构成本。</p>\n</li>\n<li>\n<p><strong>风格一致性缺失</strong>：不同 Chat 会话中生成的代码可能采用不同的命名规范、错误处理模式和日志格式。建议通过 <strong>Spec Coding</strong> 的方式，预先定义统一的技术规范和代码风格（如 <code>.cursorrules</code>），让 AI 始终在同一套规则下工作。</p>\n</li>\n<li>\n<p><strong>资源治理缺失</strong>：AI 不会自动考虑连接池大小、线程池队列长度、缓存过期策略等资源约束。例如，生成的代码可能创建大量线程但无界队列，在流量激增时导致内存溢出；或使用默认数据库连接池配置，在高并发下成为瓶颈。</p>\n</li>\n</ul>\n<p><strong>3. 安全风险（尤其需要重视）</strong></p>\n<ul>\n<li><strong>代码漏洞</strong>：AI 可能生成包含安全漏洞的代码，常见问题包括：\n<ul>\n<li><strong>SQL 注入</strong>：使用字符串拼接而非参数化查询</li>\n<li><strong>XSS</strong>：未对用户输入进行 HTML 转义</li>\n<li><strong>权限校验缺失</strong>：缺少接口级/方法级权限检查</li>\n<li><strong>敏感信息泄露</strong>：日志中打印密钥、Token 或密码</li>\n<li><strong>依赖漏洞</strong>：引入存在已知 CVE 的第三方库</li>\n</ul>\n</li>\n<li><strong>数据泄露</strong>：不当使用可能泄露公司代码、业务逻辑给外部模型（尤其是云端托管的 AI 服务）。</li>\n<li><strong>供应链风险</strong>：AI 推荐的依赖包可能存在已知漏洞或恶意代码。</li>\n<li><strong>密钥泄露</strong>：AI 生成的代码可能硬编码密钥、Token 等敏感信息。</li>\n</ul>\n<p><strong>4. 分布式场景下的失效模式（尤其危险）</strong></p>\n<p>AI 生成的代码在分布式环境中极易忽略关键约束，导致生产事故：</p>\n<p>| 失效模式               | AI 常见问题                    | 生产风险                               |<br>\n|</p>\n",
      "date_published": "2026-03-26T09:55:47.000Z",
      "date_modified": "2026-04-10T08:31:20.000Z",
      "authors": [],
      "tags": [
        "AI 应用开发"
      ]
    },
    {
      "title": "万字详解 RAG 向量索引算法和向量数据库",
      "url": "https://javaguide.cn/ai/rag/rag-vector-store.html",
      "id": "https://javaguide.cn/ai/rag/rag-vector-store.html",
      "summary": "深入解析 RAG 场景下的向量数据库选型与使用，涵盖向量索引算法（HNSW、IVFFLAT）、ANN 近似检索原理、pgvector 实践等高频面试考点。",
      "content_html": "<p>前段时间面某大厂的时候，面试官问我：“你们 RAG 系统的向量检索怎么做的？”，我说：“用 MySQL 存 Embedding，查询时遍历计算相似度。”</p>\n<p>空气突然安静了五秒。我看到面试官的嘴角抽了一下，才意识到问题大了——当时我们知识库有 50 多万条 Chunk，每次查询都要全表扫描，平均响应时间 3 秒+，用户早就跑光了。</p>\n<p>面试被挂后才懂：这叫“暴力搜索”，而生产级方案应该是<strong>向量数据库 + ANN 索引</strong>。</p>\n<p>段子归段子，向量数据库确实是当下 RAG 应用的基础设施，也是 AI 应用开发面试的高频考点。今天 Guide 分享几道向量数据库相关的面试题，希望对大家有帮助：</p>\n<ol>\n<li>⭐️ RAG 场景为什么需要向量数据库？</li>\n<li>⭐️ 什么是向量索引算法？</li>\n<li>有哪些向量索引算法？</li>\n<li>⭐️ 你的项目使用的什么向量索引算法？</li>\n<li>HNSW 索引和 IVFFLAT 索引的区别是什么？</li>\n<li>有哪些向量数据库？</li>\n<li>⭐️ 你为什么选择 PostgreSQL + pgvector？</li>\n<li>为什么不选择 MySQL 搭配向量数据库呢？</li>\n</ol>\n<h2>⭐️ RAG 场景为什么需要向量数据库？</h2>\n<p>RAG（Retrieval-Augmented Generation）的核心是“语义检索”——把文档和用户问题都转成高维向量（Embedding），然后找最相似的 Top-K 片段作为 LLM 上下文。传统关系型数据库（MySQL、PostgreSQL 原生）或全文搜索引擎（ES 的 BM25）无法高效完成这件事，所以必须引入向量数据库（或带向量扩展的数据库）。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/rag/rag-why-need-vector-store.png\" alt=\"RAG 场景为什么需要向量数据库？\"></p>\n<h3>1. 高维向量相似度搜索</h3>\n<p>Embedding 通常是 768~3072 维的稠密向量，传统数据库只能用 <code>=</code> 或 <code>LIKE</code> 做精确匹配，无法计算“余弦相似度 / 内积 / 欧氏距离”。</p>\n<p><strong>暴力搜索</strong>：如果强行用 SQL 遍历全表计算相似度，复杂度是 O(n)。以 100 万条 1024 维向量为例：</p>\n<ul>\n<li>单次查询计算：1,000,000 × 1,024 次乘法运算</li>\n<li>实际延迟：<strong>秒级</strong>（具体数值因硬件而异）</li>\n</ul>\n<p>秒级延迟——对于需要实时响应的问答系统完全不可接受。</p>\n<p><strong>ANN 近似检索</strong>：向量数据库专为最近邻搜索（ANN, Approximate Nearest Neighbor）设计，通过图导航或空间划分大幅减少距离计算次数，将检索延迟降至<strong>毫秒级</strong>。</p>\n<p>| 指标           | 暴力搜索 | ANN 索引检索                                      |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/ai/rag/rag-why-need-vector-store.png",
      "date_published": "2026-03-26T09:55:47.000Z",
      "date_modified": "2026-04-10T08:31:20.000Z",
      "authors": [],
      "tags": [
        "AI 应用开发"
      ]
    },
    {
      "title": "为什么忘记密码时只能重置，不能告诉你原密码？",
      "url": "https://javaguide.cn/system-design/security/why-password-reset-instead-of-retrieval.html",
      "id": "https://javaguide.cn/system-design/security/why-password-reset-instead-of-retrieval.html",
      "summary": "详细解答为什么忘记密码时网站只能让你重置密码，而不能告诉你原密码。核心原因是服务端使用哈希算法存储密码，哈希算法不可逆，无法从哈希值还原出原始密码。本文还介绍了密码存储安全、加盐机制、Bcrypt 加密、密码传输安全等知识。",
      "content_html": "<p>这是一个挺有意思的问题，很多公司也在面试中问过。挺简单的，不知道大家平时在重置密码的时候有没有想过这个问题。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/reset-password-page.png\" alt=\"重置帐号密码\"></p>\n<p>回答这个问题其实就一句话：<strong>因为服务端也不知道你的原密码是什么</strong>。存原密码的程序员已经被开了 🤣。</p>\n<p>如果服务端知道你的原密码，那就是严重的安全风险问题了。</p>\n<p>我们这里来简单分析一下。</p>\n<p>这篇文章不会谈论太多加密算法相关的内容，感兴趣的朋友可以看这篇文章：<a href=\"https://javaguide.cn/system-design/security/encryption-algorithms.html\" target=\"_blank\" rel=\"noopener noreferrer\">常见加密算法总结</a>。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/encryption-algorithms/javaguide-security-encryption-algorithms.png\" alt></p>\n<h2>为什么服务端不知道你的原密码？</h2>\n<p>做过开发的应该都知道，服务端在保存密码到数据库的时候，<strong>绝对不能直接明文存储</strong>。</p>\n<p>如果明文存储的话，风险太大：</p>\n<ol>\n<li>数据库数据有被盗的风险</li>\n<li>有数据库权限的内部人员可能恶意利用</li>\n<li>黑客入侵后可以直接获取所有用户密码</li>\n</ol>\n<p>因此，密码必须经过处理后才能存储。这个处理方式就是使用<strong>哈希算法</strong>。</p>\n<h2>哈希算法简介</h2>\n<p>哈希算法也叫散列函数或摘要算法，它的作用是对任意长度的数据生成一个固定长度的唯一标识，也叫哈希值、散列值或消息摘要（后文统称为哈希值）。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/encryption-algorithms/hash-function-effect-demonstration.png\" alt=\"哈希算法效果演示\"></p>\n<p>哈希算法有两个关键特点：</p>\n<ol>\n<li><strong>不可逆性</strong>：你无法通过哈希之后的值再得到原值。这是核心！</li>\n<li><strong>确定性</strong>：相同的输入永远产生相同的输出。</li>\n</ol>\n<p>有个很形象的比喻：<strong>你存的密码就像切过的土豆丝，不能被复原成土豆。但网站判断密码是否正确的方式，就是把你输入的新密码当成土豆再切一次，看看这两盘土豆丝是不是一样的。</strong></p>\n<p>这两个特点决定了哈希算法非常适合用于密码存储：服务端只存储密码的哈希值，验证时只需比较哈希值是否一致。</p>\n<h3>哈希算法的分类</h3>\n<p>哈希算法可以简单分为两类：</p>\n<ol>\n<li><strong>加密哈希算法</strong>：安全性较高的哈希算法，它可以提供一定的数据完整性保护和数据防篡改能力，能够抵御一定的攻击手段，安全性相对较高，但性能较差，适用于对安全性要求较高的场景。例如 SHA2、SHA3、SM3、RIPEMD-160、BLAKE2等等。</li>\n<li><strong>非加密哈希算法</strong>：安全性相对较低的哈希算法，易受到暴力破解、冲突攻击等攻击手段的影响，但性能较高，适用于对安全性没有要求的业务场景。例如 CRC32、MurMurHash3等等。</li>\n</ol>\n<p>除了这两种之外，还有一些特殊的哈希算法，例如安全性更高的<strong>慢哈希算法</strong>。</p>\n<h3>为什么不推荐 MD5？</h3>\n<p>早期常用 MD5 来加密密码，但现在已经<strong>不被推荐</strong>，原因如下：</p>\n<ol>\n<li><strong>抗碰撞性差</strong>：存在弱碰撞问题，即多个不同的输入可能产生相同的 MD5 值。</li>\n<li><strong>哈希值较短</strong>：128 位的哈希值容易被彩虹表攻击。</li>\n<li><strong>计算速度太快</strong>：反而容易被暴力破解。</li>\n</ol>\n<p>详细介绍可以阅读这篇文章：<a href=\"https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&amp;mid=2247542780&amp;idx=1&amp;sn=fb2fe3fb53fe596cc5b22e30766e0098&amp;scene=21#wechat_redirect\" target=\"_blank\" rel=\"noopener noreferrer\">简历别再写 MD5 加密密码了！</a></p>\n<h3>为什么需要加盐？</h3>\n<p>单纯使用哈希算法存储密码，仍然存在被<strong>彩虹表攻击</strong>的风险。彩虹表是一种预先计算好的哈希值对照表，攻击者可以通过查表的方式快速破解密码。</p>\n<p>盐（Salt）在密码学中，是指通过在密码任意固定位置插入特定的字符串，让哈希后的结果和使用原始密码的哈希结果不相符，这种过程称之为&quot;加盐&quot;。</p>\n<p><strong>加盐的作用</strong>：</p>\n<ol>\n<li>增加密码的复杂度和唯一性。</li>\n<li>使得彩虹表攻击失效（每个用户的盐都不同）。</li>\n<li>即使两个用户使用相同密码，哈希值也不同。</li>\n</ol>\n<h2>密码存储方案推荐</h2>\n<p>目前推荐的密码存储方案有两种：</p>\n<h3>方案一：加密哈希算法 + Salt</h3>\n<p>使用安全性较高的加密哈希算法（如 SHA-256、SHA-3）加上盐值。</p>\n<p>SHA-256 + Salt 示例代码：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> password </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"123456\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> salt </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"1abd1c\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建SHA-256摘要对象</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">MessageDigest</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> messageDigest </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> MessageDigest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getInstance</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"SHA-256\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">messageDigest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">update</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">((password </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> salt).</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getBytes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 计算哈希值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">byte</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] result </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> messageDigest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">digest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 将哈希值转换为十六进制字符串</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> hexString </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> HexBinaryAdapter</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">marshal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(result);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Original String: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> password);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"SHA-256 Hash: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> hexString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toLowerCase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>输出：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">Original</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> String:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 123456</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">SHA-256</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Hash:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 424026bb6e21ba5cda976caed81d15a3be7b1b2accabb79878758289df98cbec</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>方案二：慢哈希算法（更推荐）</h3>\n<p><strong>Bcrypt</strong> 是专门为密码加密而设计的哈希算法，属于慢哈希算法。它内置了 salt 机制和 cost（成本）参数：</p>\n<ul>\n<li><strong>salt</strong>：随机生成的字符串，用于和密码混合，增加密码的唯一性</li>\n<li><strong>cost</strong>：控制迭代次数，增加计算时间和资源消耗</li>\n</ul>\n<p>Bcrypt 可以有效防止彩虹表攻击和暴力破解攻击。</p>\n<p>Java 应用程序的安全框架 Spring Security 官方推荐使用 <code>BCryptPasswordEncoder</code>：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Bean</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> PasswordEncoder</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> passwordEncoder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(){</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> BCryptPasswordEncoder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>登录验证流程</h2>\n<p>当你输入密码登录时，验证流程如下：</p>\n<ol>\n<li>服务端根据用户名从数据库取出该用户的盐值和存储的哈希值。</li>\n<li>服务端将用户输入的密码与盐值拼接，计算哈希值。</li>\n<li>比较计算出的哈希值与数据库中存储的哈希值是否一致。</li>\n<li>如果一致，说明密码正确；否则密码错误。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/encryption-algorithms/sha256-salt-password.png\" alt></p>\n<h2>重置密码时如何判断新密码与旧密码相同？</h2>\n<p>细心的同学可能发现，有些网站在重置密码时会提示&quot;新密码不可与旧密码相同&quot;。那网站是怎么知道新密码和旧密码相同的呢？</p>\n<p>其实原理和验证密码正确性一样：</p>\n<ol>\n<li>用户输入新密码。</li>\n<li>服务端用该用户的盐值，计算新密码的哈希值。</li>\n<li>将新密码的哈希值与数据库中存储的旧密码哈希值比较。</li>\n<li>如果相同，说明新密码和旧密码一样，拒绝修改。</li>\n</ol>\n<p>所以网站并不知道你的旧密码是什么，只是比较了两盘&quot;土豆丝&quot;是否一样。</p>\n<h2>密码传输安全</h2>\n<p>前面讲的都是密码在服务端的存储安全，那密码在传输过程中安全吗？</p>\n<p>有个常见的面试问题：<strong>如果某个员工知道加密方式，那岂不是他可以在私下或者离职后拦截包然后模拟加密从而获取密码？</strong></p>\n<p>答案是：<strong>存储与传输本身就是分开处理的</strong>。</p>\n<p>完整的密码安全方案需要同时保障存储安全和传输安全。</p>\n<h3>使用 HTTPS</h3>\n<p>HTTPS 协议是保障传输安全的基础。HTTP 协议运行在 TCP 之上，所有传输的内容都是明文，客户端和服务器端都无法验证对方的身份。HTTPS 则是运行在 SSL/TLS 之上的 HTTP 协议，所有传输的内容都经过加密。</p>\n<p>关于 HTTP 和 HTTPS 的详细对比可以看这篇文章：<a href=\"https://javaguide.cn/cs-basics/network/http-vs-https.html\" target=\"_blank\" rel=\"noopener noreferrer\">HTTP vs HTTPS（应用层）</a>。</p>\n<p><strong>但是，仅仅依赖 HTTPS 还不够安全</strong>：</p>\n<ol>\n<li>HTTPS 存在降级攻击、中间人攻击等风险</li>\n<li>HTTPS 只能保证传输过程中第三方抓包看到的是密文，无法防范客户端本身的恶意行为</li>\n</ol>\n<p>因此，我们还需要对密码进行<strong>加密后再传输</strong>。</p>\n<h3>密码加密传输</h3>\n<p>加密算法分为<strong>对称加密</strong>和<strong>非对称加密</strong>两大类。</p>\n<p><strong>对称加密</strong>是指加密和解密使用同一个密钥的算法，也叫共享密钥加密算法。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/encryption-algorithms/symmetric-encryption.png\" alt=\"对称加密\"></p>\n<p><strong>非对称加密</strong>是指加密和解密使用不同密钥的算法，也叫公开密钥加密算法。这两个密钥一个称为公钥（可公开），另一个称为私钥（需保密）。用公钥加密的数据只能用对应的私钥解密，反之亦然。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/encryption-algorithms/asymmetric-encryption.png\" alt=\"非对称加密\"></p>\n<p>常见的非对称加密算法有 RSA、DSA、ECC 等。</p>\n<p>对于密码传输这一场景，<strong>推荐使用非对称加密</strong>。完整流程如下：</p>\n<ol>\n<li>服务端生成公私钥对，私钥严格保密存储在服务端，公钥下发到客户端</li>\n<li>客户端传输密码前，使用公钥加密密码</li>\n<li>服务端收到加密数据后，用私钥解密获取原始密码</li>\n<li>服务端对原始密码进行哈希处理、加盐后存储</li>\n</ol>\n<h3>完整的安全方案</h3>\n<p>综合存储和传输，一个完整的密码安全方案包含三层：</p>\n<div class=\"language-javascript line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"javascript\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-javascript\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 第一层：客户端加密（非对称加密传输）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">const</span><span style=\"--shiki-light:#986801;--shiki-dark:#E5C07B\"> encryptedPassword</span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> rsaEncrypt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">password</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">publicKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 第二层：HTTPS 安全传输</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 第三层：服务端存储（哈希 + 盐值）</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>所以，即使内部员工知道加密算法，他也只能拿到：</p>\n<ul>\n<li>传输层：非对称加密后的密文（无私钥无法解密）</li>\n<li>存储层：哈希后的摘要（哈希不可逆，无法还原）</li>\n</ul>\n<p>这两层保护确保了密码在全链路的安全性。</p>\n<h2>总结</h2>\n<p>回到最初的问题：为什么忘记密码时只能重置，不能告诉你原密码？</p>\n<p>因为服务端存储的是密码经过哈希算法处理后的值，<strong>哈希算法是不可逆的</strong>，无法从哈希值还原出原始密码。这是密码安全的基本原则。</p>\n<p>如果一个网站能够告诉你原密码，那说明它<strong>明文存储了密码</strong>，这是严重的安全隐患，建议立即修改密码并远离该网站。</p>\n<p><strong>更重要的是</strong>：如果你在所有网站都用了相同的密码，一个不靠谱的网站泄漏了你的密码，就相当于你所有的账户都面临风险。所以，<strong>不要在所有网站使用相同密码</strong>！</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/system-design/security/reset-password-page.png",
      "date_published": "2026-03-23T08:33:10.000Z",
      "date_modified": "2026-03-23T08:33:10.000Z",
      "authors": [],
      "tags": [
        "系统设计"
      ]
    },
    {
      "title": "Java 26 新特性概览",
      "url": "https://javaguide.cn/java/new-features/java26.html",
      "id": "https://javaguide.cn/java/new-features/java26.html",
      "summary": "概览 JDK 26 的关键新特性与预览改动，关注 HTTP/3、GC 性能优化、AOT 缓存与语言/平台增强。",
      "content_html": "<p>JDK 26 于 2026 年 3 月 17 日 发布，这是一个非 LTS（非长期支持版）版本。上一个长期支持版是 <strong>JDK 25</strong>，下一个长期支持版预计是 <strong>JDK 29</strong>。</p>\n<p>JDK 26 共有 10 个新特性，这篇文章会挑选其中较为重要的一些新特性进行详细介绍：</p>\n<ul>\n<li><a href=\"https://openjdk.org/jeps/517\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 517: HTTP/3 for the HTTP Client API (为 HTTP Client API 引入 HTTP/3 支持)</a></li>\n<li><a href=\"https://openjdk.org/jeps/522\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 522: G1 GC: Improve Throughput by Reducing Synchronization (G1 GC 吞吐量优化)</a></li>\n<li><a href=\"https://openjdk.org/jeps/516\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 516: Ahead-of-Time Object Caching with Any GC (AOT 对象缓存支持任意 GC)</a></li>\n<li><a href=\"https://openjdk.org/jeps/500\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 500: Prepare to Make Final Mean Final (准备让 final 真正不可变)</a></li>\n<li><a href=\"https://openjdk.org/jeps/526\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 526: Lazy Constants (延迟常量, 第二次预览)</a></li>\n<li><a href=\"https://openjdk.org/jeps/525\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 525: Structured Concurrency (结构化并发, 第六次预览)</a></li>\n<li><a href=\"https://openjdk.org/jeps/530\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 530: Primitive Types in Patterns, instanceof, and switch (模式匹配支持基本类型, 第四次预览)</a></li>\n<li><a href=\"https://openjdk.org/jeps/524\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 524: PEM Encodings of Cryptographic Objects (加密对象 PEM 编码, 第二次预览)</a></li>\n<li><a href=\"https://openjdk.org/jeps/529\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 529: Vector API (向量 API, 第十一次孵化)</a></li>\n<li><a href=\"https://openjdk.org/jeps/504\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 504: Remove the Applet API (移除 Applet API)</a></li>\n</ul>\n<p>下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png\" alt></p>\n<h2>JEP 517: 为 HTTP Client API 引入 HTTP/3 支持</h2>\n<p>JDK 26 为 <code>java.net.http.HttpClient</code> API 正式添加了 <strong>HTTP/3</strong> 支持，这是一个期待已久的重要更新。</p>\n<p><strong>HTTP/3 的优势</strong>：</p>\n<ul>\n<li><strong>基于 QUIC 协议</strong>：HTTP/2 是基于 TCP 协议实现的，HTTP/3 新增了 QUIC（Quick UDP Internet Connections） 协议来实现可靠的传输，提供与 TLS/SSL 相当的安全性，具有较低的连接和传输延迟。你可以将 QUIC 看作是 UDP 的升级版本，在其基础上新增了很多功能比如加密、重传等等。</li>\n<li><strong>消除队头阻塞</strong>：HTTP/2 多请求复用一个 TCP 连接，一旦发生丢包，就会阻塞住所有的 HTTP 请求。由于 QUIC 协议的特性，HTTP/3 在一定程度上解决了队头阻塞（Head-of-Line blocking, 简写：HOL blocking）问题，一个连接建立多个不同的数据流，这些数据流之间独立互不影响，某个数据流发生丢包了，其数据流不受影响（本质上是多路复用+轮询）。</li>\n<li><strong>更快的连接建立</strong>：HTTP/2 需要经过经典的 TCP 三次握手过程（由于安全的 HTTPS 连接建立还需要 TLS 握手，共需要大约 3 个 RTT）。由于 QUIC 协议的特性（TLS 1.3，TLS 1.3 除了支持 1 个 RTT 的握手，还支持 0 个 RTT 的握手）连接建立仅需 0-RTT 或者 1-RTT。这意味着 QUIC 在最佳情况下不需要任何的额外往返时间就可以建立新连接。</li>\n<li><strong>更好的移动端体验</strong>：HTTP/3.0 支持连接迁移，因为 QUIC 使用 64 位 ID 标识连接，只要 ID 不变就不会中断，网络环境改变时（如从 Wi-Fi 切换到移动数据）也能保持连接。而 TCP 连接是由（源 IP，源端口，目的 IP，目的端口）组成，这个四元组中一旦有一项值发生改变，这个连接也就不能用了。</li>\n</ul>\n<p>详细介绍可以阅读这篇文章：<a href=\"https://javaguide.cn/cs-basics/network/other-network-questions.html\" target=\"_blank\" rel=\"noopener noreferrer\">计算机网络常见面试题总结（上）</a>（网络分层模型、常见网路协议总结、HTTP、WebSocket、DNS 等）</p>\n<p><strong>使用方式</strong>：</p>\n<p>HTTP/3 的使用非常简单，几乎不需要修改现有代码。<code>HttpClient</code> 会自动协商使用最高版本的 HTTP 协议：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">HttpClient</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> client </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> HttpClient</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newHttpClient</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">HttpRequest</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> request </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> HttpRequest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newBuilder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">uri</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">URI</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">create</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"https://example.com\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">))</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">build</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 如果服务器支持 HTTP/3，HttpClient 会自动升级使用</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">HttpResponse</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> response </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">send</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(request,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    HttpResponse</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">BodyHandlers</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">ofString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">response</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">body</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>如果需要明确指定使用 HTTP/3，可以通过 <code>version()</code> 方法设置：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 所有请求默认优先使用 HTTP/3</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">HttpClient</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> client </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> HttpClient</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newBuilder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">HttpClient</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">HTTP_3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 明确指定 HTTP/3</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">build</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 设置单个HttpRequest对象的首选协议版本</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">HttpRequest</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> request </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> HttpRequest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newBuilder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">URI</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">create</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"https://javaguide.cn/\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">))</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                         .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">HttpClient</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">HTTP_3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                         .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">GET</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">build</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JEP 522: G1 GC 吞吐量优化</h2>\n<p><strong>从 JDK9 开始，G1 垃圾收集器成为了默认的垃圾收集器。</strong> 它在延迟和吞吐量之间寻求平衡。然而，这种平衡有时会影响应用程序的性能。与面向吞吐量的 Parallel GC 相比，G1 更多地与应用程序并发工作，以减少 GC 暂停时间。但这意味着应用线程必须与 GC 线程共享 CPU 并进行协调，这种同步会降低吞吐量并增加延迟。</p>\n<p>JEP 522 引入了<strong>双卡表（Card Table）</strong>机制：</p>\n<ol>\n<li><strong>第一张卡表</strong>：应用线程的写屏障在更新这张卡表时<strong>无需任何同步</strong>，使得写屏障代码更简单、更快速。</li>\n<li><strong>第二张卡表</strong>：优化器线程在后台并行处理这张初始为空的卡表。</li>\n</ol>\n<p>当 G1 检测到扫描第一张卡表可能超过暂停时间目标时，它会原子性地交换这两张卡表。应用线程继续更新空的、原先的第二张表，而优化器线程则处理满的、原先的第一张表，无需进一步同步。</p>\n<p><strong>性能提升效果</strong>：</p>\n<ul>\n<li>在<strong>频繁修改对象引用字段</strong>的应用中，吞吐量提升 <strong>5-15%</strong></li>\n<li>即使在不频繁修改引用字段的应用中，由于写屏障简化（x64 上从约 50 条指令减少到仅 12 条），吞吐量也能提升高达 <strong>5%</strong></li>\n<li>GC 暂停时间也有<strong>轻微下降</strong></li>\n</ul>\n<p><strong>内存开销</strong>：</p>\n<p>第二张卡表与第一张容量相同，每张卡表需要 Java 堆容量的 0.2%，即每 1GB 堆内存额外使用约 2MB 原生内存。</p>\n<h2>JEP 516: AOT 对象缓存支持任意 GC</h2>\n<p>这是 <strong>Project Leyden</strong> 的重要里程碑，使得提前（AOT）对象缓存能够与<strong>任意垃圾收集器</strong>配合使用。</p>\n<p>之前在 JDK 24 中引入的 AOT 类数据共享（JEP 483）只支持 G1 垃圾收集器，无法与 ZGC 等其他 GC 配合使用。这是因为 AOT 缓存中存储的对象引用使用的是物理内存地址，而不同 GC 的内存布局和对象移动策略不同。</p>\n<p>JEP 516 将对象引用的存储方式从<strong>物理内存地址</strong>改为<strong>逻辑索引</strong>：</p>\n<ul>\n<li>使用 GC 无关的流式格式存储缓存</li>\n<li>缓存可以在运行时被任意 GC 加载和解析</li>\n<li>JVM 在加载时将逻辑索引转换为实际的内存地址</li>\n</ul>\n<p><strong>性能收益</strong>：</p>\n<ul>\n<li><strong>启动时间优化</strong>：显著减少 Java 应用的冷启动时间</li>\n<li><strong>支持 ZGC</strong>：低延迟的 ZGC 现在也能享受 AOT 缓存带来的启动加速</li>\n<li><strong>云原生友好</strong>：对于微服务和无服务器函数等启动时间敏感的场景特别有价值</li>\n</ul>\n<h2>JEP 500: 准备让 final 真正不可变</h2>\n<p>这个特性为 Java 的完整性优先原则铺平道路，准备让 <code>final</code> 字段真正变得不可变。</p>\n<p>从 JDK 1.0 开始，Java 的 <code>final</code> 字段实际上可以通过<strong>深度反射</strong>被修改：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.lang.reflect.Field</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.lang.reflect.Method</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Example</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> name </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Original\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getName</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> name;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 通过反射修改 final 字段</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Example</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> example </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Example</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Field</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> field </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Example</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getDeclaredField</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"name\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">field</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setAccessible</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 移除 final 修饰符</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Field</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> modifiersField </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Field</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getDeclaredField</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"modifiers\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">modifiersField</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setAccessible</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">modifiersField</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setInt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(field, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">field</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getModifiers</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x26;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ~</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Modifier</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">FINAL</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">field</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">set</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(example, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Modified\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 成功修改了 final 字段！</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">example</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getName</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 输出 \"Modified\"</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这种能力虽然被一些框架（如序列化库、依赖注入框架、测试工具）使用，但破坏了 <code>final</code> 的不可变性保证，也阻碍了编译器优化。</p>\n<p>在 JDK 26 中，当通过深度反射修改 <code>final</code> 字段时，JVM 会<strong>发出警告</strong>。这是为未来版本中默认禁止此类操作做准备。</p>\n<p>对于确实需要修改 <code>final</code> 字段的场景，JDK 26 提供了显式的选择机制，允许开发者在过渡期继续使用此能力，同时为未来的严格模式做好准备。</p>\n<h2>JEP 526: 延迟常量 (第二次预览)</h2>\n<p>该特性第一次预览是由 <a href=\"https://openjdk.org/jeps/501\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 501</a> （JDK 25）提出，JDK 26 是第二次预览。</p>\n<p>传统的 <code>static final</code> 字段在类加载时就会初始化，这会：</p>\n<ul>\n<li>增加启动时间。</li>\n<li>如果该常量从未被使用，则浪费内存。</li>\n<li>需要复杂的延迟初始化模式（如双重检查锁定、Holder 类模式等）。</li>\n</ul>\n<p>JEP 526 引入了 <code>LazyConstant&lt;T&gt;</code>，一种持有不可变数据的对象，JVM 将其视为真正的常量，以获得与声明 <code>final</code> 字段相同的性能。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 传统方式：类加载时立即初始化</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ExpensiveObject</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> TRADITIONAL </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ExpensiveObject</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 新方式：首次访问时才初始化</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LazyConstant</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ExpensiveObject</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> LAZY </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    LazyConstant</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">of</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(() </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ExpensiveObject</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 使用时</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ExpensiveObject</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> obj </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> LAZY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 此时才初始化</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>优势</strong>：</p>\n<ul>\n<li><strong>按需初始化</strong>：只在首次访问时初始化，提升启动性能。</li>\n<li><strong>线程安全</strong>：内置线程安全保证，无需手动同步。</li>\n<li><strong>JVM 优化</strong>：JVM 可以像对待 <code>final</code> 字段一样优化延迟常量。</li>\n<li><strong>简化代码</strong>：消除双重检查锁定等复杂的延迟初始化模式。</li>\n</ul>\n<h2>JEP 525: 结构化并发 (第六次预览)</h2>\n<p>JDK 19 引入了结构化并发，一种多线程编程方法，目的是为了通过结构化并发 API 来简化多线程编程，并不是为了取代<code>java.util.concurrent</code>，目前处于孵化器阶段。</p>\n<p>结构化并发将不同线程中运行的多个任务视为单个工作单元，从而简化错误处理、提高可靠性并增强可观察性。也就是说，结构化并发保留了单线程代码的可读性、可维护性和可观察性。</p>\n<p>结构化并发的基本 API 是<code>StructuredTaskScope</code>，它支持将任务拆分为多个并发子任务，在它们自己的线程中执行，并且子任务必须在主/父任务继续之前完成或者子任务随主/父任务失败而取消。</p>\n<p><code>StructuredTaskScope</code> 的基本用法如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> scope </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> StructuredTaskScope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 使用fork方法派生线程来执行子任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Future</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> future1 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fork</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task1);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Future</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> future2 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fork</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task2);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 等待线程完成</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">join</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 结果的处理可能包括处理或重新抛出异常</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        ...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> process</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> results</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">exceptions </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// close</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>结构化并发非常适合虚拟线程，虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程，从而允许非常多的虚拟线程。</p>\n<p><strong>Java 26 的新变动</strong>：</p>\n<ul>\n<li><strong>Joiner 增强</strong>：<code>Joiner</code> 接口新增 <code>onTimeout()</code> 方法，允许在超时发生时返回特定结果。</li>\n<li><strong>返回类型优化</strong>：<code>allSuccessfulOrThrow()</code> 现在直接返回结果列表（<code>List</code>），而非之前的子任务流。</li>\n<li><strong>API 简化</strong>：将 <code>anySuccessfulResultOrThrow()</code> 简化更名为 <code>anySuccessfulOrThrow()</code>。</li>\n</ul>\n<h2>JEP 530: 模式匹配支持基本类型 (第四次预览)</h2>\n<p>该特性第一次预览是由 <a href=\"https://openjdk.org/jeps/455\" title=\"JEP 455\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 455</a> （JDK 23 ）提出。</p>\n<p>模式匹配可以在 <code>switch</code> 和 <code>instanceof</code> 语句中处理所有的基本数据类型（<code>int</code>, <code>double</code>, <code>boolean</code> 等）</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> test</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> obj) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (obj </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"这是一个int类型: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>JDK 26 对该特性进行了进一步增强：</p>\n<ul>\n<li>消除了与基本类型相关的多项限制，使模式匹配、<code>instanceof</code> 和 <code>switch</code> 更加统一和表达力更强。</li>\n<li>增强了无条件精确性的定义。</li>\n<li>在 <code>switch</code> 构造中应用更严格的支配性检查，使编译器能够识别并减少更广泛的编码错误。</li>\n</ul>\n<p>这样就可以像处理对象类型一样，对基本类型进行更安全、更简洁的类型匹配和转换，进一步消除了 Java 中的模板代码。</p>\n<h2>JEP 524: 加密对象 PEM 编码 (第二次预览)</h2>\n<p>该特性第一次预览是由 <a href=\"https://openjdk.org/jeps/518\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 518</a> （JDK 25）提出。</p>\n<p>PEM（Privacy-Enhanced Mail）是一种广泛使用的文本格式，用于存储和传输加密对象，如证书、私钥和公钥。JEP 524 提供了一个新的 API，用于将加密对象编码为 PEM 格式，以及从 PEM 格式解码回加密对象。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 将密钥编码为 PEM 格式</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">KeyPairGenerator</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> kpg </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> KeyPairGenerator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getInstance</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"RSA\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">kpg</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">initialize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2048</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">KeyPair</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> keyPair </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> kpg</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">generateKeyPair</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 编码为 PEM</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> pemEncoded </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> PemEncoding</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">encode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">keyPair</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getPrivate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 从 PEM 解码</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">PrivateKey</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> decodedKey </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> PemEncoding</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">decode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(pemEncoded);</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这个 API 减少了错误风险，简化了合规性要求，并通过简化企业、云和监管需求的加密设置和集成，增强了安全 Java 应用程序的可移植性和互操作性。</p>\n<h2>JEP 529: Vector API (向量 API, 第十一次孵化)</h2>\n<p>向量计算由对向量的一系列操作组成。向量 API 用来表达向量计算，该计算可以在运行时可靠地编译为支持的 CPU 架构上的最佳向量指令，从而实现优于等效标量计算的性能。</p>\n<p>向量 API 的目标是为用户提供简洁易用且与平台无关的表达范围广泛的向量计算。</p>\n<p>这是对数组元素的简单标量计算：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> scalarComputation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">   for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        c[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i]) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1.0f</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">   }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这是使用 Vector API 进行的等效向量计算：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> VectorSpecies</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Float</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> SPECIES </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> FloatVector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">SPECIES_PREFERRED</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> vectorComputation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> upperBound </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> SPECIES</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">loopBound</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> upperBound</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> SPECIES</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // FloatVector va, vb, vc;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> va </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> FloatVector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fromArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(SPECIES, a, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> vb </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> FloatVector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fromArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(SPECIES, b, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> vc </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> va</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">mul</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(va)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                   .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">vb</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">mul</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(vb))</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                   .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">neg</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        vc</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">intoArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(c, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        c[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i]) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1.0f</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>尽管仍在孵化中，但其第十一次迭代足以证明其重要性。它使得 Java 在科学计算、机器学习、AI 推理、大数据处理等性能敏感领域，能够编写出接近甚至媲美 C++ 等本地语言性能的代码。</p>\n<h2>JEP 504: 移除 Applet API</h2>\n<p>Applet API 在 JDK 9 中被标记为废弃，在 JDK 17 中被标记为即将移除。在 JDK 26 中，Applet API 终于被<strong>完全移除</strong>。大快人心啊！</p>\n<p>这意味着：</p>\n<ul>\n<li><code>java.applet.Applet</code> 类及其相关类已被删除。</li>\n<li>减少了 JDK 的安装和源代码体积。</li>\n<li>提升了应用程序的性能、稳定性和安全性。</li>\n</ul>\n<p>Applet 技术早已过时，现代 Web 开发已完全转向其他技术栈。移除这个遗留 API 是 Java 平台现代化的必要步骤。</p>\n<h2>总结</h2>\n<p>JDK 26 虽然是一个非 LTS 版本，但包含了一些值得关注的重要特性：</p>\n<p>| 类别     | 特性                                                       |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png",
      "date_published": "2026-03-21T06:28:26.000Z",
      "date_modified": "2026-03-21T06:28:26.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "MySQL索引失效场景总结",
      "url": "https://javaguide.cn/database/mysql/mysql-index-invalidation.html",
      "id": "https://javaguide.cn/database/mysql/mysql-index-invalidation.html",
      "summary": "全面总结MySQL索引失效的常见场景，包括SELECT *查询、违背最左前缀原则、索引列计算函数转换、LIKE模糊查询、OR连接、IN/NOT IN使用不当、隐式类型转换以及ORDER BY排序优化陷阱，帮助你避免索引失效导致的性能问题。",
      "content_html": "<p>在数据库性能优化中，索引是最直接有效的优化手段之一。然而，<strong>建了索引并不等于一定能用上索引</strong>。实际开发中，我们经常遇到这样的困惑：明明在字段上建立了索引，查询却依然慢如蜗牛，通过 <code>EXPLAIN</code> 分析发现居然是全表扫描。</p>\n<p>导致索引失效的原因多种多样，既有 SQL 语句写法问题，也有索引设计不当的因素。有些失效场景是显性的（如违背最左前缀原则），有些则非常隐蔽（如隐式类型转换）。如果不深入了解这些失效场景，很容易在生产环境中埋下性能隐患。</p>\n<p>本文将系统总结 MySQL 索引失效的常见场景，分析失效背后的原理机制，并提供相应的优化建议，帮助你在日常开发和排查问题中快速定位并解决索引失效问题。</p>\n<h3>SELECT * 查询（成本权衡）</h3>\n<ul>\n<li><strong>核心定义</strong>：<code>SELECT *</code> 本身<strong>不会直接导致索引失效</strong>。它是一种”非覆盖索引”查询，如果 <code>WHERE</code> 条件命中了索引，索引依然会被初步考虑。</li>\n<li><strong>回表成本决策</strong>：当查询需要的字段不在索引树中时，MySQL 必须拿着主键回聚簇索引查找整行数据（回表）。优化器会对比”索引扫描 + 回表”与”直接全表扫描”的成本。如果查询结果占总数据量的比例较高（通常阈值在 20%~30%），优化器会认为全表扫描的顺序 IO 效率高于回表的随机 IO，从而<strong>主动放弃索引</strong>。</li>\n<li><strong>场景权衡</strong>：\n<ul>\n<li><strong>覆盖索引场景</strong>：如果查询只需索引覆盖的字段，使用覆盖索引可以避免回表，性能最优。</li>\n<li><strong>回表不可避免时</strong>：如果业务确实需要多个非索引字段，直接 <code>SELECT 需要的字段</code> 即可。当需要大部分字段时，代码可读性可能比”省几个字段”的微优化更重要，此时用 <code>SELECT *</code> 也无妨。</li>\n</ul>\n</li>\n<li><strong>落地建议</strong>：优先 <code>SELECT 需要的字段</code>，能覆盖索引最好；如果需要大量字段且回表不可避免，不必教条地”省字段”。</li>\n</ul>\n<h3>违背最左前缀原则</h3>\n<ul>\n<li><strong>核心定义</strong>：最左前缀匹配原则指的是在使用联合索引时，MySQL 会根据索引中的字段顺序，从左到右依次匹配查询条件中的字段。如果查询条件与索引中的最左侧字段相匹配，那么 MySQL 就会使用索引来过滤数据。</li>\n<li><strong>范围查询的中断效应</strong>：在联合索引中，如果某个字段使用了范围查询（例如 &gt;、&lt;、BETWEEN、前缀匹配 LIKE &quot;abc%&quot;），该字段本身以及其之前的列可以正常匹配并用于索引的精确定位，但该字段之后的列将无法利用<br>\n索引进行快速定位（即无法使用 ref 类型的二分查找）。这是因为在 B+Tree 索引结构中，只有当前导列完全相等时，后续列才是有序的。一旦前导列变成一个范围，后续列在整个扫描区间内就呈现相对无序状态，从而中断了精准定位能力。不过，在 MySQL 5.6 及以上版本中，这些后续列并未完全失效，而是降级为使用<strong>索引下推（Index Condition Pushdown, ICP）机制</strong>，在范围扫描的过程中直接进行条件过滤，以此来减少回表次数。</li>\n<li><strong>索引跳跃扫描 (ISS)</strong>：MySQL 8.0.13 引入了<strong>索引跳跃扫描（Index Skip Scan）</strong>，允许在缺失最左前缀时，通过枚举前导列的所有 Distinct 值来跳跃扫描后续索引树。\n<ul>\n<li><strong>版本避坑指南</strong>：在 <strong>MySQL 8.0.31</strong> 中，ISS 存在严重 Bug（<a href=\"https://bugs.mysql.com/bug.php?id=109145\" target=\"_blank\" rel=\"noopener noreferrer\">[Bug #109145]</a>），在跨 Range 读取时未清理陈旧的边界值，会导致查询直接<strong>丢失数据</strong>。</li>\n<li><strong>落地建议</strong>：ISS 在前导列基数（Cardinality）极低（如性别、状态枚举）时性能最优，因为优化器需要枚举前导列的所有 distinct 值逐一跳跃扫描——distinct 值越少，跳跃次数越少。但&quot;基数低&quot;本身并非官方限制条件，优化器会综合评估成本决定是否触发 ISS。在生产环境中，<strong>严禁依赖 ISS 来弥补糟糕的索引设计</strong>，必须通过调整联合索引顺序或补齐前导列条件来满足最左前缀。</li>\n</ul>\n</li>\n</ul>\n<p><strong>Index Skip Scan 失败路径图：</strong></p>\n<p>失效示例：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 索引：(sname, s_code, address)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> s_code </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;                  </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 跳过最左列 sname，索引失效</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> sname </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 'A'</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> AND</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> address</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 'Shanghai'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 跳过中间列，仅 sname 走索引（索引下推 ICP 可优化过滤）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> sname </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 'A'</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> AND</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> s_code </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> AND</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> address</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 'Shanghai'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 范围查询后，address 无法用于定位，仅用于过滤</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>在索引列上进行计算、函数或类型转换</h3>\n<ul>\n<li><strong>核心定义</strong>：索引 B+Tree 存储的是字段的<strong>原始值</strong>。一旦在 <code>WHERE</code> 条件中对索引列应用了函数（如 <code>ABS()</code>、<code>DATE()</code>）或算术运算，该列的值在逻辑上发生了改变。</li>\n<li><strong>有序性破坏效应</strong>：由于 B+Tree 是基于原始值排序的，经过函数处理后的结果在索引树中是<strong>无序</strong>的。数据库无法利用二分查找快速定位，只能被迫进行全表扫描。</li>\n<li><strong>函数索引</strong>：MySQL 8.0 支持<strong>函数索引</strong>（Functional Index），可针对计算后的值建索引，但使用场景有限，首选还是优化 SQL 写法。</li>\n</ul>\n<p>失效示例：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> height + </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 170</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;            </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 对索引列进行计算</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> DATE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(create_time) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '2022-01-01'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 对索引列使用函数</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>优化建议：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> height </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 169</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;                </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 将计算移到等号右边</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> create_time </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">BETWEEN</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '2022-01-01 00:00:00'</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> AND</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '2022-01-01 23:59:59'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>LIKE 模糊查询以通配符开头</h3>\n<ul>\n<li><strong>核心定义</strong>：<code>LIKE</code> 查询必须以具体字符开头才能利用索引有序性，例如 <code>WHERE sname LIKE 'Guide%';</code>。这是因为 B+ 树是从左到右排序的。前缀通配符（<code>%</code>）破坏了有序性，无法定位起始点。</li>\n<li><strong>前缀通配符的失效机制</strong>：如果以 <code>%</code> 开头（如 <code>'%abc'</code>），由于索引是按字符从左到右排序的，前缀不确定意味着可能出现在索引树的任何位置，导致无法定位搜索区间的起始点。</li>\n<li><strong>落地建议</strong>：\n<ul>\n<li>如果必须进行全模糊查询，尽量只查询索引覆盖的列，此时 <code>EXPLAIN</code> 会显示 <code>type: index</code>（<strong>Index Full Scan</strong>），虽然扫描了整棵树，但无需回表，性能仍优于 <code>ALL</code>。</li>\n<li>核心业务的大规模模糊搜索应通过 <strong>ElasticSearch</strong> 或其他搜索引擎实现。</li>\n</ul>\n</li>\n</ul>\n<p>失效示例：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> sname </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">LIKE</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '%Guide'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;          </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 前缀模糊，全表扫描</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> sname </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">LIKE</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '%Guide%'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;         </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 前后模糊，全表扫描</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>OR 连接与 Index Merge</h3>\n<ul>\n<li><strong>核心定义</strong>：在 <code>OR</code> 连接的多个条件中，只要有<strong>任意一列没有索引</strong>，MySQL 就会放弃所有索引转而执行全表扫描。</li>\n<li><strong>Index Merge 机制</strong>：若 <code>OR</code> 两侧都有索引，MySQL 5.1+ 可能会触发<strong>索引合并（Index Merge）</strong>优化，分别扫描两个索引后取并集。不过，如果两个索引过滤后的数据量都很大，合并结果集的成本可能高于全表扫描，依然会放弃索引。</li>\n<li><strong>落地建议</strong>：\n<ul>\n<li>优先将 <code>OR</code> 改写为 <code>UNION ALL</code>。<code>UNION ALL</code> 可以让每一段查询独立使用索引，且规避了优化器对 <code>OR</code> 成本估算不准的问题。</li>\n<li>注意：只有当确定结果集不重复时才用 <code>UNION ALL</code>，否则需用 <code>UNION</code>（涉及临时表去重，有额外开销）。</li>\n</ul>\n</li>\n</ul>\n<p>失效示例：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 假设 sname 和 address 都有索引，但各匹配 30%+ 数据</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> sname </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '学生 1'</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> OR</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> address</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '上海'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 可能放弃索引，全表扫描</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 建议改写为</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> sname </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '学生 1'</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">UNION ALL</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> address</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '上海'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 各自走索引</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>验证方式</strong>：<code>EXPLAIN</code> 中若出现 <code>type: index_merge</code> 和 <code>Extra: Using union; Using where</code>，说明使用了 Index Merge。</p>\n<h3>IN / NOT IN 使用不当</h3>\n<p><strong><code>IN</code> 列表长度</strong>：</p>\n<ul>\n<li><code>eq_range_index_dive_limit</code>（默认 <strong>200</strong>）并不直接导致索引失效，而是影响<strong>行数估算策略</strong>：\n<ul>\n<li><strong>&lt;= 200</strong>：MySQL 使用 <strong>Index Dive</strong>（深入索引树探测）精确估算行数，成本估算准确，索引大概率有效。</li>\n<li><strong>&gt; 200</strong>：当 <code>IN</code> 列表长度超过 <code>eq_range_index_dive_limit</code>（MySQL 5.7.4+ 默认为 200）时，优化器从精确的 Index Dive 切换为基于 <code>index_statistics</code> 的估算。若表数据的基数（Cardinality）统计陈旧，可能导致估算成本异常，从而放弃走范围扫描（Range Scan）而选择全表扫描。</li>\n</ul>\n</li>\n<li>可通过调大 <code>eq_range_index_dive_limit</code> 或改写为 <code>JOIN</code> 临时表来规避。</li>\n</ul>\n<p><strong><code>NOT IN</code></strong> ：</p>\n<ul>\n<li><strong>常量列表</strong>（如 <code>NOT IN (1,2,3)</code>）：通常全表扫描，因需遍历整个 B+ 树证明&quot;不在集合中&quot;。</li>\n<li><strong>子查询关联索引列</strong>：<code>WHERE id NOT IN (SELECT user_id FROM orders WHERE user_id &gt; 1000)</code> 可用 <code>orders</code> 表的 <code>user_id</code> 索引。</li>\n<li><strong>推荐替代</strong>：优先使用 <code>NOT EXISTS</code> 或 <code>LEFT JOIN / IS NULL</code>，性能更优且语义更清晰。</li>\n</ul>\n<p>失效示例：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> s_code </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">IN</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, ..., </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">500</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">); </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 列表过长，可能改用统计估算导致误判</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> students </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> s_code </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">NOT</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> IN</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);     </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 常量列表，全表扫描</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>隐式类型转换</h3>\n<p>这是开发中最隐蔽的坑，<strong>转换的方向决定了索引的生死</strong>。</p>\n<p>| 场景                  | 示例                | 转换方向                     | 索引是否有效 |<br>\n|</p>\n",
      "date_published": "2026-03-09T10:52:44.000Z",
      "date_modified": "2026-03-21T06:28:26.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "ZAB协议详解",
      "url": "https://javaguide.cn/distributed-system/protocol/zab.html",
      "id": "https://javaguide.cn/distributed-system/protocol/zab.html",
      "summary": "ZooKeeper的核心共识协议ZAB（ZooKeeper Atomic Broadcast，原子广播协议）详解，包括消息广播模式、崩溃恢复模式、Leader选举机制（ZXID/epoch）、数据恢复机制及Follower/Observer角色解析。",
      "content_html": "<p>作为一款极其优秀的分布式协调框架，ZooKeeper 的高可用和数据一致性备受业界推崇。很多人误以为 ZooKeeper 使用的是大名鼎鼎的 Paxos 算法，但实际上，它的&quot;灵魂&quot;是一个专门为其定制的共识协议——<strong>ZAB（ZooKeeper Atomic Broadcast，原子广播协议）</strong>。</p>\n<p>ZAB 并非像 Paxos 那样是通用的分布式一致性算法，它是一种<strong>特别为 ZooKeeper 设计的、支持崩溃可恢复的原子消息广播算法</strong>。基于 ZAB 协议，ZooKeeper 实现了一种主备模式的架构，来保持集群中各个副本之间的数据一致性。</p>\n<h2>ZAB 集群的核心角色与状态</h2>\n<p>在深入协议运作之前，我们需要先了解 ZooKeeper 集群中的三个主要角色：</p>\n<ul>\n<li><strong>Leader（领导者）：</strong> 集群中<strong>唯一</strong>的写请求处理者。它负责发起投票和协调事务，所有的写操作都必须经过 Leader。</li>\n<li><strong>Follower（跟随者）：</strong> 可以直接处理客户端的读请求。收到写请求时，会将其转发给 Leader。在 Leader 选举过程中，Follower 拥有选举权和被选举权。</li>\n<li><strong>Observer（观察者）：</strong> 功能与 Follower 类似，但<strong>没有</strong>选举权和被选举权。它的存在是为了在不影响集群共识性能（即不增加需要等待的投票数）的前提下，横向扩展集群的读性能。</li>\n</ul>\n<p>对应的，集群中的节点通常处于以下四种状态之一：</p>\n<ul>\n<li><code>LOOKING</code>：寻找 Leader 状态（正在进行选举）。</li>\n<li><code>LEADING</code>：当前节点是 Leader，正在领导集群。</li>\n<li><code>FOLLOWING</code>：当前节点是 Follower，服从 Leader 领导。</li>\n<li><code>OBSERVING</code>：当前节点是 Observer。</li>\n</ul>\n<h2>核心标识：ZXID 与 Epoch</h2>\n<p>为了保证分布式环境下消息的绝对顺序性，ZAB 协议引入了一个全局单调递增的事务 ID——<strong>ZXID</strong>。</p>\n<p>ZXID 是一个 64 位的长整型（long）：</p>\n<ul>\n<li><strong>高 32 位（Epoch 纪元）：</strong> 代表当前 Leader 的任期年代。当选出一个新的 Leader 时，Epoch 就会在前一个的基础上加 1。这相当于朝代更替。</li>\n<li><strong>低 32 位（事务 ID）：</strong> 一个简单的递增计数器。针对客户端的每一个写请求，计数器都会加 1。新 Leader 上位时，这个低 32 位会被清零重置。</li>\n</ul>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/zab-zxid-structure.png\" alt=\"ZXID 结构\"></p>\n<h2>ZAB 的两种基本模式</h2>\n<p>ZAB 协议的运作可以精简为两种基本模式的交替：<strong>消息广播</strong>（正常工作状态）和<strong>崩溃恢复</strong>（异常或启动状态）。</p>\n<h3>1. 消息广播模式（正常处理写请求）</h3>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/zab-message-broadcast-flow.png\" alt=\"ZAB 消息广播模式\"></p>\n<p>当集群拥有健康的 Leader，且过半的节点完成了状态同步后，就会进入消息广播模式。这个过程类似于一个简化的“两阶段提交（2PC）”：</p>\n<ol>\n<li><strong>生成提案：</strong> Leader 接收到写请求后，将其转化为一个带有 ZXID 的提案（Proposal）。</li>\n<li><strong>顺序发送：</strong> Leader 为每个 Follower 维护了一个先进先出（FIFO）的网络队列（基于 TCP 协议），确保提案按生成顺序发送给 Follower。</li>\n<li><strong>写入与反馈（WAL 强制落盘）：</strong> Follower 收到提案后，必须将其追加到本地的事务日志（TxnLog）中，并强制执行系统调用 <code>fsync</code> 将内核缓冲区的数据物理刷入磁盘。只有确认数据切实落盘，才会向 Leader 响应 <code>ACK</code>。这一过程是 ZAB 抵御断电丢失数据的核心防线。因此，在物理部署上，强烈建议将 ZooKeeper 的事务日志目录（<code>dataLogDir</code>）挂载到独立且无锁的 SSD 上，避免与其他高 I/O 进程争用磁盘，从而规避因 <code>fsync</code> 阻塞导致的 P99 响应时间恶化。生产环境中必须重点监控节点的 <code>fsynctime</code> 指标，若平均刷盘耗时经常超过 100ms，集群随时可能崩溃。</li>\n<li><strong>广播提交：</strong> 当 Leader 收到<strong>过半数</strong> 节点的 <code>ACK</code> 响应后，就会认为该写操作成功。Leader 在本地写日志时会更新内部的 quorum 计数器（而非显式向自己发送 ACK），确认过半后向客户端返回成功响应，并向所有节点广播 <code>Commit</code> 消息。Follower 收到 <code>Commit</code> 后，正式将数据应用到内存中。</li>\n</ol>\n<h3>2. 崩溃恢复模式（Leader 宕机或网络异常）</h3>\n<p>当系统刚启动，或者 Leader 服务器崩溃、与过半 Follower 失去联系时，整个集群就会暂停对外服务，进入 <code>LOOKING</code> 状态，触发崩溃恢复模式。崩溃恢复主要包含两个阶段：<strong>Leader 选举</strong>和<strong>数据恢复</strong>。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/zab-crash-recovery-flow.png\" alt=\"zab-crash-recovery-flow\"></p>\n<h4>阶段一：Leader 选举</h4>\n<p>选举的核心原则是：<strong>拥有最新数据的节点优先当选</strong>。 每个节点都会先投自己一票，投票信息包含 <code>(Epoch, ZXID, myid)</code>。随后节点会交换选票，并按照以下顺序进行 PK：</p>\n<ol>\n<li><strong>比较 Epoch：</strong> 纪元大的优先。</li>\n<li><strong>比较 ZXID：</strong> 如果 Epoch 相同，ZXID 大的优先（代表数据越新）。</li>\n<li><strong>比较 myid：</strong> 如果前两者都相同，服务器唯一标识 <code>myid</code> 大的优先。</li>\n</ol>\n<p>一旦某个节点获得了<strong>过半数</strong>的选票，它就会成为新的 Leader。<em>(这也是为什么 ZooKeeper 推荐部署奇数台服务器的原因，能以最低的成本实现半数以上的容错。)</em></p>\n<h4>阶段二：数据恢复</h4>\n<p>选出新 Leader 只是第一步，为了保证数据一致性，ZAB 必须在数据同步阶段实现两个极其重要的保证：</p>\n<ol>\n<li><strong>确保已经在旧 Leader 上提交的事务，最终被所有节点提交。</strong> （防止数据丢失）</li>\n<li><strong>丢弃那些只在旧 Leader 上提出，但还没来得及提交的事务。</strong> （防止脏数据干扰）</li>\n</ol>\n<p>新 Leader 会找到当前最大的 <code>Epoch</code> 并加 1 作为新纪元，随后与所有 Follower 进行比对。Follower 会发送自己事务日志中最新记录的 <code>lastZxid</code>（包含已提议但尚未提交的提案），Leader 根据这个值采取多态同步策略：<strong>差异化增量同步（DIFF）</strong>、<strong>强制丢弃未提交日志（TRUNC）</strong> 或 <strong>全量快照传输（SNAP）</strong>。</p>\n<p>这一设计至关重要：Leader 需要准确识别 Follower 日志中是否残留着旧 Leader 未完成提交的&quot;幽灵提案&quot;，才能正确下发 TRUNC 指令让其截断回滚。如果只上报已提交的 ZXID，这些未提交的脏数据将无法被感知，TRUNC 分支就永远不会被触发。</p>\n<p>更关键的是，此时新的 Epoch 已经生效。若原 Leader 因 JVM 触发长达数十秒的 Full GC 而发生&quot;假死&quot;，当其苏醒并试图向集群下发旧 Epoch 的提案时，由于过半节点已记录了更高的新 Epoch 且已向新 Leader 提交 quorum，这些幽灵提案将被节点无情拒绝并抛弃。ZAB 正是通过 <strong>Epoch 机制 + 多数派 quorum</strong> 的组合，从根本上免疫了网络环境下的脑裂现象——单靠 Epoch 拒绝还不够，必须有过半节点已经连上新 Leader，旧 Leader 才真正失去写入能力。</p>\n<p>当过半的机器与新 Leader 完成了状态和数据同步，ZAB 协议就会平滑退出崩溃恢复模式，重新进入消息广播模式。</p>\n<h2>与 Raft 对比</h2>\n<p><strong>ZAB 与 Raft 的高度相似性：</strong> 如果你了解过 Raft 算法，会发现它们非常相似。它们都有唯一的主节点，都使用 Epoch/Term 来标识任期，并且都采用了只要半数以上节点确认即可提交的策略。这说明在现代分布式共识领域，这种基于主备和多数派选举的架构已经成为了事实上的标准。</p>\n<p>在当前的分布式系统实践中，Raft 算法通常被视为比 ZAB 更实用和受欢迎的选择。 这是因为 Raft 从设计之初就强调易懂性和可实现性，它将领导者选举、日志复制和安全性明确分离，这使得开发者更容易正确实施和调试，而 ZAB 作为 ZooKeeper 的专有协议，更侧重于原子广播的特定需求，导致其通用性较差。</p>\n<p>Raft 已广泛应用于现代系统，如 Kubernetes 的 etcd、Hashicorp Consul、Apache Kafka（在其 KIP-500 版本中去除 ZooKeeper 依赖，转向 Raft-based KRaft）、TiKV 等，这极大“民主化”了分布式共识的开发。</p>\n<p>相比之下，ZAB 主要绑定在 ZooKeeper 上，虽然 ZooKeeper 仍是经典的协调服务，但许多新项目倾向于选择 Raft 以避免 ZooKeeper 的额外复杂性和潜在瓶颈（如在大规模下共识开销）。</p>\n<p>此外，Raft 的社区支持更活跃，衍生出多种优化变体（如用于区块链的改进版本），使其在效率和适用场景上更具优势。 然而，如果你的系统已深度集成 ZooKeeper，ZAB 仍是最优化的选择；否则，对于新设计或通用共识需求，Raft 是当前更实用的标准。</p>\n<h2>总结</h2>\n<p>ZAB 协议通过精心设计的 Leader 选举和多数派确认机制，在分布式系统的分区容错性（P）和一致性（C）之间做出了选择（满足 CP 属性）。当出现网络分区时，ZAB 宁愿牺牲短暂的可用性（A）进行选举，也要保证数据的一致性。</p>\n<p>需要特别强调的是，<strong>ZAB 协议默认不保证严格的强一致性（线性一致性），而是提供顺序一致性（Sequential Consistency）</strong>。</p>\n<p>由于 Follower 可以直接处理客户端的读请求且不强求数据绝对同步，客户端完全可能读取到落后于 Leader 的陈旧数据（Stale Read）。在生产环境中，若业务涉及如分布式锁等对数据新鲜度要求极高的场景，必须在执行 <code>read()</code> 操作前显式调用 <code>sync()</code> 原语，强制要求连接的 Follower 追平 Leader 的事务状态机。</p>\n<p>当发生网络分区时，客户端若连接至被隔离的少数派 Follower，虽然写操作会失败，但仍可读出过期数据，这是使用 ZAB 协议时必须考虑的边界场景。</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/zab-zxid-structure.png",
      "date_published": "2026-03-04T08:41:16.000Z",
      "date_modified": "2026-03-12T04:06:59.000Z",
      "authors": [],
      "tags": [
        "分布式"
      ]
    },
    {
      "title": "如何基于Redis实现消息队列？",
      "url": "https://javaguide.cn/database/redis/redis-stream-mq.html",
      "id": "https://javaguide.cn/database/redis/redis-stream-mq.html",
      "summary": "讲解 Redis 做消息队列的三种方式：List、Pub/Sub、Stream。对比生产级 MQ 核心能力，详解 Redis 5.0 Stream 的消费者组、ACK 机制及与 Kafka/RabbitMQ 的适用场景对比。",
      "content_html": "<p>先说结论：<strong>可以是可以，但要看具体场景。和专业的消息队列（如 Kafka、RabbitMQ）相比，还是有一些欠缺的地方。</strong></p>\n<p>正式开始介绍之前，我们先来看看：<strong>一个生产级 MQ 需要具备哪些核心能力？</strong></p>\n<p>| 能力维度         | 定义                            | 关键指标/特征                       |<br>\n| :</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/database/redis/redis-pub-sub.png",
      "date_published": "2026-03-01T07:29:56.000Z",
      "date_modified": "2026-04-08T07:23:21.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "2026 最新后端面试 PDF 资料",
      "url": "https://javaguide.cn/interview-preparation/pdf-interview-javaguide.html",
      "id": "https://javaguide.cn/interview-preparation/pdf-interview-javaguide.html",
      "summary": "2026 版后端面试 PDF 资料整理（JavaGuide）：梳理校招/社招高频考点与复习优先级，覆盖 Java 基础、集合、并发、MySQL、Redis、Spring/Spring Boot、JVM、系统设计与项目经验准备，帮你抓重点高效备战。",
      "content_html": "<p>大家好，我是 Guide。</p>\n<p><strong>2026 版后端 PDF 面试资料终于搞定了！这次的更新量大得惊人，熬了几个通宵，总算能拿出来见人了。</strong></p>\n<p>在上一版的基础上，我把内容又往深里挖了挖。目前这份资料已经涵盖了 <strong>Java 核心、计算机基础、数据库、缓存、分布式、设计模式、智力题、学习路线、面经</strong>等全方位内容。毫不夸张地说，你备战后端面试需要的硬核干货，这一份全包了！</p>\n<p>为了让大家看得更爽，我对其中大部分 PDF 进行了“推倒重来式”的优化：</p>\n<ul>\n<li><strong>重构面试突击系列</strong>：将原先臃肿的内容拆分成多篇，逻辑更清晰。</li>\n<li><strong>重写设计模式总结</strong>：新增多道高频设计模式面试题，优化内容表达。</li>\n<li><strong>全方位细节完善</strong>：每一个知识点都反复推敲，确保没有逻辑断层。</li>\n</ul>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/intro/pdf-interview-javaguide.png\" alt></p>\n<p>这些 PDF 面试资料的质量都非常高，绝大部分都是 Guide 的原创，也会有一些其他优质技术博主分享的原创资料。</p>\n<p>之所以一直坚持出 PDF 版，是因为有一些朋友比较喜欢看 PDF 资料，甚至把 PDF 资料打印出来学习。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/intro/pdf-interview-javaguide-chat.png\" alt></p>\n<p>截止到目前，这套资料在各个渠道的汇总下载量已经突破了 <strong>35w+</strong> 。 说实话，这个数字对我来说不只是流量，更是沉甸甸的信任和责任。</p>\n<p>老规矩，没有任何花里胡哨的套路，直接<strong>白嫖</strong>： 在 <strong>JavaGuide</strong> 公众号后台回复 <strong>PDF</strong> 即可获取。</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n<p>由于 PDF 的时效性问题，如果想要更完美的体验，个人其实还是更建议大家去 <a href=\"https://javaguide.cn/\" target=\"_blank\" rel=\"noopener noreferrer\">JavaGuide</a> 网站上在线阅读，内容更新，一直在持续完善。</p>\n<h2>部分内容概览</h2>\n<p><strong>《JavaGuide 面试突击》— Java 集合</strong>：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/intro/javaguide-mianshituji-java-collection.png\" alt=\"《JavaGuide 面试突击》— Java 集合面试题总结\"></p>\n<p><strong>《JavaGuide 面试突击》— JVM</strong>：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/intro/javaguide-mianshituji-jvm.png\" alt=\"《JavaGuide 面试突击》— JVM面试题总结\"></p>\n<p><strong>《JavaGuide 面试突击》—设计模式</strong>：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/intro/javaguide-mianshituji-design-pattern.png\" alt=\"《JavaGuide 面试突击》—设计模式面试题总结\"></p>\n<p><strong>Java 学习路线</strong>：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/interview-preparation/java-road-map-pdf.png\" alt=\"Java 学习路线 PDF 概览 - 亮色板\"></p>\n<h2>如何获取？</h2>\n<p>老规矩，没有任何花里胡哨的套路，直接<strong>白嫖</strong>： 在 <strong>JavaGuide</strong> 公众号后台回复 <strong>PDF</strong> 即可获取。</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/intro/pdf-interview-javaguide.png",
      "date_published": "2026-01-21T03:04:06.000Z",
      "date_modified": "2026-01-21T03:04:06.000Z",
      "authors": [],
      "tags": [
        "面试准备"
      ]
    },
    {
      "title": "《SpringAI 智能面试平台+RAG 知识库》",
      "url": "https://javaguide.cn/zhuanlan/interview-guide.html",
      "id": "https://javaguide.cn/zhuanlan/interview-guide.html",
      "summary": "Spring AI智能面试平台实战项目，基于Spring Boot 4.0和Spring AI 2.0开发，集成RAG知识库和简历分析功能。",
      "content_html": "<p>很多小伙伴跟我反馈：&quot;我的简历上全是增删改查（CRUD），面试官看都不看，怎么办？&quot;</p>\n<p>既然 AI 浪潮已至，我们就直接把大模型能力、向量数据库、RAG 架构装进你的项目里。</p>\n<h2>项目介绍</h2>\n<p>这是一个基于 Spring Boot 4.0 + Java 21 + Spring AI 2.0 的 AI 智能面试辅助平台。系统提供三大核心功能：</p>\n<ol>\n<li><strong>智能简历分析</strong>：上传简历后，AI 自动进行多维度评分并给出改进建议</li>\n<li><strong>模拟面试系统</strong>：基于简历内容生成个性化面试题，支持实时问答和答案评估</li>\n<li><strong>RAG 知识库问答</strong>：上传技术文档构建私有知识库，支持向量检索增强的智能问答</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/page-resume-history.png\" alt=\"效果展示\"></p>\n<p><strong>项目地址</strong> （欢迎 star 鼓励）：</p>\n<ul>\n<li>Github：<a href=\"https://github.com/Snailclimb/interview-guide\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/Snailclimb/interview-guide</a></li>\n<li>Gitee：<a href=\"https://gitee.com/SnailClimb/interview-guide\" target=\"_blank\" rel=\"noopener noreferrer\">https://gitee.com/SnailClimb/interview-guide</a></li>\n</ul>\n<p>完整代码完全免费开源，没有 Pro 版本或者付费版！</p>\n<h2>简历写法</h2>\n<p><strong>如何将《SpringAI 智能面试平台+RAG知识库》实战项目写进简历？</strong>我一共提供了五大方向版本任选，精准匹配岗位需求：</p>\n<ol>\n<li><strong>后端方向</strong>：提供&quot;架构与分布式能力侧重&quot;、&quot;AI 应用与响应式编程侧重&quot;、&quot;工程化与基础设施侧重&quot;三个版本，无论你面试的是后端、大模型应用还是架构岗位，都能找到最合适的切入点。</li>\n<li><strong>测试/测开方向</strong>：专门设计了&quot;单元测试与 TDD&quot;以及&quot;功能/异常场景覆盖&quot;两个版本，突出测试工程师在 AI 质量保障中的核心竞争力。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/project-on-resume.png\" alt=\"《SpringAI 智能面试平台+RAG知识库》简历写法\"></p>\n<p>每一条描述都紧扣项目真实逻辑，严格遵守项目介绍规范。不仅教你怎么写，更教你怎么补，例如针对本项目未涉及的&quot;用户认证与鉴权&quot;给出补充建议，教你如何基于 SpringSecurity/Sa-Token 包装主流的认证授权方案。</p>\n<p>并且，我还补充了面试官可深挖的技术难点（如Redis Stream vs 传统消息队列<strong>、</strong>分布式限流的实现细节）以及项目难点与解决方案模板。</p>\n<h2>教程概览</h2>\n<p>带大家看看我写的配套教程，用心程度一切都在文字中！整个项目教程，我手绘了几十张技术配图帮助理解。</p>\n<p>例如，RAG 面试题总结这篇，耗时一周终于完成了第一版，一共 <strong>3.4 万字</strong>，包含 <strong>35 道高频 RAG 面试题</strong>，光校对都进行了三次。而且，这还只是第一版，后续还会继续完善优化！</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/rag-interview-questions.png\" alt=\"RAG 面试题\"></p>\n<p>这篇是对应的 RAG 知识库详细开发思路的介绍。</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/rag-knowledge-base-coding.png\" alt=\"RAG 知识库详细开发思路\"></p>\n<p>不仅教你&quot;如何写出代码&quot;，更教你&quot;为什么这么设计&quot;以及&quot;在企业真实场景中如何应对复杂挑战&quot;。</p>\n<h2>配套教程内容安排</h2>\n<p>这个项目当前实现的功能比较简单，学习门槛极低，但涉及到的知识点比较丰富。通过保姆级教程，我们将从零构建一个融合了 <strong>LLM 集成、RAG（检索增强生成）、向量数据库、分布式限流及异步处理</strong>的完整后端架构。</p>\n<p>无论你是想学习 <strong>Spring AI</strong> 的前沿应用，还是需要一个<strong>高含金量的简历项目</strong>，本项目都将为你提供从基建搭建、业务攻坚到面试话术复盘的全方位指导。</p>\n<p>配套项目教程需要付费（<strong>后文/文末</strong>有加入方法），但请大家理解，主要是想覆盖一些时间成本。而且，收费和提供的服务相比绝对是超级良心了。这辈子不可能干割韭菜的事！</p>\n<p><strong>内容安排如下（已经更完，一共 13w+ 字）</strong>：</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/tutorial-overview.png\" alt=\"配套教程内容概览\"></p>\n<h3>环境搭建</h3>\n<ul>\n<li>本地搭建 PostgreSQL + PGvector 向量数据库</li>\n<li>Spring Boot + RustFS 构建高性能 S3 兼容的对象存储服务</li>\n<li>⭐大模型 API 申请和 Ollama 部署本地模型</li>\n<li>环境搭建终章与项目启动</li>\n</ul>\n<h3>核心功能开发</h3>\n<ul>\n<li>基于 Tika 实现多格式内容提取与解析</li>\n<li>⭐Spring AI 与大模型集成</li>\n<li>⭐Spring AI + pgvector 实现 RAG 知识库问答</li>\n<li>基于 SSE 实现打字机效果输出</li>\n<li>手把手教你写出生产级结构化 Prompt</li>\n<li>AI 模拟面试功能</li>\n<li>基于 iText 8 实现 PDF 报告导出</li>\n</ul>\n<h3>进阶优化</h3>\n<ul>\n<li>MapStruct 实体映射最佳实践</li>\n<li>⭐基于 Redis Stream 的异步任务处理实现</li>\n<li>封装 Redis + Lua 多维度分布式限流组件</li>\n<li>Spring Boot 4.0 升级指南</li>\n<li>Docker Compose 一键部署</li>\n</ul>\n<h3>面试</h3>\n<ul>\n<li>⭐简历编写与项目经历深度包装指南</li>\n<li>面试官问&quot;这个项目哪里来的&quot;时，如何回答？</li>\n<li>⭐Spring AI 面试问题挖掘</li>\n<li>⭐知识库 RAG 面试问题挖掘</li>\n<li>Redis 面试问题挖掘</li>\n<li>文件上传解析与 PDF 导出面试问题挖掘</li>\n</ul>\n<h2>加入学习</h2>\n<p><strong>本项目为 <a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">JavaGuide 知识星球</a> 内部专属实战项目，通过语雀文档在线阅读学习，不单独对外开放。</strong></p>\n<p>之所以选择在星球内部发布，是为了确保每一位学习者都能获得<strong>深度的技术答疑</strong>和<strong>完整的求职配套服务</strong>。</p>\n<p>整个项目教程预计在 <strong>1-2</strong> 个月内更完。每一篇文章（不提供视频，浪费时间且不利于学习能力提高）都经过反复推敲，确保<strong>高质量、零门槛</strong>，即便是基础薄弱的同学也能跟着文档从零跑通。</p>\n<p>这只是开始。后续星球还会持续推出更多贴合企业真实业务场景的 <strong>Java 实战项目</strong>，带你始终站在技术前沿（预告一下，下一个项目是<strong>企业级智能客服系统</strong>，会带大家实践更多AI能力）。</p>\n<p>并且，我的星球还有很多其他服务，比如<strong>一对一提问、简历修改、后端系统面试资料（包含高频系统设计&amp;场景题）、学习打卡</strong>等，其中任何一项服务单独拎出来的价值都已远超星球门票。欢迎详细了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>！</p>\n<p>已经坚持维护<strong>六年</strong>，内容持续更新，虽白菜价（<strong>0.4 元/天</strong>）但质量很高，主打一个良心！</p>\n<p>目前星球正在做活动，两本书的价格，就能让你拥有上万培训班的服务！这里再提供一张 **30 ** 元的优惠卷（价格马上上调，老用户扫码续费半价 ）：</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuan-30.jpg\" alt=\"知识星球30元优惠卷\"></p>\n<p>用心做内容，坚持本心，不割韭菜，其他交给时间！共勉！</p>\n<h2>系统架构</h2>\n<p><strong>提示</strong>：架构图采用 <a href=\"http://draw.io\" target=\"_blank\" rel=\"noopener noreferrer\">draw.io</a> 绘制，导出为 svg 格式，在 Dark 模式下的显示效果会有问题。</p>\n<p>系统采用前后端分离架构，整体分为三层：前端展示层、后端服务层、数据存储层。</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/interview-guide-architecture-diagram.png\" alt=\"系统架构图\"></p>\n<p><strong>后端层</strong>：</p>\n<ul>\n<li>REST Controllers：统一的 API 入口，处理 HTTP 请求</li>\n<li>业务服务层：\n<ul>\n<li>Resume Service：简历上传、解析、AI 分析\n<ul>\n<li>Interview Service：面试会话管理、问题生成、答案评估</li>\n<li>Knowledge Service：知识库上传、文本分块、向量化</li>\n<li>RAG Chat Service：检索增强生成，流式问答</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>异步处理层：基于 Redis Stream 的消费者，异步处理耗时的 AI 任务（如简历分析、向量化、面试评估）</li>\n<li>AI 集成层：Spring AI + DashScope（通义千问）。统一的 LLM 调用接口，支持对话生成和文本向量化。</li>\n</ul>\n<p><strong>数据存储层</strong>：</p>\n<ul>\n<li>\n<p>PostgreSQL + pgvector：</p>\n<ul>\n<li>关系数据：简历、面试记录、知识库元数据</li>\n<li>向量检索：存储文档向量，支持相似度搜索</li>\n</ul>\n</li>\n<li>\n<p>Redis：</p>\n<ul>\n<li>会话缓存：面试会话状态</li>\n<li>消息队列：Redis Stream 实现异步任务队列</li>\n</ul>\n</li>\n<li>\n<p>RustFS/MinIO (S3)：原始文件（简历 PDF、知识库文档）</p>\n</li>\n</ul>\n<p><strong>异步处理流程</strong>：</p>\n<p>简历分析、知识库向量化和面试报告生成采用 Redis Stream 异步处理，这里以简历分析和知识库向量化为例介绍一下整体流程：</p>\n<div class=\"language- line-numbers-mode\" data-highlighter=\"shiki\" data-ext style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-\"><span class=\"line\"><span>上传请求 → 保存文件 → 发送消息到 Stream → 立即返回</span></span>\n<span class=\"line\"><span>                              ↓</span></span>\n<span class=\"line\"><span>                      Consumer 消费消息</span></span>\n<span class=\"line\"><span>                              ↓</span></span>\n<span class=\"line\"><span>                    执行分析/向量化任务</span></span>\n<span class=\"line\"><span>                              ↓</span></span>\n<span class=\"line\"><span>                      更新数据库状态</span></span>\n<span class=\"line\"><span>                              ↓</span></span>\n<span class=\"line\"><span>                   前端轮询获取最新状态</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>状态流转： <code>PENDING</code> → <code>PROCESSING</code> → <code>COMPLETED</code> / <code>FAILED</code></p>\n<p><strong>知识库问答处理流程</strong>：</p>\n<div class=\"language- line-numbers-mode\" data-highlighter=\"shiki\" data-ext style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-\"><span class=\"line\"><span>知识库问答 → 问题向量化 → pgvector 相似度搜索 → 检索相关文档</span></span>\n<span class=\"line\"><span>                                                      ↓</span></span>\n<span class=\"line\"><span>                                构建 Prompt → LLM 生成回答 → SSE 流式返回</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>技术栈概览</h2>\n<h3>后端技术</h3>\n<p>| 技术                  | 版本  | 说明                      |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/xingqiu/pratical-project/interview-guide/page-resume-history.png",
      "date_published": "2026-01-12T06:25:02.000Z",
      "date_modified": "2026-04-08T07:23:21.000Z",
      "authors": [],
      "tags": [
        "知识星球"
      ]
    },
    {
      "title": "一致性哈希算法详解",
      "url": "https://javaguide.cn/distributed-system/protocol/consistent-hashing.html",
      "id": "https://javaguide.cn/distributed-system/protocol/consistent-hashing.html",
      "summary": "一致性哈希算法原理详解，讲解哈希环、虚拟节点机制、数据倾斜问题解决方案，以及在分布式缓存（Redis/Memcached）、负载均衡、分库分表中的应用场景。",
      "content_html": "<p>开始之前，先说两个常见的场景：</p>\n<ol>\n<li><strong>负载均衡</strong>：由于访问人数太多，我们的网站部署了多台服务器个共同提供相同的服务，但每台服务器上存储的数据不同。为了保证请求的正确响应，相同参数（key）的请求（比如同个 IP 的请求、同一个用户的请求）需要发到同一台服务器处理。</li>\n<li><strong>分布式缓存</strong>：由于缓存数据量太大，我们部署了多台缓存服务器共同提供缓存服务。缓存数据需要尽可能均匀地分布式在这些缓存服务器上，通过 key 可以找到对应的缓存服务器。</li>\n</ol>\n<p>这两种场景的本质，都是需要建立一个<strong>从 key 到服务器/节点的稳定映射关系</strong>。</p>\n<p>为了实现这个目标，你首先会想到什么方案呢？</p>\n<h2>普通哈希算法</h2>\n<p>相信大家很快就能想到 <strong>“哈希+取模”</strong> 这个经典组合。通过哈希函数计算出 key 的哈希值，再对服务器数量取模，从而将 key 映射到固定的服务器上。</p>\n<p>公式也很简单：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">node_number </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> hash</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(key) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">%</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> N</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><ul>\n<li><code>hash(key)</code>: 使用哈希函数（建议使用性能较好的非加密哈希函数，例如 SipHash、MurMurHash3、CRC32、DJB）对唯一键进行哈希。</li>\n<li><code>% N</code>: 对哈希值取模，将哈希值映射到一个介于 0 到 N-1 之间的值，N 为节点数/服务器数。</li>\n</ul>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/hashqumo.png\" alt=\"哈希取模\"></p>\n<p>然而，传统的哈希取模算法有一个比较大的缺陷就是：<strong>无法很好的解决机器/节点动态减少（比如某台机器宕机）或者增加的场景（比如又增加了一台机器）。</strong></p>\n<p>想象一下，服务器的初始数量为 4 台 (N = 4)，如果其中一台服务器宕机，N 就变成了 3。此时，对于同一个 key，<code>hash(key) % 3</code> 的结果很可能与 <code>hash(key) % 4</code> 完全不同。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/hashqumo-remove-node2.png\" alt=\"哈希取模-移除节点Node2\"></p>\n<p>这意味着几乎所有的数据映射关系都会错乱。在分布式缓存场景下，这会导致<strong>大规模的缓存失效和缓存穿透</strong>，瞬间将压力全部打到后端的数据库上，引发系统雪崩。</p>\n<p>据估算，当节点数量从 N 变为 N-1 时，平均有 (N-1)/N 比例的数据需要迁移，这个比例 <strong>趋近于 100%</strong> 。这种“牵一发而动全身”的效应，在生产环境中是完全不可接受的。</p>\n<p>为了更好地解决这个问题，一致性哈希算法诞生了。</p>\n<h2>一致性哈希算法</h2>\n<p>一致性哈希算法在 1997 年由麻省理工学院提出（这篇论文的 PDF 在线阅读地址：<a href=\"https://www.cs.princeton.edu/courses/archive/fall09/cos518/papers/chash.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.cs.princeton.edu/courses/archive/fall09/cos518/papers/chash.pdf</a>），是一种特殊的哈希算法，在移除或者添加一个服务器时，能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了传统哈希算法在分布式<a href=\"https://baike.baidu.com/item/%E5%93%88%E5%B8%8C%E8%A1%A8/5981869\" target=\"_blank\" rel=\"noopener noreferrer\">哈希表</a>（Distributed Hash Table，DHT）中存在的动态伸缩等问题 。</p>\n<p>一致性哈希算法的底层原理也很简单，关键在于<strong>哈希环</strong>的引入。</p>\n<h3>哈希环</h3>\n<p>一致性哈希算法将哈希空间组织成一个环形结构，将数据和节点都映射到这个环上，然后根据顺时针的规则确定数据或请求应该分配到哪个节点上。通常情况下，哈希环的起点是 0，终点是 2^32 - 1，并且起点与终点连接，故这个环的整数分布范围是 <strong>[0, 2^32-1]</strong> 。</p>\n<p>传统哈希算法是对服务器数量取模，一致性哈希算法是对哈希环的范围取模，固定值，通常为 2^32：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">node_number </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> hash</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(key) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">%</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">^</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">32</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>服务器/节点如何映射到哈希环上呢？也是哈希取模。例如，一般我们会根据服务器的 IP 或者主机名进行哈希，然后再取模。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">hash（服务器ip）</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">%</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">^</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">32</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>如下图所示：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/consistent-hashing-circle.png\" alt=\"哈希环\"></p>\n<p>我们将数据和节点都映射到哈希环上，环上的每个节点都负责一个区间。对于上图来说，每个节点负责的数据情况如下：</p>\n<ul>\n<li><strong>Node1:</strong> 负责 Node4 到 Node1 之间的区域（包含 value6）。</li>\n<li><strong>Node2:</strong> 负责 Node1 到 Node2 之间的区域（包含 value1, value2）。</li>\n<li><strong>Node3:</strong> 负责 Node2 到 Node3 之间的区域（包含 value3）。</li>\n<li><strong>Node4:</strong> 负责 Node3 到 Node4 之间的区域（包含 value4, value5）。</li>\n</ul>\n<h3>节点移除/增加</h3>\n<p>新增节点和移除节点的情况下，哈希环的引入可以避免影响范围太大，减少需要迁移的数据。</p>\n<p>还是用上面分享的哈希环示意图为例，假设 Node2 节点被移除的话，那 Node3 就要负责 Node2 的数据，直接迁移 Node2 的数据到 Node3 即可，其他节点不受影响。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/consistent-hashing-circle-remove-node2.png\" alt=\"节点移除\"></p>\n<p>同样地，如果我们在 Node1 和 Node2 之间新增一个节点 Node5，那么原本应该由 Node2 负责的一部分数据（即哈希值落在 Node1 和 Node5 之间的数据，如图中的 value1）现在会由 Node5 负责。我们只需要将这部分数据从 Node2 迁移到 Node5 即可，同样只影响了相邻的节点，影响范围非常小。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/consistent-hashing-circle-add-node5.png\" alt=\"节点增加\"></p>\n<h3>数据倾斜问题</h3>\n<p>理想情况下，节点在环上是均匀分布的。然而，现实可能并不是这样的，尤其是节点数量比较少的时候。节点可能被映射到附近的区域，这样的话，就会导致绝大部分数据都由其中一个节点负责。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/consistent-hashing-circle-unbalance.png\" alt=\"数据倾斜\"></p>\n<p>对于上图来说，每个节点负责的数据情况如下：</p>\n<ul>\n<li><strong>Node1:</strong> 负责 Node4 到 Node1 之间的区域（包含 value6）。</li>\n<li><strong>Node2:</strong> 负责 Node1 到 Node2 之间的区域（包含 value1）。</li>\n<li><strong>Node3:</strong> 负责 Node2 到 Node3 之间的区域（包含 value2，value3， value4, value5）。</li>\n<li><strong>Node4:</strong> 负责 Node3 到 Node4 之间的区域。</li>\n</ul>\n<p>除了数据倾斜问题，还有一个隐患。当新增或者删除节点的时候，数据分配不均衡。例如，Node3 被移除的话，Node3 负责的所有数据都要交给 Node4，随后所有的请求都要达到 Node4 上。假设 Node4 的服务器处理能力比较差的话，那可能直接就被干崩了。理想情况下，应该有更多节点来分担压力。</p>\n<p>如何解决这些问题呢？答案是引入<strong>虚拟节点</strong>。</p>\n<h3>虚拟节点</h3>\n<p>虚拟节点就是对真实的物理节点在哈希环上虚拟出几个它的分身节点。数据落到分身节点上实际上就是落到真实的物理节点上，通过将虚拟节点均匀分散在哈希环的各个部分。</p>\n<p>如下图所示，Node1、Node2、Node3、Node4 这 4 个节点都对应 3 个虚拟节点（下图只是为了演示，实际情况节点分布不会这么有规律）。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/consistent-hashing-circle-virtual-node.png\" alt=\"虚拟节点\"></p>\n<p>对于上图来说，每个节点最终负责的数据情况如下：</p>\n<ul>\n<li><strong>Node1</strong>:value4</li>\n<li><strong>Node2</strong>:value1,value3</li>\n<li><strong>Node3</strong>:value5</li>\n<li><strong>Node4</strong>:value2,value6</li>\n</ul>\n<p><strong>引入虚拟节点的好处是巨大的：</strong></p>\n<ol>\n<li><strong>数据均衡：</strong> 虚拟节点越多，环上的“服务器点”就越密集，数据分布自然就越均匀，从根本上解决了数据倾斜问题。通常，每个真实节点对应的虚拟节点数在 100 到 200 之间，例如 Nginx 选择为每个权重分配 160 个虚拟节点。这里的权重的是为了区分服务器，例如处理能力更强的服务器权重越高，进而导致对应的虚拟节点越多，被命中的概率越大。</li>\n<li><strong>容错性增强：</strong> 这才是虚拟节点最精妙的地方。当一个物理节点宕机，它相当于在环上的多个虚拟节点同时下线。这些虚拟节点原本负责的数据和流量，会<strong>自然地、均匀地分散</strong>给环上其他<strong>多个不同</strong>的物理节点去接管，而不会将压力集中于某一个邻居节点。这极大地提升了系统的稳定性和容错能力。</li>\n</ol>\n<h2>参考</h2>\n<ul>\n<li>深入剖析 Nginx 负载均衡算法：<a href=\"https://www.taohui.tech/2021/02/08/nginx/%E6%B7%B1%E5%85%A5%E5%89%96%E6%9E%90Nginx%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E7%AE%97%E6%B3%95/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.taohui.tech/2021/02/08/nginx/深入剖析Nginx负载均衡算法/</a></li>\n<li>读源码学架构系列：一致性哈希：<a href=\"https://zhaoyang.me/posts/consistent-hash-algorithm/\" target=\"_blank\" rel=\"noopener noreferrer\">https://zhaoyang.me/posts/consistent-hash-algorithm/</a></li>\n<li>一致性 Hash 算法原理总结：<a href=\"https://mp.weixin.qq.com/s/WTz1KA9kOGrqFVTtALJzjQ\" target=\"_blank\" rel=\"noopener noreferrer\">https://mp.weixin.qq.com/s/WTz1KA9kOGrqFVTtALJzjQ</a></li>\n</ul>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/distributed-system/protocol/consistent-hashing/hashqumo.png",
      "date_published": "2025-10-29T09:03:08.000Z",
      "date_modified": "2026-03-12T08:34:17.000Z",
      "authors": [],
      "tags": [
        "分布式"
      ]
    },
    {
      "title": "Java 25 新特性概览",
      "url": "https://javaguide.cn/java/new-features/java25.html",
      "id": "https://javaguide.cn/java/new-features/java25.html",
      "summary": "概览 JDK 25 的关键新特性与预览改动，关注并发、GC 与语言/平台增强。",
      "content_html": "<p>JDK 25 于 2025 年 9 月 16 日 发布，这是一个非常重要的版本，里程碑式。</p>\n<p>JDK 25 是 LTS（长期支持版），至此为止，目前有 JDK8、JDK11、JDK17、JDK21 和 JDK 25 这五个长期支持版了。</p>\n<p>JDK 25 共有 18 个新特性，这篇文章会挑选其中较为重要的一些新特性进行详细介绍：</p>\n<ul>\n<li><a href=\"https://openjdk.org/jeps/506\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 506: Scoped Values (作用域值)</a></li>\n<li><a href=\"https://openjdk.org/jeps/512\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 512: Compact Source Files and Instance Main Methods (紧凑源文件与实例主方法)</a></li>\n<li><a href=\"https://openjdk.org/jeps/519\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 519: Compact Object Headers (紧凑对象头)</a></li>\n<li><a href=\"https://openjdk.org/jeps/521\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 521: Generational Shenandoah (分代 Shenandoah GC)</a></li>\n<li><a href=\"https://openjdk.org/jeps/507\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 507: Primitive Types in Patterns, instanceof, and switch (模式匹配支持基本类型, 第三次预览)</a></li>\n<li><a href=\"https://openjdk.org/jeps/505\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 505: Structured Concurrency (结构化并发, 第五次预览)</a></li>\n<li><a href=\"https://openjdk.org/jeps/511\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 511: Module Import Declarations (模块导入声明)</a></li>\n<li><a href=\"https://openjdk.org/jeps/513\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 513: Flexible Constructor Bodies (灵活的构造函数体)</a></li>\n<li><a href=\"https://openjdk.org/jeps/508\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 508: Vector API (向量 API, 第十次孵化)</a></li>\n</ul>\n<p>下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png\" alt></p>\n<h2>JDK 25</h2>\n<h3>JEP 506: 作用域值</h3>\n<p>作用域值（Scoped Values）可以在线程内和线程间共享不可变的数据，优于线程局部变量 <code>ThreadLocal</code> ，尤其是在使用大量虚拟线程时。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ScopedValue</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;...></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> V </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ScopedValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// In some method</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ScopedValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">where</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(V, </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">           .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(() </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> { ... </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() ... </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">call</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> methods ... });</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// In a method called directly or indirectly from the lambda expression</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ...</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>作用域值通过其“写入时复制”(copy-on-write)的特性，保证了数据在线程间的隔离与安全，同时性能极高，占用内存也极低。这个特性将成为未来 Java 并发编程的标准实践。</p>\n<h3>JEP 512: 紧凑源文件与实例主方法</h3>\n<p>该特性第一次预览是由 <a href=\"https://openjdk.org/jeps/445\" title=\"JEP 445\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 445</a> （JDK 21 ）提出，随后经过了 JDK 22 、JDK 23 和 JDK 24 的改进和完善，最终在 JDK 25 顺利转正。</p>\n<p>这个改进极大地简化了编写简单 Java 程序的步骤，允许将类和主方法写在同一个没有顶级 <code>public class</code>的文件中，并允许 <code>main</code> 方法成为一个非静态的实例方法。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> HelloWorld</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello, World!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>进一步简化：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello, World!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这是为了降低 Java 的学习门槛和提升编写小型程序、脚本的效率而迈出的一大步。初学者不再需要理解 <code>public static void main(String[] args)</code> 这一长串复杂的声明。对于快速原型验证和脚本编写，这也使得 Java 成为一个更有吸引力的选择。</p>\n<h3>JEP 519: 紧凑对象头</h3>\n<p>该特性第一次预览是由 <a href=\"https://openjdk.org/jeps/450\" title=\"JEP 450\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 450</a> （JDK 24 ）提出，JDK 25 就顺利转正了。</p>\n<p>通过优化对象头的内部结构，在 64 位架构的 HotSpot 虚拟机中，将对象头大小从原本的 96-128 位（12-16 字节）缩减至 64 位（8 字节），最终实现减少堆内存占用、提升部署密度、增强数据局部性的效果。</p>\n<p>紧凑对象头并没有成为 JVM 默认的对象头布局方式，需通过显式配置启用：</p>\n<ul>\n<li>JDK 24 需通过命令行参数组合启用：<br>\n<code>$ java -XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders ...</code> ；</li>\n<li>JDK 25 之后仅需 <code>-XX:+UseCompactObjectHeaders</code> 即可启用。</li>\n</ul>\n<h3>JEP 521: 分代 Shenandoah GC</h3>\n<p>Shenandoah GC 在 JDK12 中成为正式可生产使用的 GC，默认关闭，通过 <code>-XX:+UseShenandoahGC</code> 启用。</p>\n<p>Redhat 主导开发的 Pauseless GC 实现，主要目标是 99.9% 的暂停小于 10ms，暂停与堆大小无关等</p>\n<p>传统的 Shenandoah 对整个堆进行并发标记和整理，虽然暂停时间极短，但在处理年轻代对象时效率不如分代 GC。引入分代后，Shenandoah 可以更频繁、更高效地回收年轻代中的大量“朝生夕死”的对象，使其在保持极低暂停时间的同时，拥有了更高的吞吐量和更低的 CPU 开销。</p>\n<p>Shenandoah GC 需要通过命令启用：</p>\n<ul>\n<li>JDK 24 需通过命令行参数组合启用：<code>-XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational</code></li>\n<li>JDK 25 之后仅需 <code>-XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational</code> 即可启用。</li>\n</ul>\n<h3>JEP 507: 模式匹配支持基本类型 (第三次预览)</h3>\n<p>该特性第一次预览是由 <a href=\"https://openjdk.org/jeps/455\" title=\"JEP 455\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 455</a> （JDK 23 ）提出。</p>\n<p>模式匹配可以在 <code>switch</code> 和 <code>instanceof</code> 语句中处理所有的基本数据类型（<code>int</code>, <code>double</code>, <code>boolean</code> 等）</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> test</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> obj) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (obj </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"这是一个int类型: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这样就可以像处理对象类型一样，对基本类型进行更安全、更简洁的类型匹配和转换，进一步消除了 Java 中的模板代码。</p>\n<h3>JEP 505: 结构化并发(第五次预览)</h3>\n<p>JDK 19 引入了结构化并发，一种多线程编程方法，目的是为了通过结构化并发 API 来简化多线程编程，并不是为了取代<code>java.util.concurrent</code>，目前处于孵化器阶段。</p>\n<p>结构化并发将不同线程中运行的多个任务视为单个工作单元，从而简化错误处理、提高可靠性并增强可观察性。也就是说，结构化并发保留了单线程代码的可读性、可维护性和可观察性。</p>\n<p>结构化并发的基本 API 是<code>StructuredTaskScope</code>，它支持将任务拆分为多个并发子任务，在它们自己的线程中执行，并且子任务必须在主任务继续之前完成。</p>\n<p><code>StructuredTaskScope</code> 的基本用法如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> scope </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> StructuredTaskScope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 使用fork方法派生线程来执行子任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Future</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> future1 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fork</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task1);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Future</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> future2 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fork</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task2);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 等待线程完成</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">join</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 结果的处理可能包括处理或重新抛出异常</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        ...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> process</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> results</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">exceptions </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// close</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>结构化并发非常适合虚拟线程，虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程，从而允许非常多的虚拟线程。</p>\n<h3>JEP 511: 模块导入声明</h3>\n<p>该特性第一次预览是由 <a href=\"https://openjdk.org/jeps/476\" title=\"JEP 476\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 476</a> （JDK 23 ）提出，随后在 <a href=\"https://openjdk.org/jeps/494\" title=\"JEP 494\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 494</a> （JDK 24）中进行了完善，JDK 25 顺利转正。</p>\n<p>模块导入声明允许在 Java 代码中简洁地导入整个模块的所有导出包，而无需逐个声明包的导入。这一特性简化了模块化库的重用，特别是在使用多个模块时，避免了大量的包导入声明，使得开发者可以更方便地访问第三方库和 Java 基本类。</p>\n<p>此特性对初学者和原型开发尤为有用，因为它无需开发者将自己的代码模块化，同时保留了对传统导入方式的兼容性，提升了开发效率和代码可读性。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 导入整个 java.base 模块，开发者可以直接访问 List、Map、Stream 等类，而无需每次手动导入相关包</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> module java.base</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Example</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">fruits</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> { </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"apple\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"berry\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"citrus\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> };</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Map</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">fruitMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Stream</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">of</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(fruits)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">collect</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Collectors</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                s </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> s</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toUpperCase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">substring</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">),</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                Function</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">identity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()));</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(fruitMap);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>JEP 513: 灵活的构造函数体</h3>\n<p>该特性第一次预览是由 <a href=\"https://openjdk.org/jeps/447\" title=\"JEP 447\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 447</a> （JDK 22）提出，随后在 <a href=\"https://openjdk.org/jeps/482\" title=\"JEP 482 \" target=\"_blank\" rel=\"noopener noreferrer\">JEP 482 </a>（JDK 23）和 <a href=\"https://openjdk.org/jeps/492\" title=\"JEP 492\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 492</a> （JDK 24）经历了预览，JDK 25 顺利转正。</p>\n<p>Java 要求在构造函数中，<code>super(...)</code> 或 <code>this(...)</code> 调用必须作为第一条语句出现。这意味着我们无法在调用父类构造函数之前在子类构造函数中直接初始化字段。</p>\n<p>灵活的构造函数体解决了这一问题，它允许在构造函数体内，在调用 <code>super(..)</code> 或 <code>this(..)</code> 之前编写语句，这些语句可以初始化字段，但不能引用正在构造的实例。这样可以防止在父类构造函数中调用子类方法时，子类的字段未被正确初始化，增强了类构造的可靠性。</p>\n<p>这一特性解决了之前 Java 语法限制了构造函数代码组织的问题，让开发者能够更自由、更自然地表达构造函数的行为，例如在构造函数中直接进行参数验证、准备和共享，而无需依赖辅助方法或构造函数，提高了代码的可读性和可维护性。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Person</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> age</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Person</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> age</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (age </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> IllegalArgumentException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Age cannot be negative.\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> name; </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 在调用父类构造函数之前初始化字段</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">age</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> age;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ... 其他初始化代码</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Employee</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Person</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> employeeId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Employee</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> age</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> employeeId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">employeeId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> employeeId; </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 在调用父类构造函数之前初始化字段</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        super</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(name, age); </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 调用父类构造函数</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ... 其他初始化代码</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>JEP 508: 向量 API(第十次孵化)</h3>\n<p>向量计算由对向量的一系列操作组成。向量 API 用来表达向量计算，该计算可以在运行时可靠地编译为支持的 CPU 架构上的最佳向量指令，从而实现优于等效标量计算的性能。</p>\n<p>向量 API 的目标是为用户提供简洁易用且与平台无关的表达范围广泛的向量计算。</p>\n<p>这是对数组元素的简单标量计算：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> scalarComputation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">   for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        c[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i]) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1.0f</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">   }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这是使用 Vector API 进行的等效向量计算：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> VectorSpecies</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Float</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> SPECIES </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> FloatVector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">SPECIES_PREFERRED</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> vectorComputation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> upperBound </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> SPECIES</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">loopBound</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> upperBound</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> SPECIES</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // FloatVector va, vb, vc;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> va </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> FloatVector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fromArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(SPECIES, a, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> vb </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> FloatVector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fromArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(SPECIES, b, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> vc </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> va</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">mul</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(va)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                   .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">vb</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">mul</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(vb))</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                   .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">neg</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        vc</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">intoArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(c, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        c[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i]) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1.0f</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>尽管仍在孵化中，但其第十次迭代足以证明其重要性。它使得 Java 在科学计算、机器学习、大数据处理等性能敏感领域，能够编写出接近甚至媲美 C++等本地语言性能的代码。这是 Java 在高性能计算领域保持竞争力的关键。</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png",
      "date_published": "2025-09-18T12:54:50.000Z",
      "date_modified": "2026-03-21T06:28:26.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "为什么前后端都要做数据校验？",
      "url": "https://javaguide.cn/system-design/security/data-validation.html",
      "id": "https://javaguide.cn/system-design/security/data-validation.html",
      "summary": "前后端数据校验必要性详解，讲解参数校验、权限校验的重要性及防止绕过前端校验的安全防护措施。",
      "content_html": "<blockquote>\n<p>相关面试题：</p>\n<ul>\n<li>前端做了校验，后端还还需要做校验吗？</li>\n<li>前端已经做了数据校验，为什么后端还需要再做一遍同样（甚至更严格）的校验呢？</li>\n<li>前端/后端需要对哪些内容进行校验？</li>\n</ul>\n</blockquote>\n<p>咱们平时做 Web 开发，不管是写前端页面还是后端接口，都离不开跟数据打交道。那怎么保证这些传来传去的数据是靠谱的、安全的呢？这就得靠<strong>数据校验</strong>了。而且，这活儿，前端得干，后端<strong>更得干</strong>，还得加上<strong>权限校验</strong>这道重要的“锁”，缺一不可！</p>\n<p>为啥这么说？你想啊，前端校验主要是为了用户体验和挡掉一些明显的“瞎填”数据，但懂点技术的人绕过前端校验简直不要太轻松（比如直接用 Postman 之类的工具发请求）。所以，<strong>后端校验才是咱们系统安全和数据准确性的最后一道，也是最硬核的防线</strong>。它得确保进到系统里的数据不仅格式对，还得符合业务规矩，最重要的是，执行这个操作的人得有<strong>权限</strong>！</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/user-input-validation.png\" alt></p>\n<h2>前端校验</h2>\n<p>前端校验就像个贴心的门卫，主要目的是在用户填数据的时候，就赶紧告诉他哪儿不对，让他改，省得提交了半天，结果后端说不行，还得重来。这样做的好处显而易见：</p>\n<ol>\n<li><strong>用户体验好：</strong> 输入时就有提示，错了马上知道，改起来方便，用户感觉流畅不闹心。</li>\n<li><strong>减轻后端压力：</strong> 把一些明显格式错误、必填项没填的数据在前端就拦下来，减少了发往后端的无效请求，省了服务器资源和网络流量。需要注意的是，后端同样还是要校验，只是加上前端校验可以减少很多无效请求。</li>\n</ol>\n<p>那前端一般都得校验点啥呢？</p>\n<ul>\n<li><strong>必填项校验:</strong> 最基本的，该填的地儿可不能空着。</li>\n<li><strong>格式校验:</strong> 比如邮箱得像个邮箱样儿 (<a href=\"mailto:xxx@xx.com\" target=\"_blank\" rel=\"noopener noreferrer\">xxx@xx.com</a>)，手机号得是 11 位数字等。正则表达式这时候就派上用场了。</li>\n<li><strong>重复输入校验：</strong> 确保两次输入的内容一致，例如注册时的“确认密码”字段。</li>\n<li><strong>范围/长度校验:</strong> 年龄不能是负数吧？密码长度得在 6 到 20 位之间吧？这种都得看着。</li>\n<li><strong>合法性/业务校验:</strong> 比如用户名是不是已经被注册了？选的商品还有没有库存？这得根据具体业务来，需要配合后端来做。</li>\n<li><strong>文件上传校验：</strong>限制文件类型（如仅支持 <code>.jpg</code>、<code>.png</code> 格式）和文件大小。</li>\n<li><strong>安全性校验:</strong> 防范像 XSS（跨站脚本攻击）这种坏心思，对用户输入的东西做点处理，别让人家写的脚本在咱们页面上跑起来。</li>\n<li>...等等，根据业务需求来。</li>\n</ul>\n<p>总之，前端校验的核心是 <strong>引导用户正确输入</strong> 和 <strong>提升交互体验</strong>。</p>\n<h2>后端校验</h2>\n<p>前端校验只是第一道防线，虽然提升了用户体验，但毕竟可以被绕过，真正起决定性作用的是后端校验。后端需要对所有前端传来的数据都抱着“可能有问题”的态度，进行全面审查。后端校验不仅要覆盖前端的基本检查（如格式、范围、长度等），还需要更严格、更深入的验证，确保系统的安全性和数据的一致性。以下是后端校验的重点内容：</p>\n<ol>\n<li><strong>完整性校验:</strong> 接口文档中明确要求的字段必须存在，例如 <code>userId</code> 和 <code>orderId</code>。如果缺失任何必需字段，后端应立即返回错误，拒绝处理请求。</li>\n<li><strong>合法性/存在性校验:</strong> 验证传入的数据是否真实有效。例如，传过来的 <code>productId</code> 是否存在于数据库中？<code>couponId</code> 是否已经过期或被使用？这通常需要通过查库或调用其他服务来确认。</li>\n<li><strong>一致性校验:</strong> 针对涉及多个数据对象的操作，验证它们是否符合业务逻辑。例如，更新订单状态前，需要确保订单的当前状态允许修改，不能直接从“未支付”跳到“已完成”。一致性校验是保证数据流转正确性的关键。</li>\n<li><strong>安全性校验:</strong> 后端必须防范各种恶意攻击，包括但不限于 XSS、SQL 注入等。所有外部输入都应进行严格的过滤和验证，例如使用参数化查询防止 SQL 注入，或对返回的 HTML 数据进行转义，避免跨站脚本攻击。</li>\n<li>...基本上，前端能做的校验，后端为了安全都得再来一遍。</li>\n</ol>\n<p>在 Java 后端，每次都手写 if-else 来做这些基础校验太累了。好在 Java 社区给我们提供了 <strong>Bean Validation</strong> 这套标准规范。它允许我们用<strong>注解</strong>的方式，直接在 JavaBean（比如我们的 DTO 对象）的属性上声明校验规则，非常方便。</p>\n<ul>\n<li><strong>JSR 303 (1.0):</strong> 打下了基础，引入了 <code>@NotNull</code>, <code>@Size</code>, <code>@Min</code>, <code>@Max</code> 这些老朋友。</li>\n<li><strong>JSR 349 (1.1):</strong> 增加了对方法参数和返回值的校验，还有分组校验等增强。</li>\n<li><strong>JSR 380 (2.0):</strong> 拥抱 Java 8，支持了新的日期时间 API，还加了 <code>@NotEmpty</code>, <code>@NotBlank</code>, <code>@Email</code> 等更实用的注解。</li>\n</ul>\n<p>早期的 Spring Boot (大概 2.3.x 之前): spring-boot-starter-web 里自带了 <code>hibernate-validator</code>，你啥都不用加。</p>\n<p>Spring Boot 2.3.x 及之后: 为了更灵活，校验相关的依赖被单独拎出来了。你需要手动添加 <code>spring-boot-starter-validation</code> 依赖：</p>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>org.springframework.boot&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>spring-boot-starter-validation&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>Bean Validation 规范及其实现（如 Hibernate Validator）提供了丰富的注解，用于声明式地定义校验规则。以下是一些常用的注解及其说明：</p>\n<ul>\n<li><code>@NotNull</code>: 检查被注解的元素（任意类型）不能为 <code>null</code>。</li>\n<li><code>@NotEmpty</code>: 检查被注解的元素（如 <code>CharSequence</code>、<code>Collection</code>、<code>Map</code>、<code>Array</code>）不能为 <code>null</code> 且其大小/长度不能为 0。注意：对于字符串，<code>@NotEmpty</code> 允许包含空白字符的字符串，如 <code>&quot; &quot;</code>。</li>\n<li><code>@NotBlank</code>: 检查被注解的 <code>CharSequence</code>（如 <code>String</code>）不能为 <code>null</code>，并且去除首尾空格后的长度必须大于 0。（即，不能为空白字符串）。</li>\n<li><code>@Null</code>: 检查被注解的元素必须为 <code>null</code>。</li>\n<li><code>@AssertTrue</code> / <code>@AssertFalse</code>: 检查被注解的 <code>boolean</code> 或 <code>Boolean</code> 类型元素必须为 <code>true</code> / <code>false</code>。</li>\n<li><code>@Min(value)</code> / <code>@Max(value)</code>: 检查被注解的数字类型（或其字符串表示）的值必须大于等于 / 小于等于指定的 <code>value</code>。适用于整数类型（<code>byte</code>、<code>short</code>、<code>int</code>、<code>long</code>、<code>BigInteger</code> 等）。</li>\n<li><code>@DecimalMin(value)</code> / <code>@DecimalMax(value)</code>: 功能类似 <code>@Min</code> / <code>@Max</code>，但适用于包含小数的数字类型（<code>BigDecimal</code>、<code>BigInteger</code>、<code>CharSequence</code>、<code>byte</code>、<code>short</code>、<code>int</code>、<code>long</code>及其包装类）。 <code>value</code> 必须是数字的字符串表示。</li>\n<li><code>@Size(min=, max=)</code>: 检查被注解的元素（如 <code>CharSequence</code>、<code>Collection</code>、<code>Map</code>、<code>Array</code>）的大小/长度必须在指定的 <code>min</code> 和 <code>max</code> 范围之内（包含边界）。</li>\n<li><code>@Digits(integer=, fraction=)</code>: 检查被注解的数字类型（或其字符串表示）的值，其整数部分的位数必须 ≤ <code>integer</code>，小数部分的位数必须 ≤ <code>fraction</code>。</li>\n<li><code>@Pattern(regexp=, flags=)</code>: 检查被注解的 <code>CharSequence</code>（如 <code>String</code>）是否匹配指定的正则表达式 (<code>regexp</code>)。<code>flags</code> 可以指定匹配模式（如不区分大小写）。</li>\n<li><code>@Email</code>: 检查被注解的 <code>CharSequence</code>（如 <code>String</code>）是否符合 Email 格式（内置了一个相对宽松的正则表达式）。</li>\n<li><code>@Past</code> / <code>@Future</code>: 检查被注解的日期或时间类型（<code>java.util.Date</code>、<code>java.util.Calendar</code>、JSR 310 <code>java.time</code> 包下的类型）是否在当前时间之前 / 之后。</li>\n<li><code>@PastOrPresent</code> / <code>@FutureOrPresent</code>: 类似 <code>@Past</code> / <code>@Future</code>，但允许等于当前时间。</li>\n<li>……</li>\n</ul>\n<p>当 Controller 方法使用 <code>@RequestBody</code> 注解来接收请求体并将其绑定到一个对象时，可以在该参数前添加 <code>@Valid</code> 注解来触发对该对象的校验。如果验证失败，它将抛出<code>MethodArgumentNotValidException</code>。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Data</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">AllArgsConstructor</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">NoArgsConstructor</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Person</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">NotNull</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">message</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"classId 不能为空\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> classId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">max</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 33</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">NotNull</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">message</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"name 不能为空\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Pattern</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">regexp</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"((^Man$|^Woman$|^UGM$))\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> message</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"sex 值不在可选范围\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">NotNull</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">message</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"sex 不能为空\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> sex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Email</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">message</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"email 格式不正确\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">NotNull</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">message</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"email 不能为空\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> email</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">RestController</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">RequestMapping</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"/api\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> PersonController</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">PostMapping</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"/person\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ResponseEntity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Person</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getPerson</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">RequestBody</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Valid</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Person</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> person</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ResponseEntity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">ok</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">body</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(person);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>对于直接映射到方法参数的简单类型数据（如路径变量 <code>@PathVariable</code> 或请求参数 <code>@RequestParam</code>），校验方式略有不同：</p>\n<ol>\n<li><strong>在 Controller 类上添加 <code>@Validated</code> 注解</strong>：这个注解是 Spring 提供的（非 JSR 标准），它使得 Spring 能够处理方法级别的参数校验注解。<strong>这是必需步骤。</strong></li>\n<li><strong>将校验注解直接放在方法参数上</strong>：将 <code>@Min</code>, <code>@Max</code>, <code>@Size</code>, <code>@Pattern</code> 等校验注解直接应用于对应的 <code>@PathVariable</code> 或 <code>@RequestParam</code> 参数。</li>\n</ol>\n<p>一定一定不要忘记在类上加上 <code>@Validated</code> 注解了，这个参数可以告诉 Spring 去校验方法参数。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">RestController</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">RequestMapping</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"/api\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Validated</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 关键步骤 1: 必须在类上添加 @Validated</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> PersonController</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">GetMapping</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"/person/{id}\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ResponseEntity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getPersonByID</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">PathVariable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"id\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Max</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 5</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">message</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"ID 不能超过 5\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 关键步骤 2: 校验注解直接放在参数上</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            Integer</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> id</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    )</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 如果传入的 id > 5，Spring 会在进入方法体前抛出 ConstraintViolationException 异常。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 全局异常处理器同样需要处理此异常。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ResponseEntity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">ok</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">body</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(id);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">GetMapping</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"/person\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ResponseEntity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> findPersonByName</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">RequestParam</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"name\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">NotBlank</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">message</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"姓名不能为空\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 同样适用于 @RequestParam</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">max</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">message</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"姓名长度不能超过 10\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            String</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> name</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    )</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ResponseEntity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">ok</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">body</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Found person: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> name);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>Bean Validation 主要解决的是<strong>数据格式、语法层面</strong>的校验。但光有这个还不够。</p>\n<h2>权限校验</h2>\n<p>数据格式都验过了，没问题。但是，<strong>这个操作，当前登录的这个用户，他有权做吗？</strong> 这就是<strong>权限校验</strong>要解决的问题。比如：</p>\n<ul>\n<li>普通用户能修改别人的订单吗？（不行）</li>\n<li>游客能访问管理员后台接口吗？（不行）</li>\n<li>游客能管理其他用户的信息吗？（不行）</li>\n<li>VIP 用户能使用专属的优惠券吗？（可以）</li>\n<li>……</li>\n</ul>\n<p>权限校验发生在<strong>数据校验之后</strong>，它关心的是“<strong>谁 (Who)</strong> 能对 <strong>什么资源 (What)</strong> 执行 <strong>什么操作 (Action)</strong>”。</p>\n<p><strong>为啥权限校验这么重要？</strong></p>\n<ul>\n<li><strong>安全基石：</strong> 防止未经授权的访问和操作，保护用户数据和系统安全。</li>\n<li><strong>业务隔离：</strong> 确保不同角色（管理员、普通用户、VIP 用户等）只能访问和操作其权限范围内的功能。</li>\n<li><strong>合规要求：</strong> 很多行业法规对数据访问权限有严格要求。</li>\n</ul>\n<p>目前 Java 后端主流的方式是使用成熟的安全框架来实现权限校验，而不是自己手写（容易出错且难以维护）。</p>\n<ol>\n<li><strong>Spring Security (业界标准，推荐):</strong> 基于过滤器链（Filter Chain）拦截请求，进行认证（Authentication - 你是谁？）和授权（Authorization - 你能干啥？）。Spring Security 功能强大、社区活跃、与 Spring 生态无缝集成。不过，配置相对复杂，学习曲线较陡峭。</li>\n<li><strong>Apache Shiro:</strong> 另一个流行的安全框架，相对 Spring Security 更轻量级，API 更直观易懂。同样提供认证、授权、会话管理、加密等功能。对于不熟悉 Spring 或觉得 Spring Security 太重的项目，是一个不错的选择。</li>\n<li><strong>Sa-Token:</strong> 国产的轻量级 Java 权限认证框架。支持认证授权、单点登录、踢人下线、自动续签等功能。相比于 Spring Security 和 Shiro 来说，Sa-Token 内置的开箱即用的功能更多，使用也更简单。</li>\n<li><strong>手动检查 (不推荐用于复杂场景):</strong> 在 Service 层或 Controller 层代码里，手动获取当前用户信息（例如从 SecurityContextHolder 或 Session 中），然后 if-else 判断用户角色或权限。权限逻辑与业务逻辑耦合、代码重复、难以维护、容易遗漏。只适用于非常简单的权限场景。</li>\n</ol>\n<p><strong>权限模型简介:</strong></p>\n<ul>\n<li><strong>RBAC (Role-Based Access Control):</strong> 基于角色的访问控制。给用户分配角色，给角色分配权限。用户拥有其所有角色的权限总和。这是最常见的模型。</li>\n<li><strong>ABAC (Attribute-Based Access Control):</strong> 基于属性的访问控制。决策基于用户属性、资源属性、操作属性和环境属性。更灵活但也更复杂。</li>\n</ul>\n<p>一般情况下，绝大部分系统都使用的是 RBAC 权限模型或者其简化版本。用一个图来描述如下：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/design-of-authority-system/rbac.png\" alt=\"RBAC 权限模型示意图\"></p>\n<p>关于权限系统设计的详细介绍，可以看这篇文章：<a href=\"https://javaguide.cn/system-design/security/design-of-authority-system.html\" target=\"_blank\" rel=\"noopener noreferrer\">权限系统设计详解</a>。</p>\n<h2>总结</h2>\n<p>总而言之，要想构建一个安全、稳定、用户体验好的 Web 应用，前后端数据校验和后端权限校验这三道关卡，都得设好，而且各有侧重：</p>\n<ul>\n<li><strong>前端数据校验：</strong> 提升用户体验，减少无效请求，是第一道“友好”的防线。</li>\n<li><strong>后端数据校验：</strong> 保证数据格式正确、符合业务规则，是防止“脏数据”入库的“技术”防线。 Bean Validation 允许我们用注解的方式，直接在 JavaBean（比如我们的 DTO 对象）的属性上声明校验规则，非常方便。</li>\n<li><strong>后端权限校验：</strong> 确保“对的人”做“对的事”，是防止越权操作的“安全”防线。Spring Security、Shiro、Sa-Token 等框架可以帮助我们实现权限校验。</li>\n</ul>\n<h2>参考</h2>\n<ul>\n<li>为什么前后端都需要进行数据校验？: <a href=\"https://juejin.cn/post/7306045519099658240\" target=\"_blank\" rel=\"noopener noreferrer\">https://juejin.cn/post/7306045519099658240</a></li>\n<li>权限系统设计详解：<a href=\"https://javaguide.cn/system-design/security/design-of-authority-system.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://javaguide.cn/system-design/security/design-of-authority-system.html</a></li>\n</ul>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/system-design/security/user-input-validation.png",
      "date_published": "2025-05-04T10:24:21.000Z",
      "date_modified": "2026-03-23T08:33:10.000Z",
      "authors": [],
      "tags": [
        "系统设计"
      ]
    },
    {
      "title": "已经淘汰的 Java 技术，不要再学了！",
      "url": "https://javaguide.cn/about-the-author/deprecated-java-technologies.html",
      "id": "https://javaguide.cn/about-the-author/deprecated-java-technologies.html",
      "summary": "已淘汰的Java技术盘点，JSP、Struts、EJB、Java Applets、SOAP等过时技术不建议学习，附现代替代方案推荐。",
      "content_html": "<p>前几天，我在知乎上随手回答了一个问题：“Java 学到 JSP 就学不下去了，怎么办？”。</p>\n<p>出于不想让别人走弯路的心态，我回答说：已经淘汰的技术就不要学了，并顺带列举了一些在 Java 开发领域中已经被淘汰的技术。</p>\n<h2>已经淘汰的 Java 技术</h2>\n<p>我的回答原内容如下，列举了一些在 Java 开发领域中已经被淘汰的技术：</p>\n<p><strong>JSP</strong></p>\n<ul>\n<li><strong>原因</strong>：JSP 已经过时，无法满足现代 Web 开发需求；前后端分离成为主流。</li>\n<li><strong>替代方案</strong>：模板引擎（如 Thymeleaf、Freemarker）在传统全栈开发中更流行；而在前后端分离架构中，React、Vue、Angular 等现代前端框架已取代 JSP 的角色。</li>\n<li><strong>注意</strong>：一些国企和央企的老项目可能仍然在使用 JSP，但这种情况越来越少见。</li>\n</ul>\n<p><strong>Struts（尤其是 1.x）</strong></p>\n<ul>\n<li><strong>原因</strong>：配置繁琐、开发效率低，且存在严重的安全漏洞（如世界著名的 Apache Struts 2 漏洞）。此外，社区维护不足，生态逐渐萎缩。</li>\n<li><strong>替代方案</strong>：Spring MVC 和 Spring WebFlux 提供了更简洁的开发体验、更强大的功能以及完善的社区支持，完全取代了 Struts。</li>\n</ul>\n<p><strong>EJB (Enterprise JavaBeans)</strong></p>\n<ul>\n<li><strong>原因</strong>：EJB 过于复杂，开发成本高，学习曲线陡峭，在实际项目中逐步被更轻量化的框架取代。</li>\n<li><strong>替代方案</strong>：Spring/Spring Boot 提供了更加简洁且功能强大的企业级开发解决方案，几乎已经成为 Java 企业开发的事实标准。此外，国产的 Solon 和云原生友好的 Quarkus 等框架也非常不错。</li>\n</ul>\n<p><strong>Java Applets</strong></p>\n<ul>\n<li><strong>原因</strong>：现代浏览器（如 Chrome、Firefox、Edge）早已全面移除对 Java Applets 的支持，同时 Applets 存在严重的安全性问题。</li>\n<li><strong>替代方案</strong>：HTML5、WebAssembly 以及现代 JavaScript 框架（如 React、Vue）可以实现更加安全、高效的交互体验，无需插件支持。</li>\n</ul>\n<p><strong>SOAP / JAX-WS</strong></p>\n<ul>\n<li><strong>原因</strong>：SOAP 和 JAX-WS 过于复杂，数据格式冗长（XML），对开发效率和性能不友好。</li>\n<li><strong>替代方案</strong>：RESTful API 和 RPC 更轻量、高效，是现代微服务架构的首选。</li>\n</ul>\n<p><strong>RMI（Remote Method Invocation）</strong></p>\n<ul>\n<li><strong>原因</strong>：RMI 是一种早期的 Java 远程调用技术，但兼容性差、配置繁琐，且性能较差。</li>\n<li><strong>替代方案</strong>：RESTful API 和 PRC 提供了更简单、高效的远程调用解决方案，完全取代了 RMI。</li>\n</ul>\n<p><strong>Swing / JavaFX</strong></p>\n<ul>\n<li><strong>原因</strong>：桌面应用在开发领域的份额大幅减少，Web 和移动端成为主流。Swing 和 JavaFX 的生态不如现代跨平台框架丰富。</li>\n<li><strong>替代方案</strong>：跨平台桌面开发框架（如 Flutter Desktop、Electron）更具现代化体验。</li>\n<li><strong>注意</strong>：一些国企和央企的老项目可能仍然在使用 Swing / JavaFX，但这种情况越来越少见。</li>\n</ul>\n<p><strong>Ant</strong></p>\n<ul>\n<li><strong>原因</strong>：Ant 是一种基于 XML 配置的构建工具，缺乏易用性，配置繁琐。</li>\n<li><strong>替代方案</strong>：Maven 和 Gradle 提供了更高效的项目依赖管理和构建功能，成为现代构建工具的首选。</li>\n</ul>\n<h2>杠精言论</h2>\n<p>没想到，评论区果然出现了一类很常见的杠精：</p>\n<blockquote>\n<p>“学的不是技术，是思想。那爬也是人类不需要的技术吗？为啥你一生下来得先学会爬？如果基础思想都不会就去学各种框架，到最后只能是只会 CV 的废物！”</p>\n</blockquote>\n<img src=\"https://oss.javaguide.cn/github/javaguide/about-the-author/prattle/deprecated-java-technologies-zhihu-comments.png\" style=\"zoom:50%;\">\n<p>这句话表面上看似有道理，但实际上却暴露了一个人的<strong>无知和偏执</strong>。</p>\n<p><strong>知识越贫乏的人，相信的东西就越绝对</strong>，因为他们从未认真了解过与自己观点相对立的角度，也缺乏对技术发展的全局认识。</p>\n<p>举个例子，我刚开始学习 Java 后端开发的时候，完全没什么经验，就随便买了一本书开始看。当时看的是 <strong>《Java Web 整合开发王者归来》</strong> 这本书（梦开始的地方）。</p>\n<p>在我上大学那会儿，这本书的很多内容其实已经过时了，比如它花了大量篇幅介绍 JSP、Struts、Hibernate、EJB 和 SVN 等技术。不过，直到现在，我依然非常感谢这本书，带我走进了 Java 后端开发的大门。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/about-the-author/prattle/java-web-integration-development-king-returns.png\" alt></p>\n<p>这本书一共 <strong>1010</strong> 页，我当时可以说是废寝忘食地学，花了很长时间才把整本书完全“啃”下来。</p>\n<p>回头来看，我如果能有意识地避免学习这些已经淘汰的技术，真的可以节省大量时间去学习更加主流和实用的内容。</p>\n<p>那么，这些被淘汰的技术有用吗？说句实话，<strong>屁用没有，纯粹浪费时间</strong>。</p>\n<p><strong>既然都要花时间学习，为什么不去学那些更主流、更有实际价值的技术呢？</strong></p>\n<p>现在本身就很卷，不管是 Java 方向还是其他技术方向，要学习的技术都很多。</p>\n<p>想要理解所谓的“底层思想”，与其浪费时间在 JSP 这种已经不具备实际应用价值的技术上，不如深入学习一下 Servlet，研究 Spring 的 AOP 和 IoC 原理，从源码角度理解 Spring MVC 的工作机制。</p>\n<p>这些内容，不仅能帮助你掌握核心的思想，还能在实际开发中真正派上用场，这难道不比花大量时间在 JSP 上更有意义吗？</p>\n<h2>还有公司在用的技术就要学吗？</h2>\n<p>我把这篇文章的相关言论发表在我的<a href=\"https://mp.weixin.qq.com/s/lf2dXHcrUSU1pn28Ercj0w\" target=\"_blank\" rel=\"noopener noreferrer\">公众号</a>之后，又收到另外一类在我看来非常傻叉的言论：</p>\n<ul>\n<li>“虽然 JSP 很老了，但还是得学学，会用就行，因为我们很多老项目还在用。”</li>\n<li>“很多央企和国企的老项目还在用，肯定得学学啊！”</li>\n</ul>\n<p>这种观点完全是钻牛角尖！如果按这种逻辑，那你还需要去学 Struts2、SVN、JavaFX 等过时技术，因为它们也还有公司在用。我有一位大学同学毕业后去了武汉的一家国企，写了一年 JavaFX 就受不了跑了。他在之前从来没有接触过 JavaFX，招聘时也没被问过相关问题。</p>\n<p>一定不要假设自己要面对的是过时技术栈的项目。你要找工作肯定要用主流技术栈去找，还要尽量找能让自己技术有成长，干着也舒服点。真要是找不到合适的工作，去维护老项目，那都是后话，现学现卖就行了。</p>\n<p><strong>对于初学者来说别人劝了还非要学习淘汰的技术，多少脑子有点不够用，基本可以告别这一行了！</strong></p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/about-the-author/prattle/java-web-integration-development-king-returns.png",
      "date_published": "2025-03-25T05:34:17.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [],
      "tags": [
        "走近作者"
      ]
    },
    {
      "title": "Java 24 新特性概览",
      "url": "https://javaguide.cn/java/new-features/java24.html",
      "id": "https://javaguide.cn/java/new-features/java24.html",
      "summary": "总结 JDK 24 的新特性与改动，便于跟踪 Java 演进。",
      "content_html": "<p>JDK 24 于 2025 年 3 月发布，这是一个非 LTS（长期支持版）版本。下一个长期支持版是 <strong>JDK 25</strong>，预计于 2025 年 9 月发布。</p>\n<p>JDK 24 共有 24 个新特性，这篇文章会挑选其中较为重要的一些新特性进行详细介绍：</p>\n<ul>\n<li><a href=\"https://openjdk.org/jeps/478\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 478: Key Derivation Function API (密钥派生函数 API)</a></li>\n<li><a href=\"https://openjdk.org/jeps/483\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 483: Early Class-File Loading &amp; Linking (提前类加载和链接)</a></li>\n<li><a href=\"https://openjdk.org/jeps/484\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 484: Class File API (类文件 API)</a></li>\n<li><a href=\"https://openjdk.org/jeps/485\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 485: Stream Gatherers (流收集器)</a></li>\n<li><a href=\"https://openjdk.org/jeps/486\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 486: Disable the Security Manager (永久禁用安全管理器)</a></li>\n<li><a href=\"https://openjdk.org/jeps/487\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 487: Scoped Values (作用域值, 第四次预览)</a></li>\n<li><a href=\"https://openjdk.org/jeps/495\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 495: Simplified Source Files and Instance Main Methods (简化的源文件和实例主方法, 第四次预览)</a></li>\n<li><a href=\"https://openjdk.org/jeps/497\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 497: Quantum-Resistant Digital Signature Algorithm (ML-DSA) (量子抗性数字签名算法)</a></li>\n<li><a href=\"https://openjdk.org/jeps/499\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 499: Structured Concurrency (结构化并发, 第四次预览)</a></li>\n</ul>\n<p>下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png\" alt></p>\n<h2>JEP 478: Key Derivation Function API (密钥派生函数 API)</h2>\n<p>密钥派生函数 API 是一种用于从初始密钥和其他数据派生额外密钥的加密算法。它的核心作用是为不同的加密目的（如加密、认证等）生成多个不同的密钥，避免密钥重复使用带来的安全隐患。这在新代加密中是一个重要的里程碑，为后续新兴的量子计算环境打下了基础。</p>\n<p>通过该 API，开发者可以使用最新的密钥派生算法（如 HKDF 和未来的 Argon2）：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建一个 KDF 对象，使用 HKDF-SHA256 算法</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">KDF</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> hkdf </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> KDF</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getInstance</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"HKDF-SHA256\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建 Extract 和 Expand 参数规范</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">AlgorithmParameterSpec</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> params </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    HKDFParameterSpec</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">ofExtract</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                     .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">addIKM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(initialKeyMaterial)</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 设置初始密钥材料</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                     .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">addSalt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(salt)</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">             // 设置盐值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                     .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">thenExpand</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(info, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">32</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     // 设置扩展信息和目标长度</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 派生一个 32 字节的 AES 密钥</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">SecretKey</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> key </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> hkdf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">deriveKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"AES\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, params);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 可以使用相同的 KDF 对象进行其他密钥派生操作</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JEP 483: Early Class-File Loading &amp; Linking (提前类加载和链接)</h2>\n<p>在传统 JVM 中，应用在每次启动时需要动态加载和链接类。这种机制对启动时间敏感的应用（如微服务或无服务器函数）带来了显著的性能瓶颈。该特性通过缓存已加载和链接的类，显著减少了重复工作的开销，显著减少 Java 应用程序的启动时间。测试表明，对大型应用（如基于 Spring 的服务器应用），启动时间可减少 40% 以上。</p>\n<p>这个优化是零侵入性的，对应用程序、库或框架的代码无需任何更改，启动也方式保持一致，仅需添加相关 JVM 参数（如 <code>-XX:+ClassDataSharing</code>）。</p>\n<h2>JEP 484: Class File API (类文件 API)</h2>\n<p>类文件 API 在 JDK 22 进行了第一次预览（<a href=\"https://openjdk.org/jeps/457\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 457</a>），在 JDK 23 进行了第二次预览并进一步完善（<a href=\"https://openjdk.org/jeps/466\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 466</a>）。最终，该特性在 JDK 24 中顺利转正。</p>\n<p>类文件 API 的目标是提供一套标准化的 API，用于解析、生成和转换 Java 类文件，取代过去对第三方库（如 ASM）在类文件处理上的依赖。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建一个 ClassFile 对象，这是操作类文件的入口。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ClassFile</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> cf </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ClassFile</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">of</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 解析字节数组为 ClassModel</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ClassModel</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> classModel </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> cf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">parse</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(bytes);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 构建新的类文件，移除以 \"debug\" 开头的所有方法</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">byte</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] newBytes </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> cf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">build</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">classModel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">thisClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">asSymbol</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(),</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        classBuilder </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 遍历所有类元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ClassElement</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> ce</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> :</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> classModel) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                // 判断是否为方法 且 方法名以 \"debug\" 开头</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(ce </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> MethodModel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> mm</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">                        &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> mm</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">methodName</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">stringValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">startsWith</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"debug\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">))) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                    // 添加到新的类文件中</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                    classBuilder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">with</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(ce);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        });</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JEP 485: Stream Gatherers (流收集器)</h2>\n<p>流收集器 <code>Stream::gather(Gatherer)</code> 是一个强大的新特性，它允许开发者定义自定义的中间操作，从而实现更复杂、更灵活的数据转换。<code>Gatherer</code> 接口是该特性的核心，它定义了如何从流中收集元素，维护中间状态，并在处理过程中生成结果。</p>\n<p>与现有的 <code>filter</code>、<code>map</code> 或 <code>distinct</code> 等内置操作不同，<code>Stream::gather</code> 使得开发者能够实现那些难以用标准 Stream 操作完成的任务。例如，可以使用 <code>Stream::gather</code> 实现滑动窗口、自定义规则的去重、或者更复杂的状态转换和聚合。 这种灵活性极大地扩展了 Stream API 的应用范围，使开发者能够应对更复杂的数据处理场景。</p>\n<p>基于 <code>Stream::gather(Gatherer)</code> 实现字符串长度的去重逻辑：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> result </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Stream</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">of</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"foo\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"bar\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"baz\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"quux\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                   .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">gather</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Gatherer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">ofSequential</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                       HashSet</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">::new</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 初始化状态为 HashSet,用于保存已经遇到过的字符串长度</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                       (set, str, downstream) </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                           if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">set</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">str</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">())) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                               return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> downstream</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">push</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(str);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                           }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                           return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 继续处理流</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                       }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                   ))</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                   .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 转换为列表</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 输出结果 ==> [foo, quux]</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JEP 486: Disable the Security Manager (永久禁用安全管理器)</h2>\n<p>JDK 24 不再允许启用 <code>Security Manager</code>，即使通过 <code>java -Djava.security.manager</code>命令也无法启用，这是逐步移除该功能的关键一步。虽然 <code>Security Manager</code> 曾经是 Java 中限制代码权限（如访问文件系统或网络、读取或写入敏感文件、执行系统命令）的重要工具，但由于复杂性高、使用率低且维护成本大，Java 社区决定最终移除它。</p>\n<h2>JEP 487: Scoped Values (作用域值, 第四次预览)</h2>\n<p>作用域值（Scoped Values）可以在线程内和线程间共享不可变的数据，优于线程局部变量，尤其是在使用大量虚拟线程时。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ScopedValue</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;...></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> V </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ScopedValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// In some method</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ScopedValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">where</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(V, </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">           .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(() </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> { ... </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() ... </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">call</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> methods ... });</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// In a method called directly or indirectly from the lambda expression</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ...</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>作用域值允许在大型程序中的组件之间安全有效地共享数据，而无需求助于方法参数。</p>\n<h2>JEP 491: Virtual Threads Synchronization Without Pinning (虚拟线程的同步而不固定平台线程)</h2>\n<p>优化了虚拟线程与 <code>synchronized</code> 的工作机制。 虚拟线程在 <code>synchronized</code> 方法和代码块中阻塞时，通常能够释放其占用的操作系统线程（平台线程），避免了对平台线程的长时间占用，从而提升应用程序的并发能力。 这种机制避免了“固定 (Pinning)”——即虚拟线程长时间占用平台线程，阻止其服务于其他虚拟线程的情况。</p>\n<p>现有的使用 <code>synchronized</code> 的 Java 代码无需修改即可受益于虚拟线程的扩展能力。 例如，一个 I/O 密集型的应用程序，如果使用传统的平台线程，可能会因为线程阻塞而导致并发能力下降。 而使用虚拟线程，即使在 <code>synchronized</code> 块中发生阻塞，也不会固定平台线程，从而允许平台线程继续服务于其他虚拟线程，提高整体的并发性能。</p>\n<h2>JEP 493: Linking Run-Time Images Without JMOD Files (在没有 JMOD 文件的情况下链接运行时镜像)</h2>\n<p>默认情况下，JDK 同时包含运行时镜像（运行时所需的模块）和 JMOD 文件。这个特性使得 jlink 工具无需使用 JDK 的 JMOD 文件就可以创建自定义运行时镜像，减少了 JDK 的安装体积（约 25%）。</p>\n<p>说明：</p>\n<ul>\n<li>Jlink 是随 Java 9 一起发布的新命令行工具。它允许开发人员为基于模块的 Java 应用程序创建自己的轻量级、定制的 JRE。</li>\n<li>JMOD 文件是 Java 模块的描述文件，包含了模块的元数据和资源。</li>\n</ul>\n<h2>JEP 495: Simplified Source Files and Instance Main Methods (简化的源文件和实例主方法, 第四次预览)</h2>\n<p>这个特性主要简化了 <code>main</code> 方法的声明。对于 Java 初学者来说，这个 <code>main</code> 方法的声明引入了太多的 Java 语法概念，不利于初学者快速上手。</p>\n<p>没有使用该特性之前定义一个 <code>main</code> 方法：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> HelloWorld</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello, World!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>使用该新特性之后定义一个 <code>main</code> 方法：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> HelloWorld</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello, World!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>进一步简化（未命名的类允许我们省略类名）</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">   System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello, World!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JEP 497: Quantum-Resistant Digital Signature Algorithm (ML-DSA) (量子抗性数字签名算法)</h2>\n<p>JDK 24 引入了支持实施抗量子的基于模块晶格的数字签名算法 （Module-Lattice-Based Digital Signature Algorithm, <strong>ML-DSA</strong>），为抵御未来量子计算机可能带来的威胁做准备。</p>\n<p>ML-DSA 是美国国家标准与技术研究院（NIST）在 FIPS 204 中标准化的量子抗性算法，用于数字签名和身份验证。</p>\n<h2>JEP 498: Warnings When Using <code>sun.misc.Unsafe</code> Memory Access Methods (使用 <code>sun.misc.Unsafe</code> 内存访问方法时发出警告)</h2>\n<p>JDK 23(<a href=\"https://openjdk.org/jeps/471\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 471</a>) 提议弃用 <code>sun.misc.Unsafe</code> 中的内存访问方法，这些方法将来的版本中会被移除。在 JDK 24 中，当首次调用 <code>sun.misc.Unsafe</code> 的任何内存访问方法时，运行时会发出警告。</p>\n<p>这些不安全的方法已有安全高效的替代方案：</p>\n<ul>\n<li><code>java.lang.invoke.VarHandle</code> ：JDK 9 (JEP 193) 中引入，提供了一种安全有效地操作堆内存的方法，包括对象的字段、类的静态字段以及数组元素。</li>\n<li><code>java.lang.foreign.MemorySegment</code> ：JDK 22 (JEP 454) 中引入，提供了一种安全有效地访问堆外内存的方法，有时会与 <code>VarHandle</code> 协同工作。</li>\n</ul>\n<p>这两个类是 Foreign Function &amp; Memory API（外部函数和内存 API） 的核心组件，分别用于管理和操作堆外内存。Foreign Function &amp; Memory API 在 JDK 22 中正式转正，成为标准特性。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> jdk.incubator.foreign.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.lang.invoke.VarHandle</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 管理堆外整数数组的类</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> OffHeapIntBuffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 用于访问整数元素的VarHandle</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> VarHandle</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> ELEM_VH </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">arrayElementVarHandle</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 内存管理器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Arena</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> arena</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 堆外内存段</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> MemorySegment</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 构造函数，分配指定数量的整数空间</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> OffHeapIntBuffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">arena</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">  =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Arena</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">ofShared</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> arena</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">allocate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, size);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 释放内存</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> deallocate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        arena</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">close</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 以volatile方式设置指定索引的值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> setVolatile</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> index</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        ELEM_VH</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setVolatile</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(buffer, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0L</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, index, value);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 初始化指定范围的元素为0</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> initialize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> n</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">asSlice</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">byteSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> start,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                       ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">byteSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> n)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">              .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fill</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">((</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">byte</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 将指定范围的元素复制到新数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#61AFEF\">[] </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">copyToNewArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> n</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">asSlice</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">byteSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> start,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                              ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">byteSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> n)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                     .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JEP 499: Structured Concurrency (结构化并发, 第四次预览)</h2>\n<p>JDK 19 引入了结构化并发，一种多线程编程方法，目的是为了通过结构化并发 API 来简化多线程编程，并不是为了取代<code>java.util.concurrent</code>，目前处于孵化器阶段。</p>\n<p>结构化并发将不同线程中运行的多个任务视为单个工作单元，从而简化错误处理、提高可靠性并增强可观察性。也就是说，结构化并发保留了单线程代码的可读性、可维护性和可观察性。</p>\n<p>结构化并发的基本 API 是<code>StructuredTaskScope</code>，它支持将任务拆分为多个并发子任务，在它们自己的线程中执行，并且子任务必须在主任务继续之前完成。</p>\n<p><code>StructuredTaskScope</code> 的基本用法如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> scope </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> StructuredTaskScope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 使用fork方法派生线程来执行子任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Future</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> future1 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fork</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task1);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Future</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> future2 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fork</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task2);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 等待线程完成</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">join</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 结果的处理可能包括处理或重新抛出异常</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        ...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> process</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> results</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">exceptions </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// close</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>结构化并发非常适合虚拟线程，虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程，从而允许非常多的虚拟线程。</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png",
      "date_published": "2025-03-20T07:56:49.000Z",
      "date_modified": "2026-01-27T07:16:00.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "Async 注解原理分析",
      "url": "https://javaguide.cn/system-design/framework/spring/async.html",
      "id": "https://javaguide.cn/system-design/framework/spring/async.html",
      "summary": "Spring @Async异步注解原理详解，涵盖异步任务配置、线程池设置、@EnableAsync机制及常见使用问题。",
      "content_html": "<p><code>@Async</code> 注解由 Spring 框架提供，被该注解标注的类或方法会在 <strong>异步线程</strong> 中执行。这意味着当方法被调用时，调用者将不会等待该方法执行完成，而是可以继续执行后续的代码。</p>\n<p><code>@Async</code> 注解的使用非常简单，需要两个步骤：</p>\n<ol>\n<li>在启动类上添加注解 <code>@EnableAsync</code> ，开启异步任务。</li>\n<li>在需要异步执行的方法或类上添加注解 <code>@Async</code> 。</li>\n</ol>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">SpringBootApplication</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 开启异步任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">EnableAsync</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> YourApplication</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        SpringApplication</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">YourApplication</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, args);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 异步服务类</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Service</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> MyService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 推荐使用自定义线程池，这里只是演示基本用法</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Async</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CompletableFuture</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> doSomethingAsync</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 这里会有一些业务耗时操作</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 使用 CompletableFuture 可以更方便地处理异步任务的结果，避免阻塞主线程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> CompletableFuture</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">completedFuture</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Async Task Completed\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>接下来，我们一起来看看 <code>@Async</code> 的底层原理。</p>\n<h2>@Async 原理分析</h2>\n<p><code>@Async</code> 可以异步执行任务，本质上是使用 <strong>动态代理</strong> 来实现的。通过 Spring 中的后置处理器 <code>BeanPostProcessor</code> 为使用 <code>@Async</code> 注解的类创建动态代理，之后 <code>@Async</code> 注解方法的调用会被动态代理拦截，在拦截器中将方法的执行封装为异步任务提交给线程池处理。</p>\n<p>接下来，我们来详细分析一下。</p>\n<h3>开启异步</h3>\n<p>使用 <code>@Async</code> 之前，需要在启动类上添加 <code>@EnableAsync</code> 来开启异步，<code>@EnableAsync</code> 注解如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 省略其他注解 ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Import</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">AsyncConfigurationSelector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">interface</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\"> EnableAsync</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> { </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/* ... */</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>在 <code>@EnableAsync</code> 注解上通过 <code>@Import</code> 注解引入了 <code>AsyncConfigurationSelector</code> ，因此 Spring 会去加载通过 <code>@Import</code> 注解引入的类。</p>\n<p><code>AsyncConfigurationSelector</code> 类实现了 <code>ImportSelector</code> 接口，因此在该类中会重写 <code>selectImports()</code> 方法来自定义加载 Bean 的逻辑，如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncConfigurationSelector</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AdviceModeImportSelector</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">EnableAsync</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Nullable</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tpublic</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#61AFEF\">[] </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">selectImports</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">AdviceMode</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> adviceMode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\tswitch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (adviceMode) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   // 基于 JDK 代理织入的通知</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\tcase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> PROXY</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\t\treturn</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] {</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ProxyAsyncConfiguration</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getName</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()};</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   // 基于 AspectJ 织入的通知</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\tcase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ASPECTJ</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\t\treturn</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\tdefault:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\t\treturn</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>在 <code>selectImports()</code> 方法中，会根据通知的不同类型来选择加载不同的类，其中 <code>adviceMode</code> 默认值为 <code>PROXY</code> 。</p>\n<p>这里以基于 JDK 代理的通知为例，此时会加载 <code>ProxyAsyncConfiguration</code> 类，如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Configuration</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Role</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">BeanDefinition</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ROLE_INFRASTRUCTURE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ProxyAsyncConfiguration</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AbstractAsyncConfiguration</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Bean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> TaskManagementConfigUtils</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Role</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">BeanDefinition</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ROLE_INFRASTRUCTURE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tpublic</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncAnnotationBeanPostProcessor</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> asyncAdvisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">\t\t // ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 加载后置处理器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\t\tAsyncAnnotationBeanPostProcessor</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> bpp</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> AsyncAnnotationBeanPostProcessor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\treturn</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> bpp;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>后置处理器</h3>\n<p>在 <code>ProxyAsyncConfiguration</code> 类中，会通过 <code>@Bean</code> 注解加载一个后置处理器 <code>AsyncAnnotationBeanPostProcessor</code> ，这个后置处理器是使 <code>@Async</code> 注解起作用的关键。</p>\n<p>如果某一个类或者方法上使用了 <code>@Async</code> 注解，<code>AsyncAnnotationBeanPostProcessor</code> 处理器就会为该类创建一个动态代理。</p>\n<p>该类的方法在执行时，会被代理对象的拦截器所拦截，其中被 <code>@Async</code> 注解标记的方法会异步执行。</p>\n<p><code>AsyncAnnotationBeanPostProcessor</code> 代码如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncAnnotationBeanPostProcessor</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AbstractBeanFactoryAwareAdvisingPostProcessor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tpublic</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> setBeanFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">BeanFactory</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> beanFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">\t\tsuper</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setBeanFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(beanFactory);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 创建 AsyncAnnotationAdvisor，它是一个 Advisor</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 用于拦截带有 @Async 注解的方法并将这些方法异步执行。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\t\tAsyncAnnotationAdvisor</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> advisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> AsyncAnnotationAdvisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">exceptionHandler</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 如果设置了自定义的 asyncAnnotationType，则将其设置到 advisor 中。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // asyncAnnotationType 用于指定自定义的异步注解，例如 @MyAsync。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">asyncAnnotationType</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> !=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">\t\t\tadvisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setAsyncAnnotationType</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">asyncAnnotationType</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">\t\tadvisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setBeanFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(beanFactory);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">\t\tthis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">advisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> advisor;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>AsyncAnnotationBeanPostProcessor</code> 的父类实现了 <code>BeanFactoryAware</code> 接口，因此在该类中重写了 <code>setBeanFactory()</code> 方法作为扩展点，来加载 <code>AsyncAnnotationAdvisor</code> 。</p>\n<h4>创建 Advisor</h4>\n<p><code>Advisor</code> 是 <code>Spring AOP</code> 对 <code>Advice</code> 和 <code>Pointcut</code> 的抽象。<code>Advice</code> 为执行的通知逻辑，<code>Pointcut</code> 为通知执行的切入点。</p>\n<p>在后置处理器 <code>AsyncAnnotationBeanPostProcessor</code> 中会去创建 <code>AsyncAnnotationAdvisor</code> ， 在它的构造方法中，会构建对应的 <code>Advice</code> 和 <code>Pointcut</code> ，如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncAnnotationAdvisor</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AbstractPointcutAdvisor</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> BeanFactoryAware</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Advice</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> advice</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 异步执行的 Advice</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Pointcut</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> pointcut</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 匹配 @Async 注解方法的切点</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 构造函数</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> AsyncAnnotationAdvisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/* 参数省略 */</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 1. 创建 Advice，负责异步执行逻辑</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">advice</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> buildAdvice</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(executor, exceptionHandler);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 2. 创建 Pointcut，选择要被增强的目标方法</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">pointcut</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> buildPointcut</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(asyncAnnotationTypes);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 创建 Advice</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    protected</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Advice</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> buildAdvice</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/* 参数省略 */</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 创建处理异步执行的拦截器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        AnnotationAsyncExecutionInterceptor</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> interceptor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> AnnotationAsyncExecutionInterceptor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 使用执行器和异常处理器配置拦截器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        interceptor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">configure</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(executor, exceptionHandler);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> interceptor;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 创建 Pointcut</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    protected</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Pointcut</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> buildPointcut</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Set</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Annotation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>> </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">asyncAnnotationTypes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        ComposablePointcut</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> result</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Class</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Annotation</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">asyncAnnotationType</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> :</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> asyncAnnotationTypes) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 1. 类级别切点：如果类上有注解则匹配</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            Pointcut</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> cpc</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> AnnotationMatchingPointcut</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(asyncAnnotationType, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 2. 方法级别切点：如果方法上有注解则匹配</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            Pointcut</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> mpc</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> AnnotationMatchingPointcut</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, asyncAnnotationType, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (result </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                result </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ComposablePointcut</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(cpc);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                // 使用 union 合并之前的切点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                result</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">union</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(cpc);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 将方法级别切点添加到组合切点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            result </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> result</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">union</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(mpc);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 返回组合切点，如果没有提供注解类型则返回 Pointcut.TRUE</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (result </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> ?</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> result </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Pointcut</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">TRUE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>AsyncAnnotationAdvisor</code> 的核心在于构建 <code>Advice</code> 和 <code>Pointcut</code> ：</p>\n<ul>\n<li>构建 <code>Advice</code> ：会创建 <code>AnnotationAsyncExecutionInterceptor</code> 拦截器，在拦截器的 <code>invoke()</code> 方法中会执行通知的逻辑。</li>\n<li>构建 <code>Pointcut</code> ：由 <code>ClassFilter</code> 和 <code>MethodMatcher</code> 组成，用于匹配哪些方法需要执行通知（ <code>Advice</code> ）的逻辑。</li>\n</ul>\n<h4>后置处理逻辑</h4>\n<p><code>AsyncAnnotationBeanPostProcessor</code> 后置处理器中实现的 <code>postProcessAfterInitialization()</code> 方法在其父类 <code>AbstractAdvisingBeanPostProcessor</code> 中，在 <code>Bean</code> 初始化之后，会进入到 <code>postProcessAfterInitialization()</code> 方法进行后置处理。</p>\n<p>在后置处理方法中，会判断 <code>Bean</code> 是否符合后置处理器中 <code>Advisor</code> 通知的条件，如果符合，则创建代理对象。如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// AbstractAdvisingBeanPostProcessor</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> postProcessAfterInitialization</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> bean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> beanName) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">advisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ||</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> bean </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> AopInfrastructureBean) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\treturn</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> bean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (bean </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> Advised) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\t\tAdvised</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> advised </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (Advised) bean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">advised</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isFrozen</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> isEligible</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">AopUtils</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getTargetClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(bean)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">beforeExistingAdvisors</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">\t\t\t\tadvised</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">addAdvisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">advisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\telse</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">\t\t\t\tadvised</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">addAdvisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">advisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\treturn</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> bean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 判断给定的 Bean 是否符合后置处理器中 Advisor 通知的条件，符合的话，就创建代理对象。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isEligible</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(bean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> beanName)) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\t\tProxyFactory</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> proxyFactory </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> prepareProxyFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(bean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> beanName)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">proxyFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isProxyTargetClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">\t\t\tevaluateProxyInterfaces</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">bean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(),</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> proxyFactory)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 添加 Advisor。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">\t\tproxyFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">addAdvisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">advisor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">\t\tcustomizeProxyFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(proxyFactory)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 返回代理对象。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\treturn</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> proxyFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getProxy</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getProxyClassLoader</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\treturn</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> bean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>@Async 注解方法的拦截</h3>\n<p><code>@Async</code> 注解方法的执行会在 <code>AnnotationAsyncExecutionInterceptor</code> 中被拦截，在 <code>invoke()</code> 方法中执行拦截器的逻辑。此时会将 <code>@Async</code> 注解标注的方法封装为异步任务，交给执行器来执行。</p>\n<p><code>invoke()</code> 方法在 <code>AnnotationAsyncExecutionInterceptor</code> 的父类 <code>AsyncExecutionInterceptor</code> 中定义，如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncExecutionInterceptor</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncExecutionAspectSupport</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> MethodInterceptor</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Ordered</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Nullable</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tpublic</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> invoke</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> MethodInvocation</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> invocation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> throws</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Throwable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\t\tClass</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">targetClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">invocation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getThis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> ?</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> AopUtils</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getTargetClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">invocation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getThis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\t\tMethod</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> specificMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ClassUtils</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getMostSpecificMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">invocation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(), targetClass);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\tfinal</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Method</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> userDeclaredMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> BridgeMethodResolver</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">findBridgedMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(specificMethod);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 1、确定异步任务执行器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\t\tAsyncTaskExecutor</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> determineAsyncExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(userDeclaredMethod);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 2、将要执行的方法封装为 Callable 异步任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\t\tCallable</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">task</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> () </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\ttry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 2.1、执行方法</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\t\t\t\tObject</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> result</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> invocation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">proceed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 2.2、如果方法返回值是 Future 类型，阻塞等待结果</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\t\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (result </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> Future) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\t\t\treturn</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ((</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Future</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) result).</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t\t\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\tcatch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ExecutionException</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">\t\t\t\thandleError</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getCause</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(), userDeclaredMethod, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">invocation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getArguments</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\tcatch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Throwable</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">\t\t\t\thandleError</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(ex, userDeclaredMethod, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">invocation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getArguments</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\treturn</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t\t};</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">\t\t// 3、提交任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\treturn</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> doSubmit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task, executor, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">invocation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getReturnType</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>在 <code>invoke()</code> 方法中，主要有 3 个步骤：</p>\n<ol>\n<li>确定执行异步任务的执行器。</li>\n<li>将 <code>@Async</code> 注解标注的方法封装为 <code>Callable</code> 异步任务。</li>\n<li>将任务提交给执行器执行。</li>\n</ol>\n<h4>1、获取异步任务执行器</h4>\n<p>在 <code>determineAsyncExecutor()</code> 方法中，会获取异步任务的执行器（即执行异步任务的 <strong>线程池</strong> ）。代码如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 确定异步任务的执行器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">protected</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncTaskExecutor</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> determineAsyncExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Method</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> method) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 1、先从缓存中获取。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\tAsyncTaskExecutor</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> executor </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">executors</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(method);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (executor </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\t\tExecutor</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> targetExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 2、获取执行器的限定符。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\t\tString</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> qualifier </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getExecutorQualifier</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(method)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">StringUtils</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">hasLength</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(qualifier)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   // 3、根据限定符获取对应的执行器。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t\ttargetExecutor </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> findQualifiedExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">beanFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> qualifier)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\telse</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   // 4、如果没有限定符，则使用默认的执行器。即 Spring 提供的默认线程池：SimpleAsyncTaskExecutor。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t\ttargetExecutor </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">defaultExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (targetExecutor </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\treturn</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 5、将执行器包装为 TaskExecutorAdapter 适配器。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // TaskExecutorAdapter 是 Spring 对于 JDK 线程池做的一层抽象，还是继承自 JDK 的线程池 Executor。这里可以不用管太多，只要知道它是线程池就可以了。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\texecutor </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (targetExecutor </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> AsyncListenableTaskExecutor </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">?</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t\t\t(AsyncListenableTaskExecutor) targetExecutor </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> TaskExecutorAdapter</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(targetExecutor))</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">\t\tthis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">executors</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(method, executor);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\treturn</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>在 <code>determineAsyncExecutor()</code> 方法中确定了异步任务的执行器（线程池），主要是通过 <code>@Async</code> 注解的 <code>value</code> 值来获取执行器的限定符，根据限定符再去 <code>BeanFactory</code> 中查找对应的执行器就可以了。</p>\n<p>如果在 <code>@Async</code> 注解中没有指定线程池，则会通过 <code>this.defaultExecutor.get()</code> 来获取默认的线程池，其中 <code>defaultExecutor</code> 在下边方法中进行赋值：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// AsyncExecutionInterceptor</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">protected</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Executor</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getDefaultExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Nullable</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> BeanFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> beanFactory) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 1、尝试从 beanFactory 中获取线程池。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\tExecutor</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> defaultExecutor </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> super</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getDefaultExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(beanFactory);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 2、如果 beanFactory 中没有，则创建 SimpleAsyncTaskExecutor 线程池。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\treturn</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (defaultExecutor </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> ?</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> defaultExecutor </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> SimpleAsyncTaskExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">())</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>其中 <code>super.getDefaultExecutor()</code> 会在 <code>beanFactory</code> 中尝试获取 <code>Executor</code> 类型的线程池。代码如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">protected</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Executor</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getDefaultExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Nullable</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> BeanFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> beanFactory) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (beanFactory </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\ttry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   // 1、从 beanFactory 中获取 TaskExecutor 类型的线程池。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\treturn</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> beanFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getBean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">TaskExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\tcatch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">NoUniqueBeanDefinitionException</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\ttry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">\t\t\t\t// 2、如果有多个，则尝试从 beanFactory 中获取执行名称的 Executor 线程池。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\t\treturn</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> beanFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getBean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(DEFAULT_TASK_EXECUTOR_BEAN_NAME, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\tcatch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">NoSuchBeanDefinitionException</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ex2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\t\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">logger</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isInfoEnabled</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">\t\t\t\t\t// ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\tcatch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">NoSuchBeanDefinitionException</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\ttry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 3、如果没有，则尝试从 beanFactory 中获取执行名称的 Executor 线程池。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\t\treturn</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> beanFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getBean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(DEFAULT_TASK_EXECUTOR_BEAN_NAME, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\t\t\tcatch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">NoSuchBeanDefinitionException</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ex2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">\t\t\t\t// ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\treturn</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>在 <code>getDefaultExecutor()</code> 中，如果从 <code>beanFactory</code> 获取线程池失败的话，则会创建 <code>SimpleAsyncTaskExecutor</code> 线程池。</p>\n<p>该线程池的在每次执行异步任务时，都会创建一个新的线程去执行任务，并不会对线程进行复用，从而导致异步任务执行的开销很大。一旦在 <code>@Async</code> 注解标注的方法某一瞬间并发量剧增，应用就会大量创建线程，从而影响服务质量甚至出现服务不可用。</p>\n<p>同一时刻如果向 <code>SimpleAsyncTaskExecutor</code> 线程池提交 10000 个任务，那么该线程池就会创建 10000 个线程，其的 <code>execute()</code> 方法如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// SimpleAsyncTaskExecutor：execute() 内部会调用 doExecute()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">protected</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> doExecute</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Runnable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> task) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 创建新线程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> thread </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">threadFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> !=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> ?</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">threadFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task)</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> :</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> createThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(task))</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>建议：在使用 <code>@Async</code> 时需要自己指定线程池，避免 Spring 默认线程池带来的风险。</strong></p>\n<p>在 <code>@Async</code> 注解中的 <code>value</code> 指定了线程池的限定符，根据限定符可以获取 <strong>自定义的线程池</strong> 。获取限定符的代码如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// AnnotationAsyncExecutionInterceptor</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">protected</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getExecutorQualifier</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Method</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> method) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">\t// 1.从方法上获取 Async 注解。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">\tAsync</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> async </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> AnnotatedElementUtils</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">findMergedAnnotation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(method, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Async</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 2. 如果方法上没有找到 @Async 注解，则尝试从方法所在的类上获取 @Async 注解。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tif</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (async </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t\tasync </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> AnnotatedElementUtils</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">findMergedAnnotation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">method</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getDeclaringClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(), </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Async</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">\t}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 3. 如果找到了 @Async 注解，则获取注解的 value 值并返回，作为线程池的限定符。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> //    如果 \"value\" 属性值为空字符串，则使用默认的线程池。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> //    如果没有找到 @Async 注解，则返回 null，同样使用默认的线程池。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\treturn</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (async </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> ?</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> async</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> :</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h4>2、将方法封装为异步任务</h4>\n<p>在 <code>invoke()</code> 方法获取执行器之后，会将方法封装为异步任务，代码如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 将要执行的方法封装为 Callable 异步任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Callable</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> task </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> () </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 2.1、执行被拦截的方法 (proceed() 方法是 AOP 中的核心方法，用于执行目标方法)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> result </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> invocation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">proceed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 2.2、如果被拦截方法的返回值是 Future 类型，则需要阻塞等待结果，</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //     并将 Future 的结果作为异步任务的结果返回。 这是为了处理异步方法嵌套调用的情况。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //     例如，一个异步方法内部调用了另一个异步方法，则需要等待内部异步方法执行完成，</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //     才能返回最终的结果。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (result </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> Future) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> ((</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Future</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) result)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 阻塞等待 Future 的结果</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ExecutionException</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 2.3、处理 ExecutionException 异常。 ExecutionException 是 Future.get() 方法抛出的异常，</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        handleError</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getCause</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(),</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> userDeclaredMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> invocation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getArguments</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 处理原始异常</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Throwable</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 2.4、处理其他类型的异常。 将异常、被拦截的方法和方法参数作为参数调用 handleError() 方法进行处理。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        handleError</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> userDeclaredMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> invocation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getArguments</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 2.5、如果方法返回值不是 Future 类型，或者发生异常，则返回 null。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>相比于 <code>Runnable</code> ，<code>Callable</code> 可以返回结果，并且抛出异常。</p>\n<p>将 <code>invocation.proceed()</code> 的执行（原方法的执行）封装为 <code>Callable</code> 异步任务。这里仅仅当 <code>result</code> （方法返回值）类型为 <code>Future</code> 才返回，如果是其他类型则直接返回 <code>null</code> 。</p>\n<p>因此使用 <code>@Async</code> 注解标注的方法如果使用 <code>Future</code> 类型之外的返回值，则无法获取方法的执行结果。</p>\n<h4>3、提交异步任务</h4>\n<p>在 <code>AsyncExecutionInterceptor # invoke()</code> 中将要执行的方法封装为 Callable 任务之后，就会将任务交给执行器来执行。提交相关的代码如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">protected</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> doSubmit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Callable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> task</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncTaskExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> returnType) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 根据方法的返回值类型，选择不同的异步执行方式并返回结果。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 1. 如果方法返回值是 CompletableFuture 类型</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">CompletableFuture</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isAssignableFrom</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(returnType)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 使用 CompletableFuture.supplyAsync() 方法异步执行任务。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> CompletableFuture</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">supplyAsync</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(() </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> task</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">call</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Throwable</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> CompletionException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(ex); </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 将异常包装为 CompletionException，以便在 future.get() 时抛出</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }, executor);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 2. 如果方法返回值是 ListenableFuture 类型</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ListenableFuture</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isAssignableFrom</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(returnType)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 将 AsyncTaskExecutor 强制转换为 AsyncListenableTaskExecutor，</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 并调用 submitListenable() 方法提交任务。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // AsyncListenableTaskExecutor 是 ListenableFuture 的专用异步执行器，</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 它可以返回一个 ListenableFuture 对象，允许添加回调函数来监听任务的完成。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> ((AsyncListenableTaskExecutor) executor)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">submitListenable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 3. 如果方法返回值是 Future 类型</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Future</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isAssignableFrom</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(returnType)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 直接调用 AsyncTaskExecutor 的 submit() 方法提交任务，并返回一个 Future 对象。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">submit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 4. 如果方法返回值是 void 或其他类型</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 直接调用 AsyncTaskExecutor 的 submit() 方法提交任务。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 由于方法返回值是 void，因此不需要返回任何结果，直接返回 null。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">submit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>在 <code>doSubmit()</code> 方法中，会根据 <code>@Async</code> 注解标注方法的返回值不同，来选择不同的任务提交方式，最后任务会由执行器（线程池）执行。</p>\n<h3>总结</h3>\n<p></p>\n<p>理解 <code>@Async</code> 原理的核心在于理解 <code>@EnableAsync</code> 注解，该注解开启了异步任务的功能。</p>\n<p>主要流程如上图，会通过后置处理器来创建代理对象，之后代理对象中 <code>@Async</code> 方法的执行会走到 <code>Advice</code> 内部的拦截器中，之后将方法封装为异步任务，并提交线程池进行处理。</p>\n<h2>@Async 使用建议</h2>\n<h3>自定义线程池</h3>\n<p>如果没有显式地配置线程池，在 <code>@Async</code> 底层会先在 <code>BeanFactory</code> 中尝试获取线程池，如果获取不到，则会创建一个 <code>SimpleAsyncTaskExecutor</code> 实现。<code>SimpleAsyncTaskExecutor</code> 本质上不算是一个真正的线程池，因为它对于每个请求都会启动一个新线程而不重用现有线程，这会带来一些潜在的问题，例如资源消耗过大。</p>\n<p>具体线程池获取可以参考这篇文章：<a href=\"https://mp.weixin.qq.com/s/FySv5L0bCdrlb5MoSfQtAA\" target=\"_blank\" rel=\"noopener noreferrer\">浅析 Spring 中 Async 注解底层异步线程池原理｜得物技术</a>。</p>\n<p>一定要显式配置一个线程池，推荐<code>ThreadPoolTaskExecutor</code>。并且，还可以根据任务的性质和需求，为不同的异步方法指定不同的线程池。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Configuration</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">EnableAsync</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncConfig</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Bean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"executor1\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Executor</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> executor1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        ThreadPoolTaskExecutor</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ThreadPoolTaskExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setCorePoolSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setMaxPoolSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">5</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setQueueCapacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">50</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setThreadNamePrefix</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"AsyncExecutor1-\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">initialize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> executor;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Bean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"executor2\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Executor</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> executor2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        ThreadPoolTaskExecutor</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ThreadPoolTaskExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setCorePoolSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setMaxPoolSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">4</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setQueueCapacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">100</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setThreadNamePrefix</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"AsyncExecutor2-\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">initialize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> executor;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>@Async</code> 注解中指定线程池的 Bean 名称：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Service</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Async</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"executor1\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> performTask1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 任务1的逻辑</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Executing Task1 with Executor1\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Async</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"executor2\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> performTask2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 任务2的逻辑</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Executing Task2 with Executor2\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>避免 @Async 注解失效</h3>\n<p><code>@Async</code> 注解会在以下几个场景失效，需要注意：</p>\n<p><strong>1、同一类中调用异步方法</strong></p>\n<p>如果你在同一个类内部调用一个<code>@Async</code>注解的方法，那这个方法将不会异步执行。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Service</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> MyService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> myMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 直接通过 this 引用调用，绕过了 Spring 的代理机制，异步执行失效</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        asyncMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Async</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> asyncMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 异步执行的逻辑</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这是因为 Spring 的异步机制是通过 <strong>代理</strong> 实现的，而在同一个类内部的方法调用会绕过 Spring 的代理机制，也就是绕过了代理对象，直接通过 this 引用调用的。由于没有经过代理，所有的代理相关的处理（即将任务提交线程池异步执行）都不会发生。</p>\n<p>为了避免这个问题，比较推荐的做法是将异步方法移至另一个 Spring Bean 中。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Service</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Async</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> asyncMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 异步执行的逻辑</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Service</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> MyService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Autowired</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncService</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> asyncService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> myMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        asyncService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">asyncMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>2、使用 static 关键字修饰异步方法</strong></p>\n<p>如果<code>@Async</code>注解的方法被 <code>static</code> 关键字修饰，那这个方法将不会异步执行。</p>\n<p>这是因为 Spring 的异步机制是通过代理实现的，由于静态方法不属于实例而是属于类且不参与继承，Spring 的代理机制（无论是基于 JDK 还是 CGLIB）无法拦截静态方法来提供如异步执行这样的增强功能。</p>\n<p>篇幅问题，这里没有进一步详细介绍，不了解的代理机制的朋友，可以看看我写的 <a href=\"https://javaguide.cn/java/basis/proxy.html\" target=\"_blank\" rel=\"noopener noreferrer\">Java 代理模式详解</a>这篇文章。</p>\n<p>如果你需要异步执行一个静态方法的逻辑，可以考虑设计一个非静态的包装方法，这个包装方法使用 <code>@Async</code> 注解，并在其内部调用静态方法</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Service</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Async</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> asyncWrapper</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 调用静态方法</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        SClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">staticMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> SClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> staticMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 执行一些操作</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>3、忘记开启异步支持</strong></p>\n<p>Spring Boot 默认情况下不启用异步支持，确保在主配置类 <code>Application</code> 上添加<code>@EnableAsync</code>注解以启用异步功能。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">SpringBootApplication</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">EnableAsync</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Application</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        SpringApplication</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Application</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, args);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>4、<code>@Async</code> 注解的方法所在的类必须是 Spring Bean</strong></p>\n<p><code>@Async</code> 注解的方法必须位于 Spring 管理的 Bean 中，只有这样，Spring 才能在创建 Bean 时应用代理，代理能够拦截方法调用并实现异步执行的逻辑。如果该方法不在 Spring 管理的 bean 中，Spring 就无法创建必要的代理，<code>@Async</code> 注解就不会产生任何效果。</p>\n<h3>返回值类型</h3>\n<p>建议将 <code>@Async</code> 注解方法的返回值类型定义为 <code>void</code> 和 <code>Future</code> 。</p>\n<ul>\n<li>如果不需要获取异步方法返回的结果，将返回值类型定义为 <code>void</code> 。</li>\n<li>如果需要获取异步方法返回的结果，将返回值类型定义为 <code>Future</code>（例如<code>CompletableFuture</code> 、 <code>ListenableFuture</code> ）。</li>\n</ul>\n<p>如果将 <code>@Async</code> 注解方法的返回值定义为其他类型（如 <code>Object</code> 、 <code>String</code> 等等），则无法获取方法返回值。</p>\n<p>这种设计符合异步编程的基本原则，即调用者不应立即期待一个结果，而是应该能够在未来某个时间点获取结果。如果返回类型是 <code>Future</code>，调用者可以使用这个返回的 <code>Future</code> 对象来查询任务的状态，取消任务，或者在任务完成时获取结果。</p>\n<h3>处理异步方法中的异常</h3>\n<p>异步方法中抛出的异常默认不会被调用者捕获。为了管理这些异常，建议使用<code>CompletableFuture</code>的异常处理功能，或者配置一个全局的<code>AsyncUncaughtExceptionHandler</code>来处理没有正确捕获的异常。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Configuration</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">EnableAsync</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncConfig</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncConfigurer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">{</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncUncaughtExceptionHandler</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getAsyncUncaughtExceptionHandler</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> CustomAsyncExceptionHandler</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 自定义异常处理器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CustomAsyncExceptionHandler</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncUncaughtExceptionHandler</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> handleUncaughtException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Throwable</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Method</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> method</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">... </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">params</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 日志记录或其他处理逻辑</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>未考虑事务管理</h3>\n<p><code>@Async</code>注解的方法需要事务支持时，务必在该异步方法上独立使用。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Service</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AsyncTransactionalService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Async</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // Propagation.REQUIRES_NEW 表示 Spring 在执行异步方法时开启一个新的、与当前事务无关的事务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Transactional</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">propagation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Propagation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">REQUIRES_NEW</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> asyncTransactionalMethod</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 这里的操作会在新的事务中执行</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 执行一些数据库操作</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>未指定异步方法执行顺序</h3>\n<p><code>@Async</code>注解的方法执行是非阻塞的，它们可能以任意顺序完成。如果需要按照特定的顺序处理结果，你可以将方法的返回值设定为 <code>Future</code> 或 <code>CompletableFuture</code> ，通过返回值对象来实现一个方法在另一个方法完成后再执行。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Async</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CompletableFuture</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> fetchDataAsync</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> CompletableFuture</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">completedFuture</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Data\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Async</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CompletableFuture</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> processDataAsync</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> data) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> CompletableFuture</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">supplyAsync</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(() </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Processed \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> data);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>processDataAsync</code> 方法在 <code>fetchDataAsync</code>后执行：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">CompletableFuture</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> dataFuture </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> asyncService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fetchDataAsync</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">dataFuture</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">thenCompose</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(data </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> asyncService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">processDataAsync</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(data))</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">thenAccept</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(result </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(result));</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2></h2>\n",
      "date_published": "2024-12-26T14:09:00.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [],
      "tags": [
        "框架"
      ]
    },
    {
      "title": "Java 22 & 23 新特性概览",
      "url": "https://javaguide.cn/java/new-features/java22-23.html",
      "id": "https://javaguide.cn/java/new-features/java22-23.html",
      "summary": "概览 JDK 22/23 的关键 JEP 与语言/平台增强，持续追踪性能与并发相关改动。",
      "content_html": "<p>JDK 23 和 JDK 22 一样，这也是一个非 LTS（长期支持）版本，Oracle 仅提供六个月的支持。下一个长期支持版是 JDK 25，预计于 2025 年 9 月发布。</p>\n<p>下图是从 JDK8 到 JDK 24 每个版本的更新带来的新特性数量和更新时间：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png\" alt></p>\n<p>由于 JDK 22 和 JDK 23 重合的新特性较多，这里主要以 JDK 23 为主介绍，会补充 JDK 22 独有的一些特性。</p>\n<p>JDK 23 一共有 12 个新特性：</p>\n<ul>\n<li><a href=\"https://openjdk.org/jeps/455\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 455: Primitive Types in Patterns, instanceof, and switch (Preview)（模式中的原始类型、instanceof 和 switch，预览）</a></li>\n<li><a href=\"https://openjdk.org/jeps/466\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 466: Class File API (Second Preview)（类文件 API，第二次预览）</a></li>\n<li><a href=\"https://openjdk.org/jeps/467\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 467: Markdown Documentation Comments（Markdown 文档注释）</a></li>\n<li><a href=\"https://openjdk.org/jeps/469\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 469: Vector API (Eighth Incubator)（向量 API，第八次孵化）</a></li>\n<li><a href=\"https://openjdk.org/jeps/473\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 473: Stream Gatherers (Second Preview)（流收集器，第二次预览）</a></li>\n<li><a href=\"https://openjdk.org/jeps/471\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 471: Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal（弃用 sun.misc.Unsafe 中的内存访问方法以便移除）</a></li>\n<li><a href=\"https://openjdk.org/jeps/474\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 474: ZGC: Generational Mode by Default（ZGC：默认的分代模式）</a></li>\n<li><a href=\"https://openjdk.org/jeps/476\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 476: Module Import Declarations (Preview)（模块导入声明，预览）</a></li>\n<li><a href=\"https://openjdk.org/jeps/477\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 477: Unnamed Classes and Instance Main Methods (Third Preview)（未命名类和实例 main 方法，第三次预览）</a></li>\n<li><a href=\"https://openjdk.org/jeps/480\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 480: Structured Concurrency (Third Preview)（结构化并发，第三次预览）</a></li>\n<li><a href=\"https://openjdk.org/jeps/481\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 481: Scoped Values (Third Preview)（作用域值，第三次预览）</a></li>\n<li><a href=\"https://openjdk.org/jeps/482\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 482: Flexible Constructor Bodies (Second Preview)（灵活的构造函数体，第二次预览）</a></li>\n</ul>\n<p>JDK 22 共有 12 个新特性，如下所示：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/jdk22-new-features.png\" alt></p>\n<p>其中，下面这 4 条新特性我会单独拎出来详细介绍一下：</p>\n<ul>\n<li><a href=\"https://openjdk.org/jeps/423\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 423：G1 垃圾收集器区域固定</a></li>\n<li><a href=\"https://openjdk.org/jeps/454\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 454：外部函数与内存 API</a></li>\n<li><a href=\"https://openjdk.org/jeps/456\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 456：未命名模式和变量</a></li>\n<li><a href=\"https://openjdk.org/jeps/458\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 458：启动多文件源代码程序</a></li>\n</ul>\n<h2>JDK 23</h2>\n<h3>JEP 455: 模式中的原始类型、instanceof 和 switch（预览）</h3>\n<p>在 JEP 455 之前， <code>instanceof</code> 只支持引用类型，<code>switch</code> 表达式和语句的 <code>case</code> 标签只能使用整数字面量、枚举常量和字符串字面量。</p>\n<p>JEP 455 的预览特性中，<code>instanceof</code> 和 <code>switch</code> 全面支持所有原始类型，包括 <code>byte</code>, <code>short</code>, <code>char</code>, <code>int</code>, <code>long</code>, <code>float</code>, <code>double</code>, <code>boolean</code>。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 传统写法</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">128</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 127</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    byte</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> b </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">byte</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    ...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 使用 instanceof 改进</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (i </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> byte</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    ...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> v </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ...;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 传统写法</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (v </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1L</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">} </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (v </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 2L</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">} </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (v </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 10_000_000_000L</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 使用 long 类型的 case 标签</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">switch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (v) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    case</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1L</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        break</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    case</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 2L</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        break</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    case</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 10_000_000_000L</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        break</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    default:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>JEP 466: Class File API（第二次预览）</h3>\n<p>类文件 API 在 JDK 22 进行了第一次预览，由 <a href=\"https://openjdk.org/jeps/457\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 457</a> 提出。</p>\n<p>类文件 API 的目标是提供一套标准化的 API，用于解析、生成和转换 Java 类文件，取代过去对第三方库（如 ASM）在类文件处理上的依赖。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建一个 ClassFile 对象，这是操作类文件的入口。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ClassFile</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> cf </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ClassFile</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">of</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 解析字节数组为 ClassModel</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ClassModel</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> classModel </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> cf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">parse</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(bytes);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 构建新的类文件，移除以 \"debug\" 开头的所有方法</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">byte</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] newBytes </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> cf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">build</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">classModel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">thisClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">asSymbol</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(),</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        classBuilder </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 遍历所有类元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ClassElement</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> ce</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> :</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> classModel) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                // 判断是否为方法 且 方法名以 \"debug\" 开头</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(ce </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> MethodModel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> mm</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">                        &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> mm</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">methodName</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">stringValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">startsWith</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"debug\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">))) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                    // 添加到新的类文件中</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                    classBuilder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">with</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(ce);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        });</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>JEP 467：Markdown 文档注释</h3>\n<p>在 JavaDoc 文档注释中可以使用 Markdown 语法，取代原本只能使用 HTML 和 JavaDoc 标签的方式。</p>\n<p>Markdown 更简洁易读，减少了手动编写 HTML 的繁琐，同时保留了对 HTML 元素和 JavaDoc 标签的支持。这个增强旨在让 API 文档注释的编写和阅读变得更加轻松，同时不会影响现有注释的解释。Markdown 提供了对常见文档元素（如段落、列表、链接等）的简化表达方式，提升了文档注释的可维护性和开发者体验。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/jep467-markdown-documentation-comments.png\" alt=\"Markdown 文档注释\"></p>\n<h3>JEP 469：向量 API（第八次孵化）</h3>\n<p>向量计算由对向量的一系列操作组成。向量 API 用来表达向量计算，该计算可以在运行时可靠地编译为支持的 CPU 架构上的最佳向量指令，从而实现优于等效标量计算的性能。</p>\n<p>向量 API 的目标是为用户提供简洁易用且与平台无关的表达范围广泛的向量计算。</p>\n<p>这是对数组元素的简单标量计算：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> scalarComputation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">   for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        c[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i]) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1.0f</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">   }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这是使用 Vector API 进行的等效向量计算：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> VectorSpecies</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Float</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> SPECIES </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> FloatVector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">SPECIES_PREFERRED</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> vectorComputation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> upperBound </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> SPECIES</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">loopBound</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> upperBound</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> SPECIES</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // FloatVector va, vb, vc;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> va </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> FloatVector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fromArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(SPECIES, a, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> vb </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> FloatVector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fromArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(SPECIES, b, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> vc </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> va</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">mul</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(va)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                   .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">vb</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">mul</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(vb))</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                   .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">neg</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        vc</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">intoArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(c, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        c[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i]) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1.0f</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>JEP 473：流收集器（第二次预览）</h3>\n<p>流收集器在 JDK 22 进行了第一次预览，由 <a href=\"https://openjdk.org/jeps/461\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 461</a> 提出。</p>\n<p>这个改进使得 Stream API 可以支持自定义中间操作。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">source</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">gather</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(a).</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">gather</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(b).</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">gather</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(c).</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">collect</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(...)</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><h3>JEP 471：弃用 sun.misc.Unsafe 中的内存访问方法</h3>\n<p>JEP 471 提议弃用 <code>sun.misc.Unsafe</code> 中的内存访问方法，这些方法将来的版本中会被移除。</p>\n<p>这些不安全的方法已有安全高效的替代方案：</p>\n<ul>\n<li><code>java.lang.invoke.VarHandle</code> ：JDK 9 (JEP 193) 中引入，提供了一种安全有效地操作堆内存的方法，包括对象的字段、类的静态字段以及数组元素。</li>\n<li><code>java.lang.foreign.MemorySegment</code> ：JDK 22 (JEP 454) 中引入，提供了一种安全有效地访问堆外内存的方法，有时会与 <code>VarHandle</code> 协同工作。</li>\n</ul>\n<p>这两个类是 Foreign Function &amp; Memory API（外部函数和内存 API） 的核心组件，分别用于管理和操作堆外内存。Foreign Function &amp; Memory API 在 JDK 22 中正式转正，成为标准特性。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> jdk.incubator.foreign.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.lang.invoke.VarHandle</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 管理堆外整数数组的类</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> OffHeapIntBuffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 用于访问整数元素的VarHandle</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> VarHandle</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> ELEM_VH </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">arrayElementVarHandle</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 内存管理器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Arena</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> arena</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 堆外内存段</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> MemorySegment</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 构造函数，分配指定数量的整数空间</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> OffHeapIntBuffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">arena</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">  =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Arena</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">ofShared</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> arena</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">allocate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, size);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 释放内存</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> deallocate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        arena</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">close</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 以volatile方式设置指定索引的值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> setVolatile</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> index</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        ELEM_VH</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setVolatile</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(buffer, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0L</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, index, value);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 初始化指定范围的元素为0</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> initialize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> n</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">asSlice</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">byteSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> start,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                       ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">byteSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> n)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">              .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fill</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">((</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">byte</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 将指定范围的元素复制到新数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#61AFEF\">[] </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">copyToNewArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> n</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">asSlice</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">byteSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> start,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                              ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">byteSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> n)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                     .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">JAVA_INT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>JEP 474：ZGC：默认的分代模式</h3>\n<p>Z 垃圾回收器 (ZGC) 的默认模式切换为分代模式，并弃用非分代模式，计划在未来版本中移除。这是因为分代 ZGC 是大多数场景下的更优选择。</p>\n<h3>JEP 476：模块导入声明 (预览)</h3>\n<p>模块导入声明允许在 Java 代码中简洁地导入整个模块的所有导出包，而无需逐个声明包的导入。这一特性简化了模块化库的重用，特别是在使用多个模块时，避免了大量的包导入声明，使得开发者可以更方便地访问第三方库和 Java 基本类。</p>\n<p>此特性对初学者和原型开发尤为有用，因为它无需开发者将自己的代码模块化，同时保留了对传统导入方式的兼容性，提升了开发效率和代码可读性。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 导入整个 java.base 模块，开发者可以直接访问 List、Map、Stream 等类，而无需每次手动导入相关包</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> module java.base</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Example</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">fruits</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> { </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"apple\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"berry\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"citrus\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> };</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Map</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">fruitMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Stream</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">of</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(fruits)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">collect</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Collectors</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                s </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> s</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toUpperCase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">substring</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">),</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                Function</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">identity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()));</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(fruitMap);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>JEP 477：未命名类和实例 main 方法 （第三次预览）</h3>\n<p>这个特性主要简化了 <code>main</code> 方法的声明。对于 Java 初学者来说，这个 <code>main</code> 方法的声明引入了太多的 Java 语法概念，不利于初学者快速上手。</p>\n<p>没有使用该特性之前定义一个 <code>main</code> 方法：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> HelloWorld</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello, World!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>使用该新特性之后定义一个 <code>main</code> 方法：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> HelloWorld</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello, World!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>进一步简化（未命名的类允许我们省略类名）</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">   System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello, World!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>JEP 480：结构化并发 （第三次预览）</h3>\n<p>Java 19 引入了结构化并发，一种多线程编程方法，目的是为了通过结构化并发 API 来简化多线程编程，并不是为了取代<code>java.util.concurrent</code>，目前处于孵化器阶段。</p>\n<p>结构化并发将不同线程中运行的多个任务视为单个工作单元，从而简化错误处理、提高可靠性并增强可观察性。也就是说，结构化并发保留了单线程代码的可读性、可维护性和可观察性。</p>\n<p>结构化并发的基本 API 是<a href=\"https://download.java.net/java/early_access/loom/docs/api/jdk.incubator.concurrent/jdk/incubator/concurrent/StructuredTaskScope.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>StructuredTaskScope</code></a>。<code>StructuredTaskScope</code> 支持将任务拆分为多个并发子任务，在它们自己的线程中执行，并且子任务必须在主任务继续之前完成。</p>\n<p><code>StructuredTaskScope</code> 的基本用法如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> scope </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> StructuredTaskScope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 使用fork方法派生线程来执行子任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Future</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> future1 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fork</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task1);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Future</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> future2 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fork</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task2);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 等待线程完成</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">join</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 结果的处理可能包括处理或重新抛出异常</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        ...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> process</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> results</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">exceptions </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// close</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>结构化并发非常适合虚拟线程，虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程，从而允许非常多的虚拟线程。</p>\n<h3>JEP 481：作用域值 （第三次预览）</h3>\n<p>作用域值（Scoped Values）可以在线程内和线程间共享不可变的数据，优于线程局部变量，尤其是在使用大量虚拟线程时。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ScopedValue</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;...></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> V </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ScopedValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// In some method</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ScopedValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">where</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(V, </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">           .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(() </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> { ... </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() ... </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">call</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> methods ... });</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// In a method called directly or indirectly from the lambda expression</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ...</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>作用域值允许在大型程序中的组件之间安全有效地共享数据，而无需求助于方法参数。</p>\n<h3>JEP 482：灵活的构造函数体（第二次预览）</h3>\n<p>这个特性最初在 JDK 22 由 <a href=\"https://openjdk.org/jeps/447\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 447: Statements before super(...) (Preview)</a>提出。</p>\n<p>Java 要求在构造函数中，<code>super(...)</code> 或 <code>this(...)</code> 调用必须作为第一条语句出现。这意味着我们无法在调用父类构造函数之前在子类构造函数中直接初始化字段。</p>\n<p>灵活的构造函数体解决了这一问题，它允许在构造函数体内，在调用 <code>super(..)</code> 或 <code>this(..)</code> 之前编写语句，这些语句可以初始化字段，但不能引用正在构造的实例。这样可以防止在父类构造函数中调用子类方法时，子类的字段未被正确初始化，增强了类构造的可靠性。</p>\n<p>这一特性解决了之前 Java 语法限制了构造函数代码组织的问题，让开发者能够更自由、更自然地表达构造函数的行为，例如在构造函数中直接进行参数验证、准备和共享，而无需依赖辅助方法或构造函数，提高了代码的可读性和可维护性。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Person</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> age</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Person</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> age</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (age </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> IllegalArgumentException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Age cannot be negative.\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> name; </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 在调用父类构造函数之前初始化字段</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">age</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> age;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ... 其他初始化代码</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Employee</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Person</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> employeeId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Employee</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> age</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> employeeId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">employeeId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> employeeId; </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 在调用父类构造函数之前初始化字段</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        super</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(name, age); </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 调用父类构造函数</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ... 其他初始化代码</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JDK 22</h2>\n<h3>JEP 423：G1 垃圾收集器区域固定</h3>\n<p>JEP 423 提出在 G1 垃圾收集器中实现区域固定（Region Pinning）功能，旨在减少由于 Java Native Interface (JNI) 关键区域导致的延迟问题。</p>\n<p>JNI 关键区域内的对象不能在垃圾收集时被移动，因此 G1 以往通过禁用垃圾收集解决该问题，导致线程阻塞及严重的延迟。通过在 G1 的老年代和年轻代中引入区域固定机制，允许在关键区域内固定对象所在的内存区域，同时继续回收未固定的区域，避免了禁用垃圾回收的需求。这种改进有助于显著降低延迟，提升系统在与 JNI 交互时的吞吐量和稳定性。</p>\n<h3>JEP 454：外部函数和内存 API</h3>\n<p>Java 程序可以通过该 API 与 Java 运行时之外的代码和数据进行互操作。通过高效地调用外部函数（即 JVM 之外的代码）和安全地访问外部内存（即不受 JVM 管理的内存），该 API 使 Java 程序能够调用本机库并处理本机数据，而不会像 JNI 那样危险和脆弱。</p>\n<p>外部函数和内存 API 在 Java 17 中进行了第一轮孵化，由 <a href=\"https://openjdk.java.net/jeps/412\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 412</a> 提出。Java 18 中进行了第二次孵化，由<a href=\"https://openjdk.org/jeps/419\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 419</a> 提出。Java 19 中是第一次预览，由 <a href=\"https://openjdk.org/jeps/424\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 424</a> 提出。JDK 20 中是第二次预览，由 <a href=\"https://openjdk.org/jeps/434\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 434</a> 提出。JDK 21 中是第三次预览，由 <a href=\"https://openjdk.org/jeps/442\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 442</a> 提出。</p>\n<p>最终，该特性在 JDK 22 中顺利转正。</p>\n<p>在 <a href=\"/java/new-features/java19.html\" target=\"_blank\">Java 19 新特性概览</a> 中，我有详细介绍到外部函数和内存 API，这里就不再做额外的介绍了。</p>\n<h3>JEP 456：未命名模式和变量</h3>\n<p>未命名模式和变量在 JDK 21 中由 <a href=\"https://openjdk.org/jeps/443\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 443</a>提出预览，JDK 22 中就已经转正。</p>\n<p>关于这个新特性的详细介绍，可以看看<a href=\"/java/new-features/java21.html\" target=\"_blank\">Java 21 新特性概览(重要)</a>这篇文章中的介绍。</p>\n<h3>JEP 458：启动多文件源代码程序</h3>\n<p>Java 11 引入了 <a href=\"https://openjdk.org/jeps/330\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 330：启动单文件源代码程序</a>，增强了 <code>java</code> 启动器的功能，使其能够直接运行单个 Java 源文件。通过命令 <code>java HelloWorld.java</code>，Java 可以在内存中隐式编译源代码并立即执行，而不需要在磁盘上生成 <code>.class</code> 文件。这简化了开发者在编写小型工具程序或学习 Java 时的工作流程，避免了手动编译的额外步骤。</p>\n<p>假设文件<code>Prog.java</code>声明了两个类：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Prog</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> { </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Helper</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(); }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Helper</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> { </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">); }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>java Prog.java</code>命令会在内存中编译两个类并执行<code>main</code>该文件中声明的第一个类的方法。</p>\n<p>这种方式有一个限制，程序的所有源代码必须放在一个<code>.java</code>文件中。</p>\n<p><a href=\"https://openjdk.org/jeps/458\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 458：启动多文件源代码程序</a> 是对 JEP 330 功能的扩展，允许直接运行由多个 Java 源文件组成的程序，而无需显式的编译步骤。</p>\n<p>假设一个目录中有两个 Java 源文件 <code>Prog.java</code> 和 <code>Helper.java</code>，每个文件各自声明了一个类：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// Prog.java</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Prog</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> { </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Helper</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(); }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// Helper.java</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Helper</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> { </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">); }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>当你运行命令 <code>java Prog.java</code> 时，Java 启动器会在内存中编译并执行 <code>Prog</code> 类的 <code>main</code> 方法。由于 <code>Prog</code> 类中的代码引用了 <code>Helper</code> 类，启动器会自动在文件系统中找到 <code>Helper.java</code> 文件，编译其中的 <code>Helper</code> 类，并在内存中执行它。这个过程是自动的，开发者无需显式调用 <code>javac</code> 来编译所有源文件。</p>\n<p>这一特性使得从小型项目到大型项目的过渡更加平滑，开发者可以自由选择何时引入构建工具，避免在快速迭代时被迫设置复杂的项目结构。该特性消除了单文件的限制，进一步简化了从单一文件到多文件程序的开发过程，特别适合原型开发、快速实验以及早期项目的探索阶段。</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png",
      "date_published": "2024-09-20T03:02:27.000Z",
      "date_modified": "2026-01-27T07:16:00.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "程序员最该拿的几种高含金量证书",
      "url": "https://javaguide.cn/high-quality-technical-articles/programmer/high-value-certifications-for-programmers.html",
      "id": "https://javaguide.cn/high-quality-technical-articles/programmer/high-value-certifications-for-programmers.html",
      "summary": "程序员最该拿的几种高含金量证书：围绕技术知识与面试总结梳理关键概念、常见问题与实践要点，帮助你高效学习与备战面试。",
      "content_html": "<p>证书是能有效证明自己能力的好东西，它就是你实力的象征。在短短的面试时间内，证书可以为你加不少分。通过考证来提升自己，是一种性价比很高的办法。不过，相比金融、建筑、医疗等行业，IT 行业的职业资格证书并没有那么多。</p>\n<p>下面我总结了一下程序员可以考的一些常见证书。</p>\n<h2>软考</h2>\n<p>全国计算机技术与软件专业技术资格（水平）考试，简称“软考”，是国内认可度较高的一项计算机技术资格认证。尽管一些人吐槽其实际价值，但在特定领域和情况下，它还是非常有用的，例如软考证书在国企和事业单位中具有较高的认可度、在某些城市软考证书可以用于积分落户、可用于个税补贴。</p>\n<p>软考有初、中、高三个级别，建议直接考高级。相比于 PMP（项目管理专业人士认证），软考高项的难度更大，特别是论文部分，绝大部分人都挂在了论文部分。过了软考高项，在一些单位可以内部挂证，每个月多拿几百。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/ruankao-advanced-certification .jpg\" alt=\"软考高级证书\"></p>\n<p>官网地址：<a href=\"https://www.ruankao.org.cn/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.ruankao.org.cn/</a>。</p>\n<p>备考建议：<a href=\"https://mp.weixin.qq.com/s/9aUXHJ7dXgrHuT19jRhCnw\" target=\"_blank\" rel=\"noopener noreferrer\">2024 年上半年，一次通过软考高级架构师考试的备考秘诀 - 阿里云开发者</a></p>\n<h2>PAT</h2>\n<p>攀拓计算机能力测评（PAT）是一个专注于考察算法能力的测评体系，由浙江大学主办。该测评分为四个级别：基础级、乙级、甲级和顶级。</p>\n<p>通过 PAT 测评并达到联盟企业规定的相应评级和分数，可以跳过学历门槛，免除筛选简历和笔试环节，直接获得面试机会。具体有哪些公司可以去官网看看：<a href=\"https://www.patest.cn/company\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.patest.cn/company</a> 。</p>\n<p>对于考研浙江大学的同学来说，PAT（甲级）成绩在一年内可以作为硕士研究生招生考试上机复试成绩。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/pat-enterprise-alliance.png\" alt=\"PAT（甲级）成绩作用\"></p>\n<h2>PMP</h2>\n<p>PMP（Project Management Professional）认证由美国项目管理协会（PMI）提供，是全球范围内认可度最高的项目管理专业人士资格认证。PMP 认证旨在提升项目管理专业人士的知识和技能，确保项目顺利完成。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/pmp-certification.png\" alt=\"PMP 证书\"></p>\n<p>PMP 是“一证在手，全球通用”的资格认证，对项目管理人士来说，PMP 证书含金量还是比较高的。放眼全球，很多成功企业都会将 PMP 认证作为项目经理的入职标准。</p>\n<p>但是！真正有价值的不是 PMP 证书，而是《PMBOK》 那套项目管理体系，在《PMBOK》（PMP 考试指定用书）中也包含了非常多商业活动、实业项目、组织规划、建筑行业等各个领域的项目案例。</p>\n<p>另外，PMP 证书不是一个高大上的证书，而是一个基础的证书。</p>\n<h2>ACP</h2>\n<p>ACP（Agile Certified Practitioner）认证同样由美国项目管理协会（PMI）提供，是项目管理领域的另一个重要认证。与 PMP（Project Management Professional）注重传统的瀑布方法论不同，ACP 专注于敏捷项目管理方法论，如 Scrum、Kanban、Lean、Extreme Programming（XP）等。</p>\n<h2>OCP</h2>\n<p>Oracle Certified Professional（OCP）是 Oracle 公司提供的一项专业认证，专注于 Oracle 数据库及相关技术。这个认证旨在验证和认证个人在使用和管理 Oracle 数据库方面的专业知识和技能。</p>\n<p>下图展示了 Oracle 认证的不同路径和相应的认证级别，分别是核心路径（Core Track）和专业路径（Speciality Track）。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/oracle-certified-professional.jpg\" alt=\"OCP 认证路径\"></p>\n<h2>阿里云认证</h2>\n<p>阿里云（Alibaba Cloud）提供的专业认证，认证方向包括云计算、大数据、人工智能、Devops 等。职业认证分为 ACA、ACP、ACE 三个等级，除了职业认证之外，还有一个开发者 Clouder 认证，这是专门为开发者设立的专项技能认证。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/aliyun-professional-certification.png\" alt></p>\n<p>官网地址：<a href=\"https://edu.aliyun.com/certification/\" target=\"_blank\" rel=\"noopener noreferrer\">https://edu.aliyun.com/certification/</a>。</p>\n<h2>华为认证</h2>\n<p>华为认证是由华为技术有限公司提供的面向 ICT（信息与通信技术）领域的专业认证，认证方向包括网络、存储、云计算、大数据、人工智能等，非常庞大的认证体系。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/huawei-professional-certification.png\" alt></p>\n<h2>AWS 认证</h2>\n<p>AWS 云认证考试是 AWS 云计算服务的官方认证考试，旨在验证 IT 专业人士在设计、部署和管理 AWS 基础架构方面的技能。</p>\n<p>AWS 认证分为多个级别，包括基础级、从业者级、助理级、专业级和专家级（Specialty），涵盖多个角色和技能：</p>\n<ul>\n<li><strong>基础级别</strong>：AWS Certified Cloud Practitioner，适合初学者，验证对 AWS 基础知识的理解，是最简单的入门认证。</li>\n<li><strong>助理级别</strong>：包括 AWS Certified Solutions Architect – Associate、AWS Certified Developer – Associate 和 AWS Certified SysOps Administrator – Associate，适合中级专业人士，验证其设计、开发和管理 AWS 应用的能力。</li>\n<li><strong>专业级别</strong>：包括 AWS Certified Solutions Architect – Professional 和 AWS Certified DevOps Engineer – Professional，适合高级专业人士，验证其在复杂和大规模 AWS 环境中的能力。</li>\n<li><strong>专家级别</strong>：包括 AWS Certified Advanced Networking – Specialty、AWS Certified Big Data – Specialty 等，专注于特定技术领域的深度知识和技能。</li>\n</ul>\n<p>备考建议：<a href=\"https://mp.weixin.qq.com/s/xAqNOnfZ05GDRuUbAiMHIA\" target=\"_blank\" rel=\"noopener noreferrer\">小白入门云计算的最佳方式，是去考一张 AWS 的证书（附备考经验）</a></p>\n<h2>Google Cloud 认证</h2>\n<p>与 AWS 认证不同，Google Cloud 认证只有一门助理级认证（Associate Cloud Engineer），其他大部分为专业级（专家级）认证。</p>\n<p>备考建议：<a href=\"https://mp.weixin.qq.com/s/Vw5LGPI_akA7TQl1FMygWw\" target=\"_blank\" rel=\"noopener noreferrer\">如何备考谷歌云认证</a></p>\n<p>官网地址：<a href=\"https://cloud.google.com/certification\" target=\"_blank\" rel=\"noopener noreferrer\">https://cloud.google.com/certification</a></p>\n<h2>微软认证</h2>\n<p>微软的认证体系主要针对其 Azure 云平台，分为基础级别、助理级别和专家级别，认证方向包括云计算、数据管理、开发、生产力工具等。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/microsoft-certification.png\" alt></p>\n<h2>Elastic 认证</h2>\n<p>Elastic 认证是由 Elastic 公司提供的一系列专业认证，旨在验证个人在使用 Elastic Stack（包括 Elasticsearch、Logstash、Kibana 、Beats 等）方面的技能和知识。</p>\n<p>如果你在日常开发核心工作是负责 ElasticSearch 相关业务的话，还是比较建议考的，含金量挺高。</p>\n<p>目前 Elastic 认证证书分为四类：Elastic Certified Engineer、Elastic Certified Analyst、Elastic Certified Observability Engineer、Elastic Certified SIEM Specialist。</p>\n<p>比较建议考 <strong>Elastic Certified Engineer</strong>，这个是 Elastic Stack 的基础认证，考察安装、配置、管理和维护 Elasticsearch 集群等核心技能。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/elastic-certified-engineer-certification.png\" alt></p>\n<h2>其他</h2>\n<ul>\n<li>PostgreSQL 认证：国内的 PostgreSQL 认证分为专员级（PCA）、专家级（PCP）和大师级（PCM），主要考查 PostgreSQL 数据库管理和优化，价格略贵，不是很推荐。</li>\n<li>Kubernetes 认证：Cloud Native Computing Foundation (CNCF) 提供了几个官方认证，例如 Certified Kubernetes Administrator (CKA)、Certified Kubernetes Application Developer (CKAD)，主要考察 Kubernetes 方面的技能和知识。</li>\n</ul>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/programmer-life/programmer-certification/ruankao-advanced-certification%20.jpg",
      "date_published": "2024-07-22T11:26:12.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [],
      "tags": [
        "技术文章精选集"
      ]
    },
    {
      "title": "CAS 详解",
      "url": "https://javaguide.cn/java/concurrent/cas.html",
      "id": "https://javaguide.cn/java/concurrent/cas.html",
      "summary": "CAS比较并交换深度解析：详解CAS原子操作原理、Unsafe类实现、ABA问题及解决方案、自旋锁机制、与悲观锁性能对比。",
      "content_html": "<p>乐观锁和悲观锁的介绍以及乐观锁常见实现方式可以阅读笔者写的这篇文章：<a href=\"https://javaguide.cn/java/concurrent/optimistic-lock-and-pessimistic-lock.html\" target=\"_blank\" rel=\"noopener noreferrer\">乐观锁和悲观锁详解</a>。</p>\n<p>这篇文章主要介绍 ：Java 中 CAS 的实现以及 CAS 存在的一些问题。</p>\n<h2>Java 中 CAS 是如何实现的？</h2>\n<p>在 Java 中，实现 CAS（Compare-And-Swap, 比较并交换）操作的一个关键类是<code>Unsafe</code>。</p>\n<p><code>Unsafe</code>类位于<code>sun.misc</code>包下，是一个提供低级别、不安全操作的类。由于其强大的功能和潜在的危险性，它通常用于 JVM 内部或一些需要极高性能和底层访问的库中，而不推荐普通开发者在应用程序中使用。关于 <code>Unsafe</code>类的详细介绍，可以阅读这篇文章：📌<a href=\"https://javaguide.cn/java/basis/unsafe.html\" target=\"_blank\" rel=\"noopener noreferrer\">Java 魔法类 Unsafe 详解</a>。</p>\n<p><code>sun.misc</code>包下的<code>Unsafe</code>类提供了<code>compareAndSwapObject</code>、<code>compareAndSwapInt</code>、<code>compareAndSwapLong</code>方法来实现的对<code>Object</code>、<code>int</code>、<code>long</code>类型的 CAS 操作：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 以原子方式更新对象字段的值。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> *</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic\">@param</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> o</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        要操作的对象</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic\">@param</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> offset</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   对象字段的内存偏移量</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic\">@param</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> expected</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> 期望的旧值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic\">@param</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> x</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        要设置的新值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic\">@return</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> 如果值被成功更新，则返回 true；否则返回 false</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> compareAndSwapObject</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> offset</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> expected</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 以原子方式更新 int 类型的对象字段的值。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> compareAndSwapInt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> offset</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> expected</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 以原子方式更新 long 类型的对象字段的值。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> compareAndSwapLong</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> offset</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> expected</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>Unsafe</code>类中的 CAS 方法是<code>native</code>方法。<code>native</code>关键字表明这些方法是用本地代码（通常是 C 或 C++）实现的，而不是用 Java 实现的。这些方法直接调用底层的硬件指令来实现原子操作。也就是说，Java 语言并没有直接用 Java 实现 CAS。</p>\n<p>更准确点来说，Java 中 CAS 是 C++ 内联汇编的形式实现的，通过 JNI（Java Native Interface） 调用。因此，CAS 的具体实现与操作系统以及 CPU 密切相关。</p>\n<p><code>java.util.concurrent.atomic</code> 包提供了一些用于原子操作的类。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/JUC原子类概览.png\" alt=\"JUC原子类概览\"></p>\n<p>关于这些 Atomic 原子类的介绍和使用，可以阅读这篇文章：<a href=\"https://javaguide.cn/java/concurrent/atomic-classes.html\" target=\"_blank\" rel=\"noopener noreferrer\">Atomic 原子类总结</a>。</p>\n<p>Atomic 类依赖于 CAS 乐观锁来保证其方法的原子性，而不需要使用传统的锁机制（如 <code>synchronized</code> 块或 <code>ReentrantLock</code>）。</p>\n<p><code>AtomicInteger</code>是 Java 的原子类之一，主要用于对 <code>int</code> 类型的变量进行原子操作，它利用<code>Unsafe</code>类提供的低级别原子操作方法实现无锁的线程安全性。</p>\n<p>下面，我们通过解读<code>AtomicInteger</code>的核心源码（JDK1.8），来说明 Java 如何使用<code>Unsafe</code>类的方法来实现原子操作。</p>\n<p><code>AtomicInteger</code>核心源码如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 获取 Unsafe 实例</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Unsafe</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> unsafe </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Unsafe</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getUnsafe</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> valueOffset</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 获取“value”字段在AtomicInteger类中的内存偏移量</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        valueOffset </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> unsafe</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">objectFieldOffset</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">AtomicInteger</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getDeclaredField</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"value\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Exception</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) { </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Error</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(ex)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 确保“value”字段的可见性</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> volatile</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 如果当前值等于预期值，则原子地将值设置为newValue</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 使用 Unsafe#compareAndSwapInt 方法进行CAS操作</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> compareAndSet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> expect</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> update) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> unsafe</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">compareAndSwapInt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, valueOffset, expect, update);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 原子地将当前值加 delta 并返回旧值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getAndAdd</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> delta) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> unsafe</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getAndAddInt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, valueOffset, delta);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 原子地将当前值加 1 并返回加之前的值（旧值）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 使用 Unsafe#getAndAddInt 方法进行CAS操作。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getAndIncrement</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> unsafe</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getAndAddInt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, valueOffset, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 原子地将当前值减 1 并返回减之前的值（旧值）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getAndDecrement</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> unsafe</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getAndAddInt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, valueOffset, </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>Unsafe#getAndAddInt</code>源码：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 原子地获取并增加整数值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getAndAddInt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> offset</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> delta) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> v</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    do</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 以 volatile 方式获取对象 o 在内存偏移量 offset 处的整数值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        v </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getIntVolatile</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> offset)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">compareAndSwapInt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> offset</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> v</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> v </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> delta))</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 返回旧值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> v</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>可以看到，<code>getAndAddInt</code> 使用了 <code>do-while</code> 循环：在<code>compareAndSwapInt</code>操作失败时，会不断重试直到成功。也就是说，<code>getAndAddInt</code>方法会通过 <code>compareAndSwapInt</code> 方法来尝试更新 <code>value</code> 的值，如果更新失败（当前值在此期间被其他线程修改），它会重新获取当前值并再次尝试更新，直到操作成功。</p>\n<p>由于 CAS 操作可能会因为并发冲突而失败，因此通常会与<code>while</code>循环搭配使用，在失败后不断重试，直到操作成功。这就是 <strong>自旋锁机制</strong> 。</p>\n<h2>CAS 算法存在哪些问题？</h2>\n<p>ABA 问题是 CAS 算法最常见的问题。</p>\n<h3>ABA 问题</h3>\n<p>如果一个变量 V 初次读取的时候是 A 值，并且在准备赋值的时候检查到它仍然是 A 值，那我们就能说明它的值没有被其他线程修改过了吗？很明显是不能的，因为在这段时间它的值可能被改为其他值，然后又改回 A，那 CAS 操作就会误认为它从来没有被修改过。这个问题被称为 CAS 操作的 <strong>&quot;ABA&quot;问题。</strong></p>\n<p>ABA 问题的解决思路是在变量前面追加上<strong>版本号或者时间戳</strong>。JDK 1.5 以后的 <code>AtomicStampedReference</code> 类就是用来解决 ABA 问题的，其中的 <code>compareAndSet()</code> 方法就是首先检查当前引用是否等于预期引用，并且当前标志是否等于预期标志，如果全部相等，则以原子方式将该引用和该标志的值设置为给定的更新值。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> compareAndSet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">   expectedReference</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">                             V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">   newReference</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                             int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> expectedStamp</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                             int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> newStamp) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Pair</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> current </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> pair</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        expectedReference </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> current</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">reference</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        expectedStamp </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> current</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">stamp</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        ((newReference </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> current</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">reference</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">          newStamp </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> current</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">stamp</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">||</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">         casPair</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(current</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Pair</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">of</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(newReference, newStamp)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">))</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>循环时间长开销大</h3>\n<p>CAS 经常会用到自旋操作来进行重试，也就是不成功就一直循环执行直到成功。如果长时间不成功，会给 CPU 带来非常大的执行开销。</p>\n<p>如果 JVM 能够支持处理器提供的<code>pause</code>指令，那么自旋操作的效率将有所提升。<code>pause</code>指令有两个重要作用：</p>\n<ol>\n<li><strong>延迟流水线执行指令</strong>：<code>pause</code>指令可以延迟指令的执行，从而减少 CPU 的资源消耗。具体的延迟时间取决于处理器的实现版本，在某些处理器上，延迟时间可能为零。</li>\n<li><strong>避免内存顺序冲突</strong>：在退出循环时，<code>pause</code>指令可以避免由于内存顺序冲突而导致的 CPU 流水线被清空，从而提高 CPU 的执行效率。</li>\n</ol>\n<h3>只能保证一个共享变量的原子操作</h3>\n<p>CAS 操作仅能对单个共享变量有效。当需要操作多个共享变量时，CAS 就显得无能为力。不过，从 JDK 1.5 开始，Java 提供了<code>AtomicReference</code>类，这使得我们能够保证引用对象之间的原子性。通过将多个变量封装在一个对象中，我们可以使用<code>AtomicReference</code>来执行 CAS 操作。</p>\n<p>除了 <code>AtomicReference</code> 这种方式之外，还可以利用加锁来保证。</p>\n<h2>总结</h2>\n<p>在 Java 中，CAS 通过 <code>Unsafe</code> 类中的 <code>native</code> 方法实现，这些方法调用底层的硬件指令来完成原子操作。由于其实现依赖于 C++ 内联汇编和 JNI 调用，因此 CAS 的具体实现与操作系统以及 CPU 密切相关。</p>\n<p>CAS 虽然具有高效的无锁特性，但也需要注意 ABA 、循环时间长开销大等问题。</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/JUC%E5%8E%9F%E5%AD%90%E7%B1%BB%E6%A6%82%E8%A7%88.png",
      "date_published": "2024-07-17T04:53:20.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "如何基于Redis实现延时任务？",
      "url": "https://javaguide.cn/database/redis/redis-delayed-task.html",
      "id": "https://javaguide.cn/database/redis/redis-delayed-task.html",
      "summary": "详解基于Redis实现延时任务的两种方案：过期事件监听和Redisson延时队列，分析各方案的优缺点、可靠性问题和适用场景。",
      "content_html": "<p>基于 Redis 实现延时任务的功能无非就下面两种方案：</p>\n<ol>\n<li>Redis 过期事件监听</li>\n<li>Redisson 内置的延时队列</li>\n</ol>\n<p>面试的时候，你可以先说自己考虑了这两种方案，但最后发现 Redis 过期事件监听这种方案存在很多问题，因此你最终选择了 Redisson 内置的 DelayedQueue 这种方案。</p>\n<p>这个时候面试官可能会追问你一些相关的问题，我们后面会提到，提前准备就好了。</p>\n<p>另外，除了下面介绍到的这些问题之外，Redis 相关的常见问题建议你都复习一遍，不排除面试官会顺带问你一些 Redis 的其他问题。</p>\n<h3>Redis 过期事件监听实现延时任务功能的原理？</h3>\n<p>Redis 2.0 引入了发布订阅 (pub/sub) 功能。在 pub/sub 中，引入了一个叫做 <strong>channel（频道）</strong> 的概念，有点类似于消息队列中的 <strong>topic（主题）</strong>。</p>\n<p>pub/sub 涉及发布者（publisher）和订阅者（subscriber，也叫消费者）两个角色：</p>\n<ul>\n<li>发布者通过 <code>PUBLISH</code> 投递消息给指定 channel。</li>\n<li>订阅者通过<code>SUBSCRIBE</code>订阅它关心的 channel。并且，订阅者可以订阅一个或者多个 channel。</li>\n</ul>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/redis/redis-pub-sub.png\" alt=\"Redis 发布订阅 (pub/sub) 功能\"></p>\n<p>在 pub/sub 模式下，生产者需要指定消息发送到哪个 channel 中，而消费者则订阅对应的 channel 以获取消息。</p>\n<p>Redis 中有很多默认的 channel，这些 channel 是由 Redis 本身向它们发送消息的，而不是我们自己编写的代码。其中，<code>__keyevent@0__:expired</code> 就是一个默认的 channel，负责监听 key 的过期事件。也就是说，当一个 key 过期之后，Redis 会发布一个 key 过期的事件到<code>__keyevent@&lt;db&gt;__:expired</code>这个 channel 中。</p>\n<p>我们只需要监听这个 channel，就可以拿到过期的 key 的消息，进而实现了延时任务功能。</p>\n<p>这个功能被 Redis 官方称为 <strong>keyspace notifications</strong> ，作用是实时监控 Redis 键和值的变化。</p>\n<h3>Redis 过期事件监听实现延时任务功能有什么缺陷？</h3>\n<p><strong>1、时效性差</strong></p>\n<p>官方文档的一段介绍解释了时效性差的原因，地址：<a href=\"https://redis.io/docs/manual/keyspace-notifications/#timing-of-expired-events\" target=\"_blank\" rel=\"noopener noreferrer\">https://redis.io/docs/manual/keyspace-notifications/#timing-of-expired-events</a> 。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/redis/redis-timing-of-expired-events.png\" alt=\"Redis 过期事件\"></p>\n<p>这段话的核心是：过期事件消息是在 Redis 服务器删除 key 时发布的，而不是一个 key 过期之后就会就会直接发布。</p>\n<p>我们知道常用的过期数据的删除策略就两个：</p>\n<ol>\n<li><strong>惰性删除</strong>：只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好，但是可能会造成太多过期 key 没有被删除。</li>\n<li><strong>定期删除</strong>：每隔一段时间抽取一批 key 执行删除过期 key 操作。并且，Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。</li>\n</ol>\n<p>定期删除对内存更加友好，惰性删除对 CPU 更加友好。两者各有千秋，所以 Redis 采用的是 <strong>定期删除+惰性/懒汉式删除</strong> 。</p>\n<p>因此，就会存在我设置了 key 的过期时间，但到了指定时间 key 还未被删除，进而没有发布过期事件的情况。</p>\n<p><strong>2、丢消息</strong></p>\n<p>Redis 的 pub/sub 模式中的消息并不支持持久化，这与消息队列不同。在 Redis 的 pub/sub 模式中，发布者将消息发送给指定的频道，订阅者监听相应的频道以接收消息。当没有订阅者时，消息会被直接丢弃，在 Redis 中不会存储该消息。</p>\n<p><strong>3、多服务实例下消息重复消费</strong></p>\n<p>Redis 的 pub/sub 模式目前只有广播模式，这意味着当生产者向特定频道发布一条消息时，所有订阅相关频道的消费者都能够收到该消息。</p>\n<p>这个时候，我们需要注意多个服务实例重复处理消息的问题，这会增加代码开发量和维护难度。</p>\n<h3>Redisson 延迟队列原理是什么？有什么优势？</h3>\n<p>Redisson 是一个开源的 Java 语言 Redis 客户端，提供了很多开箱即用的功能，比如多种分布式锁的实现、延时队列。</p>\n<p>我们可以借助 Redisson 内置的延时队列 RDelayedQueue 来实现延时任务功能。</p>\n<p>Redisson 的延迟队列 RDelayedQueue 是基于 Redis 的 SortedSet 来实现的。SortedSet 是一个有序集合，其中的每个元素都可以设置一个分数，代表该元素的权重。Redisson 利用这一特性，将需要延迟执行的任务插入到 SortedSet 中，并给它们设置相应的过期时间作为分数。</p>\n<p>Redisson 定期使用 <code>zrangebyscore</code> 命令扫描 SortedSet 中过期的元素，然后将这些过期元素从 SortedSet 中移除，并将它们加入到就绪消息列表中。就绪消息列表是一个阻塞队列，有消息进入就会被消费者监听到。这样做可以避免消费者对整个 SortedSet 进行轮询，提高了执行效率。</p>\n<p>相比于 Redis 过期事件监听实现延时任务功能，这种方式具备下面这些优势：</p>\n<ol>\n<li><strong>减少了丢消息的可能</strong>：DelayedQueue 中的消息会被持久化，即使 Redis 宕机了，根据持久化机制，也只可能丢失一点消息，影响不大。当然了，你也可以使用扫描数据库的方法作为补偿机制。</li>\n<li><strong>消息不存在重复消费问题</strong>：每个客户端都是从同一个目标队列中获取任务的，不存在重复消费的问题。</li>\n</ol>\n<p>跟 Redisson 内置的延时队列相比，消息队列可以通过保障消息消费的可靠性、控制消息生产者和消费者的数量等手段来实现更高的吞吐量和更强的可靠性，实际项目中首选使用消息队列的延时消息这种方案。</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/database/redis/redis-pub-sub.png",
      "date_published": "2024-03-23T02:55:07.000Z",
      "date_modified": "2026-03-23T08:33:10.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "Redis为什么用跳表实现有序集合",
      "url": "https://javaguide.cn/database/redis/redis-skiplist.html",
      "id": "https://javaguide.cn/database/redis/redis-skiplist.html",
      "summary": "深入讲解Redis有序集合Zset为何选择跳表而非红黑树、B+树实现，详解跳表的数据结构原理、时间复杂度分析和Redis源码实现。",
      "content_html": "<h2>前言</h2>\n<p>近几年针对 Redis 面试时会涉及常见数据结构的底层设计，其中就有这么一道比较有意思的面试题：“Redis 的有序集合底层为什么要用跳表，而不用平衡树、红黑树或者 B+树？”。</p>\n<p>本文就以这道大厂常问的面试题为切入点，带大家详细了解一下跳表这个数据结构。</p>\n<p>本文整体脉络如下图所示，笔者会从有序集合的基本使用到跳表的源码分析和实现，让你会对 Redis 的有序集合底层实现的跳表有着更深刻的理解和掌握。</p>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005468.png\" alt></p>\n<h2>跳表在 Redis 中的运用</h2>\n<p>这里我们需要先了解一下 Redis 用到跳表的数据结构有序集合的使用，Redis 有个比较常用的数据结构叫<strong>有序集合(sorted set，简称 zset)</strong>，正如其名它是一个可以保证有序且元素唯一的集合，所以它经常用于排行榜等需要进行统计排列的场景。</p>\n<p>这里我们通过命令行的形式演示一下排行榜的实现，可以看到笔者分别输入 3 名用户：<strong>xiaoming</strong>、<strong>xiaohong</strong>、<strong>xiaowang</strong>，它们的<strong>score</strong>分别是 60、80、60，最终按照成绩升级降序排列。</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">127.0.0.1:6379</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">zadd</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> rankList</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 60</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> xiaoming</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">127.0.0.1:6379</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">zadd</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> rankList</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 80</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> xiaohong</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">127.0.0.1:6379</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">zadd</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> rankList</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 60</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> xiaowang</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">1</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"># 返回有序集中指定区间内的成员，通过索引，分数从高到低</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">127.0.0.1:6379</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">ZREVRANGE</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> rankList</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 100</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> WITHSCORES</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"xiaohong\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"80\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"xiaowang\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">4</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"60\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">5</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"xiaoming\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">6</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"60\"</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>此时我们通过 <code>object</code> 指令查看 zset 的数据结构，可以看到当前有序集合存储的还是<strong>ziplist(压缩列表)</strong>。</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">127.0.0.1:6379</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">object</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> encoding</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> rankList</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">\"ziplist\"</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>因为设计者考虑到 Redis 数据存放于内存，为了节约宝贵的内存空间，在有序集合元素小于 64 字节且个数小于 128 的时候，会使用 ziplist，而这个阈值的默认值的设置就来自下面这两个配置项。</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">zset-max-ziplist-value</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 64</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">zset-max-ziplist-entries</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 128</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>一旦有序集合中的某个元素超出这两个其中的一个阈值它就会转为 <strong>skiplist</strong>（实际是 dict+skiplist，还会借用字典来提高获取指定元素的效率）。</p>\n<p>我们不妨在添加一个大于 64 字节的元素，可以看到有序集合的底层存储转为 skiplist。</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">127.0.0.1:6379</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">zadd</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> rankList</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 90</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> yigemingzihuichaoguo64zijiedeyonghumingchengyongyuceshitiaobiaodeshijiyunyong</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">1</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"># 超过阈值，转为跳表</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">127.0.0.1:6379</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">object</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> encoding</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> rankList</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">\"skiplist\"</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>也就是说，ZSet 有两种不同的实现，分别是 ziplist 和 skiplist，具体使用哪种结构进行存储的规则如下：</p>\n<ul>\n<li>当有序集合对象同时满足以下两个条件时，使用 ziplist：\n<ol>\n<li>ZSet 保存的键值对数量少于 128 个；</li>\n<li>每个元素的长度小于 64 字节。</li>\n</ol>\n</li>\n<li>如果不满足上述两个条件，那么使用 skiplist 。</li>\n</ul>\n<h2>手写一个跳表</h2>\n<p>为了更好的回答上述问题以及更好的理解和掌握跳表，这里可以通过手写一个简单的跳表的形式来帮助读者理解跳表这个数据结构。</p>\n<p>我们都知道有序链表在添加、查询、删除的平均时间复杂都都是 <strong>O(n)</strong> 即线性增长，所以一旦节点数量达到一定体量后其性能表现就会非常差劲。而跳表我们完全可以理解为在原始链表基础上，建立多级索引，通过多级索引检索定位将增删改查的时间复杂度变为 <strong>O(log n)</strong> 。</p>\n<p>可能这里说的有些抽象，我们举个例子，以下图跳表为例，其原始链表存储按序存储 1-10，有 2 级索引，每级索引的索引个数都是基于下层元素个数的一半。</p>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005436.png\" alt></p>\n<p>假如我们需要查询元素 6，其工作流程如下：</p>\n<ol>\n<li>从 2 级索引开始，先来到节点 4。</li>\n<li>查看 4 的后继节点，是 8 的 2 级索引，这个值大于 6，说明 2 级索引后续的索引都是大于 6 的，没有再往后搜寻的必要，我们索引向下查找。</li>\n<li>来到 4 的 1 级索引，比对其后继节点为 6，查找结束。</li>\n</ol>\n<p>相较于原始有序链表需要 6 次，我们的跳表通过建立多级索引，我们只需两次就直接定位到了目标元素，其查寻的复杂度被直接优化为<strong>O(log n)</strong>。</p>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005524.png\" alt></p>\n<p>对应的添加也是一个道理，假如我们需要在这个有序集合中添加一个元素 7，那么我们就需要通过跳表找到<strong>小于元素 7 的最大值</strong>，也就是下图元素 6 的位置，将其插入到元素 6 的后面，让元素 6 的索引指向新插入的节点 7，其工作流程如下：</p>\n<ol>\n<li>从 2 级索引开始定位到了元素 4 的索引。</li>\n<li>查看索引 4 的后继索引为 8，索引向下推进。</li>\n<li>来到 1 级索引，发现索引 4 后继索引为 6，小于插入元素 7，指针推进到索引 6 位置。</li>\n<li>继续比较 6 的后继节点为索引 8，大于元素 7，索引继续向下。</li>\n<li>最终我们来到 6 的原始节点，发现其后继节点为 7，指针没有继续向下的空间，自此我们可知元素 6 就是小于插入元素 7 的最大值，于是便将元素 7 插入。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005480.png\" alt></p>\n<p>这里我们又面临一个问题，我们是否需要为元素 7 建立索引，索引多高合适？</p>\n<p>我们上文提到，理想情况是每一层索引是下一层元素个数的二分之一，假设我们的总共有 16 个元素，对应各级索引元素个数应该是：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">1.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 一级索引:16/2=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">8</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">2.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 二级索引:8/2</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">4</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">3.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 三级索引:4/2=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>由此我们用数学归纳法可知：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">1.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 一级索引:16/2=16/2^1=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">8</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">2.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 二级索引:8/2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> => </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">16/2^2</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">4</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">3.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 三级索引:4/2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">=></span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">16/2^3=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>假设元素个数为 n，那么对应 k 层索引的元素个数 r 计算公式为:</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">r</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">n/2^k</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>同理我们再来推断以下索引的最大高度，一般来说最高级索引的元素个数为 2，我们设元素总个数为 n，索引高度为 h，代入上述公式可得：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> n/2^h</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">2</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">*</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">2^h=n</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">2^</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">h+1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">=n</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">h+1=log2^n</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">h=log2^n</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> -1</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>而 Redis 又是内存数据库，我们假设元素最大个数是<strong>65536</strong>，我们把<strong>65536</strong>代入上述公式可知最大高度为 16。所以我们建议添加一个元素后为其建立的索引高度不超过 16。</p>\n<p>因为我们要求尽可能保证每一个上级索引都是下级索引的一半，在实现高度生成算法时，我们可以这样设计：</p>\n<ol>\n<li>跳表的高度计算从原始链表开始，即默认情况下插入的元素的高度为 1，代表没有索引，只有元素节点。</li>\n<li>设计一个为插入元素生成节点索引高度 level 的方法。</li>\n<li>进行一次随机运算，随机数值范围为 0-1 之间。</li>\n<li>如果随机数大于 0.5 则为当前元素添加一级索引，自此我们保证生成一级索引的概率为 <strong>50%</strong> ，这也就保证了 1 级索引理想情况下只有一半的元素会生成索引。</li>\n<li>同理后续每次随机算法得到的值大于 0.5 时，我们的索引高度就加 1，这样就可以保证节点生成的 2 级索引概率为 <strong>25%</strong> ，3 级索引为 <strong>12.5%</strong> ……</li>\n</ol>\n<p>我们回过头，上述插入 7 之后，我们通过随机算法得到 2，即要为其建立 1 级索引：</p>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005505.png\" alt></p>\n<p>最后我们再来说说删除，假设我们这里要删除元素 10，我们必须定位到当前跳表<strong>各层</strong>元素小于 10 的最大值，索引执行步骤为：</p>\n<ol>\n<li>2 级索引 4 的后继节点为 8，指针推进。</li>\n<li>索引 8 无后继节点，该层无要删除的元素，指针直接向下。</li>\n<li>1 级索引 8 后继节点为 10，说明 1 级索引 8 在进行删除时需要将自己的指针和 1 级索引 10 断开联系，将 10 删除。</li>\n<li>1 级索引完成定位后，指针向下，后继节点为 9，指针推进。</li>\n<li>9 的后继节点为 10，同理需要让其指向 null，将 10 删除。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005503.png\" alt></p>\n<h3>模板定义</h3>\n<p>有了整体的思路之后，我们可以开始实现一个跳表了，首先定义一下跳表中的节点<strong>Node</strong>，从上文的演示中可以看出每一个<strong>Node</strong>它都包含以下几个元素：</p>\n<ol>\n<li>存储的<strong>value</strong>值。</li>\n<li>后继节点的地址。</li>\n<li>多级索引。</li>\n</ol>\n<p>为了更方便统一管理<strong>Node</strong>后继节点地址和多级索引指向的元素地址，笔者在<strong>Node</strong>中设置了一个<strong>forwards</strong>数组，用于记录原始链表节点的后继节点和多级索引的后继节点指向。</p>\n<p>以下图为例，我们<strong>forwards</strong>数组长度为 5，其中<strong>索引 0</strong>记录的是原始链表节点的后继节点地址，而其余自底向上表示从 1 级索引到 4 级索引的后继节点指向。</p>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005347.png\" alt></p>\n<p>于是我们的就有了这样一个代码定义，可以看出笔者对于数组的长度设置为固定的 16**(上文的推算最大高度建议是 16)<strong>，默认</strong>data<strong>为-1，节点最大高度</strong>maxLevel<strong>初始化为 1，注意这个</strong>maxLevel**的值代表原始链表加上索引的总高度。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 跳表索引最大高度为16</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> MAX_LEVEL </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 16</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> data </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] forwards </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[MAX_LEVEL]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> maxLevel </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>元素添加</h3>\n<p>定义好节点之后，我们先实现以下元素的添加，添加元素时首先自然是设置<strong>data</strong>这一步我们直接根据将传入的<strong>value</strong>设置到<strong>data</strong>上即可。</p>\n<p>然后就是高度<strong>maxLevel</strong>的设置 ，我们在上文也已经给出了思路，默认高度为 1，即只有一个原始链表节点，通过随机算法每次大于 0.5 索引高度加 1，由此我们得出高度计算的算法<code>randomLevel()</code>：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 理论来讲，一级索引中元素个数应该占原始数据的 50%，二级索引中元素个数占 25%，三级索引12.5% ，一直到最顶层。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 因为这里每一层的晋升概率是 50%。对于每一个新插入的节点，都需要调用 randomLevel 生成一个合理的层数。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 该 randomLevel 方法会随机生成 1~MAX_LEVEL 之间的数，且 ：</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 50%的概率返回 1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 25%的概率返回 2</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> *  12.5%的概率返回 3 ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic\">@return</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> randomLevel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> level </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Math</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">random</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> PROB </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x26;&#x26;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> level </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> MAX_LEVEL) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        ++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">level</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> level</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>然后再设置当前要插入的<strong>Node</strong>和<strong>Node</strong>索引的后继节点地址，这一步稍微复杂一点，我们假设当前节点的高度为 4，即 1 个节点加 3 个索引，所以我们创建一个长度为 4 的数组<strong>maxOfMinArr</strong> ，遍历各级索引节点中小于当前<strong>value</strong>的最大值。</p>\n<p>假设我们要插入的<strong>value</strong>为 5，我们的数组查找结果当前节点的前驱节点和 1 级索引、2 级索引的前驱节点都为 4，三级索引为空。</p>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005299.png\" alt></p>\n<p>然后我们基于这个数组<strong>maxOfMinArr</strong> 定位到各级的后继节点，让插入的元素 5 指向这些后继节点，而<strong>maxOfMinArr</strong>指向 5，结果如下图：</p>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005369.png\" alt></p>\n<p>转化成代码就是下面这个形式，是不是很简单呢？我们继续：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 默认情况下的高度为1，即只有自己一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 跳表最底层的节点，即头节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> h </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> level </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> randomLevel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 新节点的随机高度</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> newNode </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">maxLevel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> level</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 用于记录每层前驱节点的数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] update </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[level]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> level</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        update[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> h</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> h</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 关键修正：从跳表的当前最高层开始查找</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">--</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 只记录需要更新的层的前驱节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> level) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            update[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 插入新节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> level</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> update[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        update[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 更新跳表的总高度</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> level) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> level</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>元素查询</h3>\n<p>查询逻辑比较简单，从跳表最高级的索引开始定位找到小于要查的 value 的最大值，以下图为例，我们希望查找到节点 8：</p>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005323.png\" alt></p>\n<ul>\n<li><strong>从最高层级开始 (3 级索引)</strong> ：查找指针 <code>p</code> 从头节点开始。在 3 级索引上，<code>p</code> 的后继 <code>forwards[2]</code>（假设最高 3 层，索引从 0 开始）指向节点 <code>5</code>。由于 <code>5 &lt; 8</code>，指针 <code>p</code> 向右移动到节点 <code>5</code>。节点 <code>5</code> 在 3 级索引上的后继 <code>forwards[2]</code> 为 <code>null</code>（或指向一个大于 <code>8</code> 的节点，图中未画出）。当前层级向右查找结束，指针 <code>p</code> 保持在节点 <code>5</code>，<strong>向下移动到 2 级索引</strong>。</li>\n<li><strong>在 2 级索引</strong>：当前指针 <code>p</code> 为节点 <code>5</code>。<code>p</code> 的后继 <code>forwards[1]</code> 指向节点 <code>8</code>。由于 <code>8</code> 不小于 <code>8</code>（即 <code>8 &lt; 8</code> 为 <code>false</code>），当前层级向右查找结束（<code>p</code> 不会移动到节点 <code>8</code>）。指针 <code>p</code> 保持在节点 <code>5</code>，<strong>向下移动到 1 级索引</strong>。</li>\n<li><strong>在 1 级索引</strong> ：当前指针 <code>p</code> 为节点 <code>5</code>。<code>p</code> 的后继 <code>forwards[0]</code> 指向最底层的节点 <code>5</code>。由于 <code>5 &lt; 8</code>，指针 <code>p</code> 向右移动到最底层的节点 <code>5</code>。此时，当前指针 <code>p</code> 为最底层的节点 <code>5</code>。其后继 <code>forwards[0]</code> 指向最底层的节点 <code>6</code>。由于 <code>6 &lt; 8</code>，指针 <code>p</code> 向右移动到最底层的节点 <code>6</code>。当前指针 <code>p</code> 为最底层的节点 <code>6</code>。其后继 <code>forwards[0]</code> 指向最底层的节点 <code>7</code>。由于 <code>7 &lt; 8</code>，指针 <code>p</code> 向右移动到最底层的节点 <code>7</code>。当前指针 <code>p</code> 为最底层的节点 <code>7</code>。其后继 <code>forwards[0]</code> 指向最底层的节点 <code>8</code>。由于 <code>8</code> 不小于 <code>8</code>（即 <code>8 &lt; 8</code> 为 <code>false</code>），当前层级向右查找结束。此时，已经遍历完所有层级，<code>for</code> 循环结束。</li>\n<li><strong>最终定位与检查</strong> ：经过所有层级的查找，指针 <code>p</code> 最终停留在最底层（0 级索引）的节点 <code>7</code>。这个节点是整个跳表中值小于目标值 <code>8</code> 的那个最大的节点。检查节点 <code>7</code> 的<strong>后继节点</strong>（即 <code>p.forwards[0]</code>）：<code>p.forwards[0]</code> 指向节点 <code>8</code>。判断 <code>p.forwards[0].data</code>（即节点 <code>8</code> 的值）是否等于目标值 <code>8</code>。条件满足（<code>8 == 8</code>），<strong>查找成功，找到节点 <code>8</code></strong>。</li>\n</ul>\n<p>所以我们的代码实现也很上述步骤差不多，从最高级索引开始向前查找，如果不为空且小于要查找的值，则继续向前搜寻，遇到不小于的节点则继续向下，如此往复，直到得到当前跳表中小于查找值的最大节点，查看其前驱是否等于要查找的值：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> h</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 从头节点开始</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 从最高层级索引开始，逐层向下</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">--</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 在当前层级向右查找，直到 p.forwards[i] 为 null</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 或者 p.forwards[i].data 大于等于目标值 value</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 向右移动</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 此时 p.forwards[i] 为 null，或者 p.forwards[i].data >= value</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 或者 p 是当前层级中小于 value 的最大节点（如果存在这样的节点）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 经过所有层级的查找，p 现在是原始链表（0级索引）中</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 小于目标值 value 的最大节点（或者头节点，如果所有元素都大于等于 value）</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 检查 p 在原始链表中的下一个节点是否是目标值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ==</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 找到了，返回该节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 未找到</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>元素删除</h3>\n<p>最后是删除逻辑，需要查找各层级小于要删除节点的最大值，假设我们要删除 10：</p>\n<ol>\n<li>3 级索引得到小于 10 的最大值为 5，继续向下。</li>\n<li>2 级索引从索引 5 开始查找，发现小于 10 的最大值为 8，继续向下。</li>\n<li>同理 1 级索引得到 8，继续向下。</li>\n<li>原始节点找到 9。</li>\n<li>从最高级索引开始，查看每个小于 10 的节点后继节点是否为 10，如果等于 10，则让这个节点指向 10 的后继节点，将节点 10 及其索引交由 GC 回收。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005350.png\" alt></p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 删除</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> *</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic\">@param</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> value</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> delete</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> h</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //找到各级节点小于value的最大值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] updateArr </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[levelCount]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">--</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        updateArr[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //查看原始层节点前驱是否等于value，若等于则说明存在要删除的值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ==</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //从最高级索引开始查看其前驱是否等于value，若等于则将当前节点指向value节点的后继节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">--</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (updateArr[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> updateArr[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ==</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                updateArr[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> updateArr[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //从最高级开始查看是否有一级索引为空，若为空则层级减1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> h</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        levelCount</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">--;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>完整代码以及测试</h3>\n<p>完整代码如下，读者可自行参阅:</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> SkipList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 跳表索引最大高度为16</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> MAX_LEVEL </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 16</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 每个节点添加一层索引高度的概率为二分之一</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> PROB </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0.5f</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 默认情况下的高度为1，即只有自己一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 跳表最底层的节点，即头节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> h </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> SkipList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> data </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">         *</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">         */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] forwards </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[MAX_LEVEL]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> maxLevel </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> toString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Node{\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">                    +</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"data=\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> data</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">                    +</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \", maxLevel=\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> maxLevel</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">                    +</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '}'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> level</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> randomLevel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(); </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 新节点的随机高度</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> value;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">maxLevel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> level;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 用于记录每层前驱节点的数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">update</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[level];</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> level; i++) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            update[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> h;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> h;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 关键修正：从跳表的当前最高层开始查找</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i--) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i];</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 只记录需要更新的层的前驱节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> level) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                update[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> p;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 插入新节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> level; i++) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> update[i].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i];</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            update[i].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> newNode;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 更新跳表的总高度</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> level) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> level;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 理论来讲，一级索引中元素个数应该占原始数据的 50%，二级索引中元素个数占 25%，三级索引12.5% ，一直到最顶层。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 因为这里每一层的晋升概率是 50%。对于每一个新插入的节点，都需要调用 randomLevel 生成一个合理的层数。 该 randomLevel</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 方法会随机生成 1~MAX_LEVEL 之间的数，且 ： 50%的概率返回 1 25%的概率返回 2 12.5%的概率返回 3 ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     *</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * </span><span style=\"--shiki-light:#A626A4;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic\">@return</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> randomLevel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> level</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Math</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">random</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> PROB </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x26;&#x26;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> level </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> MAX_LEVEL) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            ++level;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> level;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> h;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //找到小于value的最大值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i--) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i];</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //如果p的前驱节点等于value则直接返回</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ==</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">];</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 删除</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     *</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * </span><span style=\"--shiki-light:#A626A4;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic\">@param</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> value</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> delete</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> h;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //找到各级节点小于value的最大值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">updateArr</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[levelCount];</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i--) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i];</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            updateArr[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> p;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //查看原始层节点前驱是否等于value，若等于则说明存在要删除的值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ==</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //从最高级索引开始查看其前驱是否等于value，若等于则将当前节点指向value节点的后继节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i--) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (updateArr[i].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> updateArr[i].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ==</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                    updateArr[i].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> updateArr[i].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[i];</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //从最高级开始查看是否有一级索引为空，若为空则层级减1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> h</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[levelCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            levelCount--;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> printAll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> h;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //基于最底层的非索引层进行遍历，只要后继节点不为空，则速速出当前节点，并移动到后继节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">]);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">forwards</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">];</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>测试代码：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] args) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        SkipList</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> skipList </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> SkipList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 24</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            skipList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"**********输出添加结果**********\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        skipList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">printAll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        SkipList</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> node </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> skipList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">22</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"**********查询结果:\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\" **********\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        skipList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">delete</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">22</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"**********删除结果**********\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        skipList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">printAll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>Redis 跳表的特点</strong>：</p>\n<ol>\n<li>采用<strong>双向链表</strong>，不同于上面的示例，存在一个回退指针。主要用于简化操作，例如删除某个元素时，还需要找到该元素的前驱节点，使用回退指针会非常方便。</li>\n<li><code>score</code> 值可以重复，如果 <code>score</code> 值一样，则按照 ele（节点存储的值，为 sds）字典排序</li>\n<li>Redis 跳跃表默认允许最大的层数是 32，被源码中 <code>ZSKIPLIST_MAXLEVEL</code> 定义。</li>\n</ol>\n<h2>和其余三种数据结构的比较</h2>\n<p>最后，我们再来回答一下文章开头的那道面试题: “Redis 的有序集合底层为什么要用跳表，而不用平衡树、红黑树或者 B+树？”。</p>\n<h3>平衡树 vs 跳表</h3>\n<p>先来说说它和平衡树的比较，平衡树我们又会称之为 <strong>AVL 树</strong>，是一个严格的平衡二叉树，平衡条件必须满足（所有节点的左右子树高度差不超过 1，即平衡因子为范围为 <code>[-1,1]</code>）。平衡树的插入、删除和查询的时间复杂度和跳表一样都是 <strong>O(log n)</strong> 。</p>\n<p>对于范围查询来说，它也可以通过中序遍历的方式达到和跳表一样的效果。但是它的每一次插入或者删除操作都需要保证整颗树左右节点的绝对平衡，只要不平衡就要通过旋转操作来保持平衡，这个过程是比较耗时的。</p>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005312.png\" alt></p>\n<p>跳表诞生的初衷就是为了克服平衡树的一些缺点，跳表的发明者在论文<a href=\"https://15721.courses.cs.cmu.edu/spring2018/papers/08-oltpindexes1/pugh-skiplists-cacm1990.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">《Skip lists: a probabilistic alternative to balanced trees》</a>中有详细提到：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/redis/skiplist-a-probabilistic-alternative-to-balanced-trees.png\" alt></p>\n<blockquote>\n<p>Skip lists are a data structure that can be used in place of balanced trees. Skip lists use probabilistic balancing rather than strictly enforced balancing and as a result the algorithms for insertion and deletion in skip lists are much simpler and significantly faster than equivalent algorithms for balanced trees.</p>\n<p>跳表是一种可以用来代替平衡树的数据结构。跳表使用概率平衡而不是严格强制的平衡，因此，跳表中的插入和删除算法比平衡树的等效算法简单得多，速度也快得多。</p>\n</blockquote>\n<p>笔者这里也贴出了 AVL 树插入操作的核心代码，可以看出每一次添加操作都需要进行一次递归定位插入位置，然后还需要根据回溯到根节点检查沿途的各层节点是否失衡，再通过旋转节点的方式进行调整。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 向二分搜索树中添加新的元素(key, value)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    root </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(root</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 向以node为根的二分搜索树中插入元素(key, value)，递归算法</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 返回插入新节点后二分搜索树的根</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value) {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (node </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">compareTo</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">compareTo</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">right</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">right</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    else</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // key.compareTo(node.key) == 0</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">height</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Math</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">max</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getHeight</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">), </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getHeight</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">right</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">));</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> balanceFactor </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getBalanceFactor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(node)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // LL型需要右旋</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (balanceFactor </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getBalanceFactor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> rightRotate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(node)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //RR型失衡需要左旋</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (balanceFactor </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getBalanceFactor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">right</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> leftRotate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(node)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //LR需要先左旋成LL型，然后再右旋</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (balanceFactor </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getBalanceFactor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> leftRotate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> rightRotate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(node)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //RL</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (balanceFactor </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getBalanceFactor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">right</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">right</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> rightRotate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">right</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> leftRotate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(node)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>红黑树 vs 跳表</h3>\n<p>红黑树（Red Black Tree）也是一种自平衡二叉查找树，它的查询性能略微逊色于 AVL 树，但插入和删除效率更高。红黑树的插入、删除和查询的时间复杂度和跳表一样都是 <strong>O(log n)</strong> 。</p>\n<p>红黑树是一个<strong>黑平衡树</strong>，即从任意节点到另外一个叶子叶子节点，它所经过的黑节点是一样的。当对它进行插入操作时，需要通过旋转和染色（红黑变换）来保证黑平衡。不过，相较于 AVL 树为了维持平衡的开销要小一些。关于红黑树的详细介绍，可以查看这篇文章：<a href=\"https://javaguide.cn/cs-basics/data-structure/red-black-tree.html\" target=\"_blank\" rel=\"noopener noreferrer\">红黑树</a>。</p>\n<p>相比较于红黑树来说，跳表的实现也更简单一些。并且，按照区间来查找数据这个操作，红黑树的效率没有跳表高。</p>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005709.png\" alt></p>\n<p>对应红黑树添加的核心代码如下，读者可自行参阅理解：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> V </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> V </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> val) {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (node </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> val)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">compareTo</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> val)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">compareTo</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">right</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">right</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> val)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">val</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> val</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //左节点不为红，右节点为红，左旋</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isRed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">right</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x26;&#x26;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> !</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isRed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        node </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> leftRotate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(node)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //左链右旋</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isRed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x26;&#x26;</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> isRed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        node </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> rightRotate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(node)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //颜色翻转</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isRed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">left</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x26;&#x26;</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> isRed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">right</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        flipColors</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(node)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>B+树 vs 跳表</h3>\n<p>想必使用 MySQL 的读者都知道 B+树这个数据结构，B+树是一种常用的数据结构，具有以下特点：</p>\n<ol>\n<li><strong>多叉树结构</strong>：它是一棵多叉树，每个节点可以包含多个子节点，减小了树的高度，查询效率高。</li>\n<li><strong>存储效率高</strong>:其中非叶子节点存储多个 key，叶子节点存储 value，使得每个节点更够存储更多的键，根据索引进行范围查询时查询效率更高。-</li>\n<li><strong>平衡性</strong>：它是绝对的平衡，即树的各个分支高度相差不大，确保查询和插入时间复杂度为 <strong>O(log n)</strong> 。</li>\n<li><strong>顺序访问</strong>：叶子节点间通过链表指针相连，范围查询表现出色。</li>\n<li><strong>数据均匀分布</strong>：B+树插入时可能会导致数据重新分布，使得数据在整棵树分布更加均匀，保证范围查询和删除效率。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005649.png\" alt></p>\n<p>所以，B+树更适合作为数据库和文件系统中常用的索引结构之一，它的核心思想是通过可能少的 IO 定位到尽可能多的索引来获得查询数据。对于 Redis 这种内存数据库来说，它对这些并不感冒，因为 Redis 作为内存数据库它不可能存储大量的数据，所以对于索引不需要通过 B+树这种方式进行维护，只需按照概率进行随机维护即可，节约内存。而且使用跳表实现 zset 时相较前者来说更简单一些，在进行插入时只需通过索引将数据插入到链表中合适的位置再随机维护一定高度的索引即可，也不需要像 B+树那样插入时发现失衡时还需要对节点分裂与合并。</p>\n<h3>Redis 作者给出的理由</h3>\n<p>当然我们也可以通过 Redis 的作者自己给出的理由:</p>\n<blockquote>\n<p>There are a few reasons:<br>\n1、They are not very memory intensive. It's up to you basically. Changing parameters about the probability of a node to have a given number of levels will make then less memory intensive than btrees.<br>\n2、A sorted set is often target of many ZRANGE or ZREVRANGE operations, that is, traversing the skip list as a linked list. With this operation the cache locality of skip lists is at least as good as with other kind of balanced trees.<br>\n3、They are simpler to implement, debug, and so forth. For instance thanks to the skip list simplicity I received a patch (already in Redis master) with augmented skip lists implementing ZRANK in O(log(N)). It required little changes to the code.</p>\n</blockquote>\n<p>翻译过来的意思就是:</p>\n<blockquote>\n<p>有几个原因：</p>\n<p>1、它们不是很占用内存。这主要取决于你。改变节点拥有给定层数的概率的参数，会使它们比 B 树更节省内存。</p>\n<p>2、有序集合经常是许多 ZRANGE 或 ZREVRANGE 操作的目标，也就是说，以链表的方式遍历跳表。通过这种操作，跳表的缓存局部性至少和其他类型的平衡树一样好。</p>\n<p>3、它们更容易实现、调试等等。例如，由于跳表的简单性，我收到了一个补丁（已经在 Redis 主分支中），用增强的跳表实现了 O(log(N))的 ZRANK。它只需要对代码做很少的修改。</p>\n</blockquote>\n<h2>小结</h2>\n<p>本文通过大量篇幅介绍跳表的工作原理和实现，帮助读者更进一步的熟悉跳表这一数据结构的优劣，最后再结合各个数据结构操作的特点进行比对，从而帮助读者更好的理解这道面试题，建议读者实现理解跳表时，尽可能配合执笔模拟来了解跳表的增删改查详细过程。</p>\n<h2>参考</h2>\n<ul>\n<li>为啥 redis 使用跳表(skiplist)而不是使用 red-black？:<a href=\"https://www.zhihu.com/question/20202931/answer/16086538\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.zhihu.com/question/20202931/answer/16086538</a></li>\n<li>Skip List--跳表（全网最详细的跳表文章没有之一）:<a href=\"https://www.jianshu.com/p/9d8296562806\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.jianshu.com/p/9d8296562806</a></li>\n<li>Redis 对象与底层数据结构详解:<a href=\"https://blog.csdn.net/shark_chili3007/article/details/104171986\" target=\"_blank\" rel=\"noopener noreferrer\">https://blog.csdn.net/shark_chili3007/article/details/104171986</a></li>\n<li>Redis 有序集合(sorted set):<a href=\"https://www.runoob.com/redis/redis-sorted-sets.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.runoob.com/redis/redis-sorted-sets.html</a></li>\n<li>红黑树和跳表比较:<a href=\"https://zhuanlan.zhihu.com/p/576984787\" target=\"_blank\" rel=\"noopener noreferrer\">https://zhuanlan.zhihu.com/p/576984787</a></li>\n<li>为什么 redis 的 zset 用跳跃表而不用 b+ tree？:<a href=\"https://blog.csdn.net/f80407515/article/details/129136998\" target=\"_blank\" rel=\"noopener noreferrer\">https://blog.csdn.net/f80407515/article/details/129136998</a></li>\n</ul>\n",
      "image": "https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005468.png",
      "date_published": "2024-01-29T16:10:36.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "访问网页的全过程（知识串联）",
      "url": "https://javaguide.cn/cs-basics/network/the-whole-process-of-accessing-web-pages.html",
      "id": "https://javaguide.cn/cs-basics/network/the-whole-process-of-accessing-web-pages.html",
      "summary": "串联从输入 URL 到页面渲染的完整链路，涵盖 DNS、TCP、HTTP 与静态资源加载，助力面试与实践理解。",
      "content_html": "<p>开发岗中总是会考很多计算机网络的知识点，但如果让面试官只考一道题，便涵盖最多的计网知识点，那可能就是 <strong>网页浏览的全过程</strong> 了。本篇文章将带大家从头到尾过一遍这道被考烂的面试题，必会！！！</p>\n<p>总的来说，网络通信模型可以用下图来表示，也就是大家只要熟记网络结构五层模型，按照这个体系，很多知识点都能顺出来了。访问网页的过程也是如此。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/network/five-layers.png\" alt></p>\n<p>开始之前，我们先简单过一遍完整流程：</p>\n<ol>\n<li>在浏览器中输入指定网页的 URL。</li>\n<li>浏览器通过 DNS 协议，获取域名对应的 IP 地址。</li>\n<li>浏览器根据 IP 地址和端口号，向目标服务器发起一个 TCP 连接请求。</li>\n<li>浏览器在 TCP 连接上，向服务器发送一个 HTTP 请求报文，请求获取网页的内容。</li>\n<li>服务器收到 HTTP 请求报文后，处理请求，并返回 HTTP 响应报文给浏览器。</li>\n<li>浏览器收到 HTTP 响应报文后，解析响应体中的 HTML 代码，渲染网页的结构和样式，同时根据 HTML 中的其他资源的 URL（如图片、CSS、JS 等），再次发起 HTTP 请求，获取这些资源的内容，直到网页完全加载显示。</li>\n<li>浏览器在不需要和服务器通信时，可以主动关闭 TCP 连接，或者等待服务器的关闭请求。</li>\n</ol>\n<h2>应用层</h2>\n<p>一切的开始——打开浏览器，在地址栏输入 URL，回车确认。那么，什么是 URL？访问 URL 有什么用？</p>\n<h3>URL</h3>\n<p>URL（Uniform Resource Locators），即统一资源定位器。网络上的所有资源都靠 URL 来定位，每一个文件就对应着一个 URL，就像是路径地址。理论上，文件资源和 URL 一一对应。实际上也有例外，比如某些 URL 指向的文件已经被重定位到另一个位置，这样就有多个 URL 指向同一个文件。</p>\n<h3>URL 的组成结构</h3>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/network/URL-parts.png\" alt=\"URL的组成结构\"></p>\n<ol>\n<li>协议。URL 的前缀通常表示了该网址采用了何种应用层协议，通常有两种——HTTP 和 HTTPS。当然也有一些不太常见的前缀头，比如文件传输时用到的<code>ftp:</code>。</li>\n<li>域名。域名便是访问网址的通用名，这里也有可能是网址的 IP 地址，域名可以理解为 IP 地址的可读版本，毕竟绝大部分人都不会选择记住一个网址的 IP 地址。</li>\n<li>端口。如果指明了访问网址的端口的话，端口会紧跟在域名后面，并用一个冒号隔开。</li>\n<li>资源路径。域名（端口）后紧跟的就是资源路径，从第一个<code>/</code>开始，表示从服务器上根目录开始进行索引到的文件路径，上图中要访问的文件就是服务器根目录下<code>/path/to/myfile.html</code>。早先的设计是该文件通常物理存储于服务器主机上，但现在随着网络技术的进步，该文件不一定会物理存储在服务器主机上，有可能存放在云上，而文件路径也有可能是虚拟的（遵循某种规则）。</li>\n<li>参数。参数是浏览器在向服务器提交请求时，在 URL 中附带的参数。服务器解析请求时，会提取这些参数。参数采用键值对的形式<code>key=value</code>，每一个键值对使用<code>&amp;</code>隔开。参数的具体含义和请求操作的具体方法有关。</li>\n<li>锚点。锚点顾名思义，是在要访问的页面上的一个锚。要访问的页面大部分都多于一页，如果指定了锚点，那么在客户端显示该网页是就会定位到锚点处，相当于一个小书签。值得一提的是，在 URL 中，锚点以<code>#</code>开头，并且<strong>不会</strong>作为请求的一部分发送给服务端。</li>\n</ol>\n<h3>DNS</h3>\n<p>键入了 URL 之后，第一个重头戏登场——DNS 服务器解析。DNS（Domain Name System）域名系统，要解决的是 <strong>域名和 IP 地址的映射问题</strong> 。毕竟，域名只是一个网址便于记住的名字，而网址真正存在的地址其实是 IP 地址。</p>\n<p>传送门：<a href=\"https://javaguide.cn/cs-basics/network/dns.html\" target=\"_blank\" rel=\"noopener noreferrer\">DNS 域名系统详解（应用层）</a></p>\n<h3>HTTP/HTTPS</h3>\n<p>利用 DNS 拿到了目标主机的 IP 地址之后，浏览器便可以向目标 IP 地址发送 HTTP 报文，请求需要的资源了。在这里，根据目标网站的不同，请求报文可能是 HTTP 协议或安全性增强的 HTTPS 协议。</p>\n<p>传送门：</p>\n<ul>\n<li><a href=\"https://javaguide.cn/cs-basics/network/http-vs-https.html\" target=\"_blank\" rel=\"noopener noreferrer\">HTTP vs HTTPS（应用层）</a></li>\n<li><a href=\"https://javaguide.cn/cs-basics/network/http1.0-vs-http1.1.html\" target=\"_blank\" rel=\"noopener noreferrer\">HTTP 1.0 vs HTTP 1.1（应用层）</a></li>\n<li><a href=\"https://javaguide.cn/cs-basics/network/http-status-codes.html\" target=\"_blank\" rel=\"noopener noreferrer\">HTTP 常见状态码总结（应用层）</a></li>\n</ul>\n<h2>传输层</h2>\n<p>由于 HTTP 协议是基于 TCP 协议的，在应用层的数据封装好以后，要交给传输层，经 TCP 协议继续封装。</p>\n<p>TCP 协议保证了数据传输的可靠性，是数据包传输的主力协议。</p>\n<p>传送门：</p>\n<ul>\n<li><a href=\"https://javaguide.cn/cs-basics/network/tcp-connection-and-disconnection.html\" target=\"_blank\" rel=\"noopener noreferrer\">TCP 三次握手和四次挥手（传输层）</a></li>\n<li><a href=\"https://javaguide.cn/cs-basics/network/tcp-reliability-guarantee.html\" target=\"_blank\" rel=\"noopener noreferrer\">TCP 传输可靠性保障（传输层）</a></li>\n</ul>\n<h2>网络层</h2>\n<p>终于，来到网络层，此时我们的主机不再是和另一台主机进行交互了，而是在和中间系统进行交互。也就是说，应用层和传输层都是端到端的协议，而网络层及以下都是中间件的协议了。</p>\n<p><strong>网络层的核心功能——转发与路由</strong>，必会！！！如果面试官问到了网络层，而你恰好又什么都不会的话，最最起码要说出这五个字——<strong>转发与路由</strong>。</p>\n<ul>\n<li>转发：将分组从路由器的输入端口转移到合适的输出端口。</li>\n<li>路由：确定分组从源到目的经过的路径。</li>\n</ul>\n<p>所以到目前为止，我们的数据包经过了应用层、传输层的封装，来到了网络层，终于开始准备在物理层面传输了，第一个要解决的问题就是——<strong>往哪里传输？或者说，要把数据包发到哪个路由器上？</strong> 这便是 BGP 协议要解决的问题。</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/cs-basics/network/five-layers.png",
      "date_published": "2024-01-29T14:06:19.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "计算机基础"
      ]
    },
    {
      "title": "美团三年，总结的10条血泪教训",
      "url": "https://javaguide.cn/high-quality-technical-articles/advanced-programmer/meituan-three-year-summary-lesson-10.html",
      "id": "https://javaguide.cn/high-quality-technical-articles/advanced-programmer/meituan-three-year-summary-lesson-10.html",
      "summary": "美团三年，总结的10条血泪教训：围绕技术知识与面试总结梳理关键概念、常见问题与实践要点，帮助你高效学习与备战面试。",
      "content_html": "<blockquote>\n<p><strong>推荐语</strong>：作者用了很多生动的例子和故事展示了自己在美团的成长和感悟，看了之后受益颇多！</p>\n<p><strong>内容概览</strong>：</p>\n<p>本文的作者提出了以下十条建议，希望能对其他职场人有所启发和帮助：</p>\n<ol>\n<li>结构化思考与表达，提高个人影响力</li>\n<li>忘掉职级，该怼就怼，推动事情往前走</li>\n<li>用好平台资源，结识优秀的人，学习通识课</li>\n<li>一切都是争取来的，不要等待机会，要主动寻求</li>\n<li>关注商业，升维到老板思维，看清趋势，及时止损</li>\n<li>培养数据思维，利用数据了解世界，指导决策</li>\n<li>做一个好&quot;销售&quot;，无论是自己还是产品，都要学会展示和说服</li>\n<li>少加班多运动，保持身心健康，提高工作效率</li>\n<li>有随时可以离开的底气，不要被职场所困，借假修真，提升自己</li>\n<li>只是一份工作，不要过分纠结，相信自己，走出去看看</li>\n</ol>\n<p><strong>原文地址</strong>：<a href=\"https://mp.weixin.qq.com/s/XidSVIwd4oKkDKEICaY1mQ\" target=\"_blank\" rel=\"noopener noreferrer\">https://mp.weixin.qq.com/s/XidSVIwd4oKkDKEICaY1mQ</a></p>\n</blockquote>\n<p>在美团的三年多时光，如同一部悠长的交响曲，高高低低，而今离开已有一段时间。闲暇之余，梳理了三年多的收获与感慨，总结成 10 条，既是对过去一段时光的一个深情回眸，也是对未来之路的一份期许。</p>\n<p>倘若一些感悟能为刚步入职场的年轻人，或是刚在职业生涯中崭露头角的后起之秀，带来一点点启示与帮助，也是莫大的荣幸。</p>\n<h2>01 结构化思考与表达</h2>\n<p>美团是一家特别讲究方法论的公司，人人都要熟读四大名著《高效能人士的七个习惯》、《金字塔原理》、《用图表说话》和《学会提问》。</p>\n<p>与结构化思考和表达相关的，是《金字塔原理》，作者是麦肯锡公司第一位女性咨询顾问。这本书告诉我们，思考和表达的过程，就像构建金字塔（或者构建一棵树），先有整体结论，再寻找证据，证据之间要讲究相互独立、而且能穷尽（MECE 原则），论证的过程也要按特定的顺序进行，比如时间顺序、空间顺序、重要性顺序……</p>\n<p>作为大厂社畜，日常很大一部分工作就是写文档、看别人文档。大家做的事，但最后呈现的结果却有很大差异。一篇逻辑清晰、详略得当的文档，给人一种如沐春风的感受，能提炼出重要信息，是好的参考指南。</p>\n<p>结构化思考与表达算是职场最通用的能力，也是打造个人影响力最重要的途径之一。</p>\n<h2>02 忘掉职级，该怼就怼</h2>\n<p>在阿里工作时，能看到每个人的 Title，看到江湖地位高（职级高+入职时间早）的同学，即便跟自己没有汇报关系，不自然的会多一层敬畏。推进工作时，会多一层压力，对方未读或已读未回时，不知如何应对。</p>\n<p>美团只能看到每个人的坑位信息，还有 Ta 的上级。工作相关的问题，可以向任何人提问，如果协同方没有及时响应，隔段时间@一次，甚至&quot;怼一怼&quot;，都没啥问题，事情一直往前推进才最重要。除了大象消息直接提问外，还有个大杀器--TT（公司级问题流转系统），在上面提问时，加上对方主管，如果对方未及时回应，问题会自动升级，每天定时 Push，直到解决为止。</p>\n<p>我见到一些很年轻的同事，他们在推动 OKR、要资源的事上，很有一套，只要能达到自己的目标，不会考虑别人的感受，最终，他们还真能把事办成。</p>\n<p>当然了，段位越高的人，越能用自己的人格魅力、影响力、资源等，去影响和推动事情的进程，而不是靠对他人的 Push。只是在拿结果的事上，不要把自己太当回事，把别人太当回事，大家在一起，也只是为了完成各自的任务，忘掉职级，该怼时还得怼。</p>\n<h2>03 用好平台资源</h2>\n<p>没有人能在一家公司待一辈子，公司再牛，跟自己关系不大，重要的是，在有限的时间内，最大化用好平台资源。</p>\n<p>在美团除了认识自己节点的同事外，有幸认识一群特别棒的协作方，还有其他 BU 的同学。</p>\n<p>这些优秀的人身上，有很多共同的特质：谦虚、利他、乐于分享、双赢思维。</p>\n<p>有两位做运营的同学。</p>\n<p>一位是无意中关注他公众号结识上的。他公众号记录了很多职场成长、家庭建造上的思考和收获，还有定期个人复盘。他和太太都是大厂中层管理者，从文章中看到的不是他多厉害，而是非常接地气的故事。我们约饭了两次，有很多共同话题，现在还时不时有一些互动。</p>\n<p>一位职级更高的同学，他在内网发起了一个&quot;请我喝一杯咖啡，和我一起聊聊个人困惑&quot;的活动，我报名参与了一期。和他聊天的过程，特别像是一场教练对话（最近学习教练课程时才感受到的），帮我排除干扰、聚焦目标的同时，也从他分享个人成长蜕变的过程，收获很多动力。（刚好自己最近也学习了教练技术，后面也准备采用类似的方式，去帮助曾经像我一样迷茫的人）</p>\n<p>还有一些协作方同学。他们工作做得超级到位，能感受到，他们在乎他人时间；稍微有点出彩的事儿，不忘记拉上更多人。利他和双赢思维，在他们身上是最好的阐释。</p>\n<p>除了结识优秀的人，向他们学习外，还可以关注各个通道/工种的课程资源。</p>\n<p>在大厂，多数人的角色都是螺丝钉，但千万不要局限于做一颗螺丝钉。多去学习一些通识课，了解商业交付的各个环节，看清商业世界，明白自己的定位，超越自己的定位。</p>\n<h2>04 一切都是争取来的</h2>\n<p>工作很多年了，很晚才明白这个道理。</p>\n<p>之前一直认为，只要做好自己该做的，一定会被看见，被赏识，也会得到更多机会。但很多时候，这只是个人的一厢情愿。除了自己，不会有人关心你的权益。</p>\n<p>社会主义初级阶段，我国国内的主要矛盾是人民日益增长的物质文化需要同落后的社会生产之间的矛盾。无论在哪里，资源都是稀缺的，自己在乎的，就得去争取。</p>\n<p>想成长某个技能、想参与哪个模块、想做哪个项目，升职加薪……自己不提，不去争取，不会有人主动给你。</p>\n<p>争不争取是一回事，能不能得到是一回事，只有争取，才有可能得到。争取了，即便没有得到，最终也没失去什么。</p>\n<h2>05 关注商业</h2>\n<p>大公司，极度关注效率，大部分岗位，拆解的粒度越细，效率会越高，这些对组织是有利的。但对个人来说，则很容易螺丝钉化。</p>\n<p>做技术的同学，更是这样。</p>\n<p>做前端的同学，不会关注数据是如何落库的；做后端的同学，不会思考页面是否存在兼容性问题；做业务开发的，不用考虑微服务诸多中间件是如何搭建起来的……</p>\n<p>大部分人都想着怎么把自己这摊子事搞好，不会去思考上下游同学在做些什么，更少有人真正关注商业，关心公司的盈利模式，关心每一次产品迭代到底带来哪些业务价值。</p>\n<p>把手头的事做好是应该的，但绝不能停留在此。所有的产品，只有在商业社会产生交付，让客户真正获益，才是有价值的。</p>\n<p>关注商业，能帮我们升维到老板思维，明白投入产出比，抓大放小；也帮助我们，在碰到不好的业务时，及时止损；更重要的是，它帮助我们真正看清趋势，提前做好准备。</p>\n<p>《五分钟商学院》系列，是很好的商业入门级书籍。尽管作者刘润最近存在争议，但不可否认，他比我们大多数人段位还是高很多，他的书值得一读。</p>\n<h2>06 培养数据思维</h2>\n<p>当今数字化时代，数据思维显得尤为重要。数据不仅可以帮助我们更好地了解世界，还可以指导我们的决策和行动。</p>\n<p>非常幸运的是，在阿里和美团的两份经历，都是做商业化广告业务，在离钱💰最近的地方，也培养了数据的敏感性。见过商业数据指标的定义、加工、生产和应用全流程，也在不断熏陶下，能看懂大部分指标背后的价值。</p>\n<p>除了直接面向业务的数据，还有研发协作全流程产生的数据。数据被记录和汇总统计后，能直观地看到每个环节的效率和质量。螺丝钉们的工作，也彻彻底底被数字量化，除了积极面对虚拟化、线上化、数字化外，我们别无他法。</p>\n<p>受工作数据化的影响，生活中，我也渐渐变成了一个数据记录狂，日常运动（骑行、跑步、健走等）必须通过智能手表记录下来，没带 Apple Watch，感觉这次白运动了。每天也在很努力地完成三个圆环。</p>\n<p>数据时代，我们沦为了透明人。也得益于数据被记录和分析，我们做任何事，都能快速得到反馈，这也是自我提升的一个重要环节。</p>\n<h2>07 做一个好&quot;销售&quot;</h2>\n<p>就某种程度来说，所有的工作，本质都是销售。</p>\n<p>这是很多大咖的观点，我也是很晚才明白这个道理。</p>\n<p>我们去一家公司应聘，本质上是在讲一个「我很牛」的故事，销售的是自己；日常工作汇报、季度/年度述职、晋升答辩，是在销售自己；在任何一个场合曝光，也是在销售自己。</p>\n<p>如果我们所服务的组织，对外提供的是一件产品或一项服务，所有上下游协作的同学，唯一在做的事就是，齐心协力把产品/服务卖出去， 我们本质做的还是销售。</p>\n<p>所以， 千万不要看不起任何销售，也不要认为认为销售是一件很丢面子的事。</p>\n<p>真正的大佬，随时随地都在销售。</p>\n<h2>08 少加班多运动</h2>\n<p>在职场，大家都认同一个观点，工作是做不完的。</p>\n<p>我们要做的是，用好时间管理四象限法，识别重要程度和优先级，有限时间，聚焦在固定几件事上。</p>\n<p>这要求我们不断提高自己的问题识别能力、拆解能力，还有专注力。</p>\n<p>我们会因为部分项目的需要而加班，但不会长期加班。</p>\n<p>加班时间短一点，就能腾出更多时间运动。</p>\n<p>最近一次线下培训课，认识一位老师 Hubert，Hubert 是一位超级有魅力的中年大叔（可以通过「有意思教练」的课程链接到他），从外企高管的位置离开后，和太太一起创办了一家培训机构。作为公司高层，日常工作非常忙，头发也有些花白了，但一身腱子肉胜过很多健身教练，给人的状态也是很年轻。聊天得知，Hubert 经常 5 点多起来泡健身房~</p>\n<p>我身边还有一些同事，跟我年龄差不多，因为长期加班，发福严重，比实际年龄看起来苍老 10+岁；</p>\n<p>还有同事曾经加班进 ICU，幸好后面身体慢慢恢复过来。</p>\n<p>某某厂员工长期加班猝死的例子，更是屡见不鲜。</p>\n<p>减少加班，增加运动，绝对是一件性价比极高的事。</p>\n<h2>09 有随时可以离开的底气</h2>\n<p>当今职场，跟父辈时候完全不一样，职业的多样性和变化性越来越快，很少有人能够在同一份工作或同一个公司待一辈子。除了某些特定的岗位，如公务员、事业单位等，大多数人都会在职业生涯中经历多次的职业变化和调整。</p>\n<p>在商业组织里，个体是弱势群体，但不要做弱者。每一段职场，每一项工作，都是上天给我们的修炼。</p>\n<p>我很喜欢&quot;借假修真&quot;这个词。我们参与的大大小小的项目， 重要吗？对公司来说可能重要，对个人来说，则未必。我们去做，一方面是迫于生计；</p>\n<p>另外一方面，参与每个项目的感悟、心得、体会，是真实存在的，很多的能力，都是在这个过程得到提升。</p>\n<p>明白这一点，就不会被职场所困，会刻意在各样事上提升自己，积累的越多，对事务的本质理解的越深、越广，也越发相信很多底层知识是通用的，内心越平静，也会建立起随时都可以离开的底气。</p>\n<h2>10 只是一份工作</h2>\n<p>工作中，我们时常会遇到各种挑战和困难，如发展瓶颈、难以处理的人和事，甚至职场 PUA 等。这些经历可能会让我们感到疲惫、沮丧，甚至怀疑自己的能力和价值。然而，重要的是要明白，困难只是成长道路上的暂时阻碍，而不是我们的定义。</p>\n<p>写总结和复盘是很好的方式，可以帮我们理清思路，找到问题的根源，并学习如何应对类似的情况。但也要注意不要陷入自我怀疑和内耗的陷阱。遇到困难时，应该学会相信自己，积极寻找解决问题的方法，而不是过分纠结于自己的不足和错误。</p>\n<p>内网常有同学匿名分享工作压力过大，常常失眠甚至中度抑郁，每次看到这些话题，非常难过。大环境不好，是不争的事实，但并不代表个体就没有出路。</p>\n<p>我们容易预设困难，容易加很多&quot;可是&quot;，当窗户布满灰尘时，不要试图努力把窗户擦干净，走出去吧，你将看到一片蔚蓝的天空。</p>\n<h2>最后</h2>\n<p>写到最后，特别感恩美团三年多的经历。感谢我的 Leader 们，感谢曾经并肩作战过的小伙伴，感谢遇到的每一位和我一样在平凡的岗位，努力想带给身边一片微光的同学。所有的相遇，都是缘分。</p>\n",
      "date_published": "2023-12-17T07:37:37.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [
        {
          "name": "CityDreamer部落"
        }
      ],
      "tags": [
        "技术文章精选集"
      ]
    },
    {
      "title": "程序员如何快速学习新技术",
      "url": "https://javaguide.cn/high-quality-technical-articles/advanced-programmer/programmer-quickly-learn-new-technology.html",
      "id": "https://javaguide.cn/high-quality-technical-articles/advanced-programmer/programmer-quickly-learn-new-technology.html",
      "summary": "程序员如何快速学习新技术：围绕技术知识与面试总结梳理关键概念、常见问题与实践要点，帮助你高效学习与备战面试。",
      "content_html": "<blockquote>\n<p><strong>推荐语</strong>：这是<a href=\"https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\" rel=\"noopener noreferrer\">《Java 面试指北》</a>练级攻略篇中的一篇文章，分享了我对于如何快速学习一门新技术的看法。</p>\n<p><img src=\"https://oss.javaguide.cn/javamianshizhibei/training-strategy-articles.png\" alt=\"《Java 面试指北》练级攻略篇\"></p>\n</blockquote>\n<p>很多时候，我们因为工作原因需要快速学习某项技术，进而在项目中应用。或者说，我们想要去面试的公司要求的某项技术我们之前没有接触过，为了应对面试需要，我们需要快速掌握这项技术。</p>\n<p>作为一个人纯自学出生的程序员，这篇文章简单聊聊自己对于如何快速学习某项技术的看法。</p>\n<p>学习任何一门技术的时候，一定要先搞清楚这个技术是为了解决什么问题的。深入学习这个技术的之前，一定先从全局的角度来了解这个技术，思考一下它是由哪些模块构成的，提供了哪些功能，和同类的技术想必它有什么优势。</p>\n<p>比如说我们在学习 Spring 的时候，通过 Spring 官方文档你就可以知道 Spring 最新的技术动态，Spring 包含哪些模块 以及 Spring 可以帮你解决什么问题。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/web-real-time-message-push/20210506110341207.png\" alt></p>\n<p>再比如说我在学习消息队列的时候，我会先去了解这个消息队列一般在系统中有什么作用，帮助我们解决了什么问题。消息队列的种类很多，具体学习研究某个消息队列的时候，我会将其和自己已经学习过的消息队列作比较。像我自己在学习 RocketMQ 的时候，就会先将其和自己曾经学习过的第 1 个消息队列 ActiveMQ 进行比较，思考 RocketMQ 相对于 ActiveMQ 有了哪些提升，解决了 ActiveMQ 的哪些痛点，两者有哪些相似的地方，又有哪些不同的地方。</p>\n<p><strong>学习一个技术最有效最快的办法就是将这个技术和自己之前学到的技术建立连接，形成一个网络。</strong></p>\n<p>然后，我建议你先去看看官方文档的教程，运行一下相关的 Demo ，做一些小项目。</p>\n<p>不过，官方文档通常是英文的，通常只有国产项目以及少部分国外的项目提供了中文文档。并且，官方文档介绍的往往也比较粗糙，不太适合初学者作为学习资料。</p>\n<p>如果你看不太懂官网的文档，你也可以搜索相关的关键词找一些高质量的博客或者视频来看。 <strong>一定不要一上来就想着要搞懂这个技术的原理。</strong></p>\n<p>就比如说我们在学习 Spring 框架的时候，我建议你在搞懂 Spring 框架所解决的问题之后，不是直接去开始研究 Spring 框架的原理或者源码，而是先实际去体验一下 Spring 框架提供的核心功能 IoC（Inverse of Control:控制反转） 和 AOP(Aspect-Oriented Programming:面向切面编程)，使用 Spring 框架写一些 Demo，甚至是使用 Spring 框架做一些小项目。</p>\n<p>一言以蔽之， <strong>在研究这个技术的原理之前，先要搞懂这个技术是怎么使用的。</strong></p>\n<p>这样的循序渐进的学习过程，可以逐渐帮你建立学习的快感，获得即时的成就感，避免直接研究原理性的知识而被劝退。</p>\n<p><strong>研究某个技术原理的时候，为了避免内容过于抽象，我们同样可以动手实践。</strong></p>\n<p>比如说我们学习 Tomcat 原理的时候，我们发现 Tomcat 的自定义线程池挺有意思，那我们自己也可以手写一个定制版的线程池。再比如我们学习 Dubbo 原理的时候，可以自己动手造一个简易版的 RPC 框架。</p>\n<p>另外，学习项目中需要用到的技术和面试中需要用到的技术其实还是有一些差别的。</p>\n<p>如果你学习某一项技术是为了在实际项目中使用的话，那你的侧重点就是学习这项技术的使用以及最佳实践，了解这项技术在使用过程中可能会遇到的问题。你的最终目标就是这项技术为项目带来了实际的效果，并且，这个效果是正面的。</p>\n<p>如果你学习某一项技术仅仅是为了面试的话，那你的侧重点就应该放在这项技术在面试中最常见的一些问题上，也就是我们常说的八股文。</p>\n<p>很多人一提到八股文，就是一脸不屑。在我看来，如果你不是死记硬背八股文，而是去所思考这些面试题的本质。那你在准备八股文的过程中，同样也能让你加深对这项技术的了解。</p>\n<p>最后，最重要同时也是最难的还是 <strong>知行合一！知行合一！知行合一！</strong> 不论是编程还是其他领域，最重要不是你知道的有多少，而是要尽量做到知行合一。</p>\n",
      "image": "https://oss.javaguide.cn/javamianshizhibei/training-strategy-articles.png",
      "date_published": "2023-11-09T08:24:19.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [],
      "tags": [
        "技术文章精选集"
      ]
    },
    {
      "title": "经典算法思想总结（含LeetCode题目推荐）",
      "url": "https://javaguide.cn/cs-basics/algorithms/classical-algorithm-problems-recommendations.html",
      "id": "https://javaguide.cn/cs-basics/algorithms/classical-algorithm-problems-recommendations.html",
      "summary": "总结常见算法思想与解题模板，配合典型题目推荐，强调思维路径与复杂度权衡，快速构建解题体系。",
      "content_html": "<h2>贪心算法</h2>\n<h3>算法思想</h3>\n<p>贪心的本质是选择每一阶段的局部最优，从而达到全局最优。</p>\n<h3>一般解题步骤</h3>\n<ul>\n<li>将问题分解为若干个子问题</li>\n<li>找出适合的贪心策略</li>\n<li>求解每一个子问题的最优解</li>\n<li>将局部最优解堆叠成全局最优解</li>\n</ul>\n<h3>LeetCode</h3>\n<p>455.分发饼干：<a href=\"https://leetcode.cn/problems/assign-cookies/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/assign-cookies/</a></p>\n<p>121.买卖股票的最佳时机：<a href=\"https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/</a></p>\n<p>122.买卖股票的最佳时机 II：<a href=\"https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/</a></p>\n<p>55.跳跃游戏：<a href=\"https://leetcode.cn/problems/jump-game/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/jump-game/</a></p>\n<p>45.跳跃游戏 II：<a href=\"https://leetcode.cn/problems/jump-game-ii/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/jump-game-ii/</a></p>\n<h2>动态规划</h2>\n<h3>算法思想</h3>\n<p>动态规划中每一个状态一定是由上一个状态推导出来的，这一点就区分于贪心，贪心没有状态推导，而是从局部直接选最优的。</p>\n<p>经典题目：01 背包、完全背包</p>\n<h3>一般解题步骤</h3>\n<ul>\n<li>确定 dp 数组（dp table）以及下标的含义</li>\n<li>确定递推公式</li>\n<li>dp 数组如何初始化</li>\n<li>确定遍历顺序</li>\n<li>举例推导 dp 数组</li>\n</ul>\n<h3>LeetCode</h3>\n<p>509.斐波那契数：<a href=\"https://leetcode.cn/problems/fibonacci-number/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/fibonacci-number/</a></p>\n<p>746.使用最小花费爬楼梯：<a href=\"https://leetcode.cn/problems/min-cost-climbing-stairs/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/min-cost-climbing-stairs/</a></p>\n<p>416.分割等和子集：<a href=\"https://leetcode.cn/problems/partition-equal-subset-sum/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/partition-equal-subset-sum/</a></p>\n<p>518.零钱兑换：<a href=\"https://leetcode.cn/problems/coin-change-ii/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/coin-change-ii/</a></p>\n<p>647.回文子串：<a href=\"https://leetcode.cn/problems/palindromic-substrings/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/palindromic-substrings/</a></p>\n<p>516.最长回文子序列：<a href=\"https://leetcode.cn/problems/longest-palindromic-subsequence/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/longest-palindromic-subsequence/</a></p>\n<h2>回溯算法</h2>\n<h3>算法思想</h3>\n<p>回溯算法实际上一个类似枚举的搜索尝试过程，主要是在搜索尝试过程中寻找问题的解，当发现已不满足求解条</p>\n<p>件时，就“回溯”返回，尝试别的路径。其本质就是穷举。</p>\n<p>经典题目：8 皇后</p>\n<h3>一般解题步骤</h3>\n<ul>\n<li>针对所给问题，定义问题的解空间，它至少包含问题的一个（最优）解。</li>\n<li>确定易于搜索的解空间结构,使得能用回溯法方便地搜索整个解空间 。</li>\n<li>以深度优先的方式搜索解空间，并且在搜索过程中用剪枝函数避免无效搜索。</li>\n</ul>\n<h3>leetcode</h3>\n<p>77.组合：<a href=\"https://leetcode.cn/problems/combinations/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/combinations/</a></p>\n<p>39.组合总和：<a href=\"https://leetcode.cn/problems/combination-sum/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/combination-sum/</a></p>\n<p>40.组合总和 II：<a href=\"https://leetcode.cn/problems/combination-sum-ii/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/combination-sum-ii/</a></p>\n<p>78.子集：<a href=\"https://leetcode.cn/problems/subsets/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/subsets/</a></p>\n<p>90.子集 II：<a href=\"https://leetcode.cn/problems/subsets-ii/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/subsets-ii/</a></p>\n<p>51.N 皇后：<a href=\"https://leetcode.cn/problems/n-queens/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/n-queens/</a></p>\n<h2>分治算法</h2>\n<h3>算法思想</h3>\n<p>将一个规模为 N 的问题分解为 K 个规模较小的子问题，这些子问题相互独立且与原问题性质相同。求出子问题的解，就可得到原问题的解。</p>\n<p>经典题目：二分查找、汉诺塔问题</p>\n<h3>一般解题步骤</h3>\n<ul>\n<li>将原问题分解为若干个规模较小，相互独立，与原问题形式相同的子问题；</li>\n<li>若子问题规模较小而容易被解决则直接解，否则递归地解各个子问题</li>\n<li>将各个子问题的解合并为原问题的解。</li>\n</ul>\n<h3>LeetCode</h3>\n<p>108.将有序数组转换成二叉搜索数：<a href=\"https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/</a></p>\n<p>148.排序列表：<a href=\"https://leetcode.cn/problems/sort-list/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/sort-list/</a></p>\n<p>23.合并 k 个升序链表：<a href=\"https://leetcode.cn/problems/merge-k-sorted-lists/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/merge-k-sorted-lists/</a></p>\n",
      "date_published": "2023-10-28T10:18:37.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "计算机基础"
      ]
    },
    {
      "title": "常见数据结构经典LeetCode题目推荐",
      "url": "https://javaguide.cn/cs-basics/algorithms/common-data-structures-leetcode-recommendations.html",
      "id": "https://javaguide.cn/cs-basics/algorithms/common-data-structures-leetcode-recommendations.html",
      "summary": "按数据结构类别整理经典 LeetCode 题目清单，聚焦高频与核心考点，助力系统化刷题与巩固。",
      "content_html": "<h2>数组</h2>\n<p>704.二分查找：<a href=\"https://leetcode.cn/problems/binary-search/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/binary-search/</a></p>\n<p>80.删除有序数组中的重复项 II：<a href=\"https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii</a></p>\n<p>977.有序数组的平方：<a href=\"https://leetcode.cn/problems/squares-of-a-sorted-array/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/squares-of-a-sorted-array/</a></p>\n<h2>链表</h2>\n<p>707.设计链表：<a href=\"https://leetcode.cn/problems/design-linked-list/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/design-linked-list/</a></p>\n<p>206.反转链表：<a href=\"https://leetcode.cn/problems/reverse-linked-list/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/reverse-linked-list/</a></p>\n<p>92.反转链表 II：<a href=\"https://leetcode.cn/problems/reverse-linked-list-ii/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/reverse-linked-list-ii/</a></p>\n<p>61.旋转链表：<a href=\"https://leetcode.cn/problems/rotate-list/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/rotate-list/</a></p>\n<h2>栈与队列</h2>\n<p>232.用栈实现队列：<a href=\"https://leetcode.cn/problems/implement-queue-using-stacks/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/implement-queue-using-stacks/</a></p>\n<p>225.用队列实现栈：<a href=\"https://leetcode.cn/problems/implement-stack-using-queues/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/implement-stack-using-queues/</a></p>\n<p>347.前 K 个高频元素：<a href=\"https://leetcode.cn/problems/top-k-frequent-elements/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/top-k-frequent-elements/</a></p>\n<p>239.滑动窗口最大值：<a href=\"https://leetcode.cn/problems/sliding-window-maximum/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/sliding-window-maximum/</a></p>\n<h2>二叉树</h2>\n<p>105.从前序与中序遍历构造二叉树：<a href=\"https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/</a></p>\n<p>117.填充每个节点的下一个右侧节点指针 II：<a href=\"https://leetcode.cn/problems/populating-next-right-pointers-in-each-node-ii\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/populating-next-right-pointers-in-each-node-ii</a></p>\n<p>236.二叉树的最近公共祖先：<a href=\"https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/</a></p>\n<p>129.求根节点到叶节点数字之和：<a href=\"https://leetcode.cn/problems/sum-root-to-leaf-numbers/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/sum-root-to-leaf-numbers/</a></p>\n<p>102.二叉树的层序遍历：<a href=\"https://leetcode.cn/problems/binary-tree-level-order-traversal/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/binary-tree-level-order-traversal/</a></p>\n<p>530.二叉搜索树的最小绝对差：<a href=\"https://leetcode.cn/problems/minimum-absolute-difference-in-bst/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/minimum-absolute-difference-in-bst/</a></p>\n<h2>图</h2>\n<p>200.岛屿数量：<a href=\"https://leetcode.cn/problems/number-of-islands/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/number-of-islands/</a></p>\n<p>207.课程表：<a href=\"https://leetcode.cn/problems/course-schedule/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/course-schedule/</a></p>\n<p>210.课程表 II：<a href=\"https://leetcode.cn/problems/course-schedule-ii/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/course-schedule-ii/</a></p>\n<h2>堆</h2>\n<p>215.数组中的第 K 个最大元素:<a href=\"https://leetcode.cn/problems/kth-largest-element-in-an-array/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/kth-largest-element-in-an-array/</a></p>\n<p>216.数据流的中位数:<a href=\"https://leetcode.cn/problems/find-median-from-data-stream/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/find-median-from-data-stream/</a></p>\n<p>217.前 K 个高频元素：<a href=\"https://leetcode.cn/problems/top-k-frequent-elements/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/top-k-frequent-elements/</a></p>\n",
      "date_published": "2023-10-28T10:18:37.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "计算机基础"
      ]
    },
    {
      "title": "虚拟线程常见问题总结",
      "url": "https://javaguide.cn/java/concurrent/virtual-thread.html",
      "id": "https://javaguide.cn/java/concurrent/virtual-thread.html",
      "summary": "Java 21虚拟线程详解：全面解析Virtual Threads虚拟线程原理、与平台线程区别、Project Loom项目、适用IO密集型场景、使用注意事项与最佳实践。",
      "content_html": "<blockquote>\n<p>本文部分内容来自 <a href=\"https://github.com/Lorin-github\" target=\"_blank\" rel=\"noopener noreferrer\">Lorin</a> 的<a href=\"https://github.com/Snailclimb/JavaGuide/pull/2190\" target=\"_blank\" rel=\"noopener noreferrer\">PR</a>。</p>\n</blockquote>\n<p>虚拟线程在 Java 21 正式发布，这是一项重量级的更新。</p>\n<h2>什么是虚拟线程？</h2>\n<p>虚拟线程（Virtual Thread）是 JDK 而不是 OS 实现的轻量级线程(Lightweight Process，LWP），由 JVM 调度。许多虚拟线程共享同一个操作系统线程，虚拟线程的数量可以远大于操作系统线程的数量。</p>\n<h2>虚拟线程和平台线程有什么关系？</h2>\n<p>在引入虚拟线程之前，<code>java.lang.Thread</code> 包已经支持所谓的平台线程（Platform Thread），也就是没有虚拟线程之前，我们一直使用的线程。JVM 调度程序通过平台线程（载体线程）来管理虚拟线程，一个平台线程可以在不同的时间执行不同的虚拟线程（多个虚拟线程挂载在一个平台线程上），当虚拟线程被阻塞或等待时，平台线程可以切换到执行另一个虚拟线程。</p>\n<p>虚拟线程、平台线程和系统内核线程的关系图如下所示（图源：<a href=\"https://medium.com/javarevisited/how-to-use-java-19-virtual-threads-c16a32bad5f7\" target=\"_blank\" rel=\"noopener noreferrer\">How to Use Java 19 Virtual Threads</a>）：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/virtual-threads-platform-threads-kernel-threads-relationship.png\" alt=\"虚拟线程、平台线程和系统内核线程的关系\"></p>\n<p>关于平台线程和系统内核线程的对应关系多提一点：在 Windows 和 Linux 等主流操作系统中，Java 线程采用的是一对一的线程模型，也就是一个平台线程对应一个系统内核线程。Solaris 系统是一个特例，HotSpot VM 在 Solaris 上支持多对多和一对一。具体可以参考 R 大的回答: <a href=\"https://www.zhihu.com/question/23096638/answer/29617153\" target=\"_blank\" rel=\"noopener noreferrer\">JVM 中的线程模型是用户级的么？</a>。</p>\n<h2>虚拟线程有什么优点和缺点？</h2>\n<h3>优点</h3>\n<ul>\n<li><strong>非常轻量级</strong>：可以在单个线程中创建成百上千个虚拟线程而不会导致过多的线程创建和上下文切换。</li>\n<li><strong>简化异步编程</strong>： 虚拟线程可以简化异步编程，使代码更易于理解和维护。它可以将异步代码编写得更像同步代码，避免了回调地狱（Callback Hell）。</li>\n<li><strong>减少资源开销</strong>： 由于虚拟线程是由 JVM 实现的，它能够更高效地利用底层资源，例如 CPU 和内存。虚拟线程的上下文切换比平台线程更轻量，因此能够更好地支持高并发场景。</li>\n</ul>\n<h3>缺点</h3>\n<ul>\n<li><strong>不适用于计算密集型任务</strong>： 虚拟线程适用于 I/O 密集型任务，但不适用于计算密集型任务，因为密集型计算始终需要 CPU 资源作为支持。</li>\n<li><strong>与某些第三方库不兼容</strong>： 虽然虚拟线程设计时考虑了与现有代码的兼容性，但某些依赖平台线程特性的第三方库可能不完全兼容虚拟线程。</li>\n</ul>\n<h2>如何创建虚拟线程？</h2>\n<p>官方提供了以下四种方式创建虚拟线程：</p>\n<ol>\n<li>使用 <code>Thread.startVirtualThread()</code> 创建</li>\n<li>使用 <code>Thread.ofVirtual()</code> 创建</li>\n<li>使用 <code>ThreadFactory</code> 创建</li>\n<li>使用 <code>Executors.newVirtualThreadPerTaskExecutor()</code>创建</li>\n</ol>\n<p><strong>1、使用 <code>Thread.startVirtualThread()</code> 创建</strong></p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> VirtualThreadTest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    CustomThread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> customThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> CustomThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    Thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">startVirtualThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(customThread);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CustomThread</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Runnable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"CustomThread run\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>2、使用 <code>Thread.ofVirtual()</code> 创建</strong></p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> VirtualThreadTest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    CustomThread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> customThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> CustomThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 创建不启动</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> unStarted</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">ofVirtual</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unstarted</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(customThread);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    unStarted</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 创建直接启动</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    Thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">ofVirtual</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(customThread);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CustomThread</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Runnable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"CustomThread run\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>3、使用 <code>ThreadFactory</code> 创建</strong></p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> VirtualThreadTest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    CustomThread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> customThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> CustomThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    ThreadFactory</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> factory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">ofVirtual</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">factory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> factory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(customThread);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CustomThread</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Runnable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"CustomThread run\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>4、使用<code>Executors.newVirtualThreadPerTaskExecutor()</code>创建</strong></p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> VirtualThreadTest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    CustomThread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> customThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> CustomThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    ExecutorService</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Executors</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newVirtualThreadPerTaskExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">submit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(customThread);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CustomThread</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Runnable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"CustomThread run\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>虚拟线程和平台线程性能对比</h2>\n<p>通过多线程和虚拟线程的方式处理相同的任务，对比创建的系统线程数和处理耗时。</p>\n<p><strong>说明</strong>：统计创建的系统线程中部分为后台线程（比如 GC 线程），两种场景下都一样，所以并不影响对比。</p>\n<p><strong>测试代码</strong>：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> VirtualThreadTest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> List</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> list </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ArrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 开启线程 统计平台线程数</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        ScheduledExecutorService</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> scheduledExecutorService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Executors</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newScheduledThreadPool</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        scheduledExecutorService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">scheduleAtFixedRate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(() </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            ThreadMXBean</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> threadBean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ManagementFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getThreadMXBean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            ThreadInfo</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">threadInfo</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> threadBean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">dumpAllThreads</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">            updateMaxThreadNum</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">threadInfo</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">MILLISECONDS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        long</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 虚拟线程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        ExecutorService</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">  Executors</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newVirtualThreadPerTaskExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 使用平台线程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // ExecutorService executor =  Executors.newFixedThreadPool(200);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 10000</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i++) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">submit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(() </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                    // 线程睡眠 0.5 s，模拟业务处理</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                    TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">MILLISECONDS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">sleep</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">500</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">InterruptedException</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ignored</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            });</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        executor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">close</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"max：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \" platform thread/os thread\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">printf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"totalMillis：%dms</span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\n</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> start);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 更新创建的平台最大线程数</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> updateMaxThreadNum</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> num</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isEmpty</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(num);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (num </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> integer) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, num);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>请求数 10000 单请求耗时 1s</strong>：</p>\n<div class=\"language-plain line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"plain\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-plain\"><span class=\"line\"><span>// Virtual Thread</span></span>\n<span class=\"line\"><span>max：22 platform thread/os thread</span></span>\n<span class=\"line\"><span>totalMillis：1806ms</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>// Platform Thread  线程数200</span></span>\n<span class=\"line\"><span>max：209 platform thread/os thread</span></span>\n<span class=\"line\"><span>totalMillis：50578ms</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>// Platform Thread  线程数500</span></span>\n<span class=\"line\"><span>max：509 platform thread/os thread</span></span>\n<span class=\"line\"><span>totalMillis：20254ms</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>// Platform Thread  线程数1000</span></span>\n<span class=\"line\"><span>max：1009 platform thread/os thread</span></span>\n<span class=\"line\"><span>totalMillis：10214ms</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>// Platform Thread  线程数2000</span></span>\n<span class=\"line\"><span>max：2009 platform thread/os thread</span></span>\n<span class=\"line\"><span>totalMillis：5358ms</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>请求数 10000 单请求耗时 0.5s</strong>：</p>\n<div class=\"language-plain line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"plain\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-plain\"><span class=\"line\"><span>// Virtual Thread</span></span>\n<span class=\"line\"><span>max：22 platform thread/os thread</span></span>\n<span class=\"line\"><span>totalMillis：1316ms</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>// Platform Thread  线程数200</span></span>\n<span class=\"line\"><span>max：209 platform thread/os thread</span></span>\n<span class=\"line\"><span>totalMillis：25619ms</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>// Platform Thread  线程数500</span></span>\n<span class=\"line\"><span>max：509 platform thread/os thread</span></span>\n<span class=\"line\"><span>totalMillis：10277ms</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>// Platform Thread  线程数1000</span></span>\n<span class=\"line\"><span>max：1009 platform thread/os thread</span></span>\n<span class=\"line\"><span>totalMillis：5197ms</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>// Platform Thread  线程数2000</span></span>\n<span class=\"line\"><span>max：2009 platform thread/os thread</span></span>\n<span class=\"line\"><span>totalMillis：2865ms</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><ul>\n<li>可以看到在密集 IO 的场景下，需要创建大量的平台线程异步处理才能达到虚拟线程的处理速度。</li>\n<li>因此，在密集 IO 的场景，虚拟线程可以大幅提高线程的执行效率，减少线程资源的创建以及上下文切换。</li>\n</ul>\n<p><strong>注意</strong>：有段时间 JDK 一直致力于 Reactor 响应式编程来提高 Java 性能，但响应式编程难以理解、调试、使用，最终又回到了同步编程，最终虚拟线程诞生。</p>\n<h2>虚拟线程的底层原理是什么？</h2>\n<p>如果你想要详细了解虚拟线程实现原理，推荐一篇文章：<a href=\"https://www.cnblogs.com/throwable/p/16758997.html\" target=\"_blank\" rel=\"noopener noreferrer\">虚拟线程 - VirtualThread 源码透视</a>。</p>\n<p>面试一般是不会问到这个问题的，仅供学有余力的同学进一步研究学习。</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/new-features/virtual-threads-platform-threads-kernel-threads-relationship.png",
      "date_published": "2023-10-15T12:01:30.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "Java 21 新特性概览(重要)",
      "url": "https://javaguide.cn/java/new-features/java21.html",
      "id": "https://javaguide.cn/java/new-features/java21.html",
      "summary": "概览 JDK 21 的关键新特性与实践影响，重点介绍字符串模板、Sequenced Collections、分代 ZGC、虚拟线程等。",
      "content_html": "<p>JDK 21 于 2023 年 9 月 19 日 发布，这是一个非常重要的版本，里程碑式。</p>\n<p>JDK 21 是 LTS（长期支持版），至此为止，目前有 JDK8、JDK11、JDK17 和 JDK21 这四个长期支持版了。</p>\n<p>JDK 21 共有 15 个新特性，这篇文章会挑选其中较为重要的一些新特性进行详细介绍：</p>\n<ul>\n<li><a href=\"https://openjdk.org/jeps/430\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 430: String Templates（字符串模板）</a>（预览）</li>\n<li><a href=\"https://openjdk.org/jeps/431\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 431: Sequenced Collections（序列化集合）</a></li>\n<li><a href=\"https://openjdk.org/jeps/439\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 439: Generational ZGC（分代 ZGC）</a></li>\n<li><a href=\"https://openjdk.org/jeps/440\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 440: Record Patterns（记录模式）</a></li>\n<li><a href=\"https://openjdk.org/jeps/441\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 441: Pattern Matching for switch（switch 的模式匹配）</a></li>\n<li><a href=\"https://openjdk.org/jeps/442\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 442: Foreign Function &amp; Memory API（外部函数和内存 API）</a>（第三次预览）</li>\n<li><a href=\"https://openjdk.org/jeps/443\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 443: Unnamed Patterns and Variables（未命名模式和变量）</a>（预览）</li>\n<li><a href=\"https://openjdk.org/jeps/444\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 444: Virtual Threads（虚拟线程）</a></li>\n<li><a href=\"https://openjdk.org/jeps/445\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 445: Unnamed Classes and Instance Main Methods（未命名类和实例 main 方法）</a>（预览）</li>\n</ul>\n<p>下图是从 JDK 8 到 JDK 24 每个版本的更新带来的新特性数量和更新时间：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png\" alt></p>\n<h2>JEP 430: String Templates（字符串模板，预览）</h2>\n<p>String Templates(字符串模板) 目前仍然是 JDK 21 中的一个预览功能。</p>\n<p>String Templates 提供了一种更简洁、更直观的方式来动态构建字符串。通过使用占位符<code>${}</code>，我们可以将变量的值直接嵌入到字符串中，而不需要手动处理。在运行时，Java 编译器会将这些占位符替换为实际的变量值。并且，表达式支持局部变量、静态/非静态字段甚至方法、计算结果等特性。</p>\n<p>实际上，String Templates（字符串模板）在大多数编程语言中都存在:</p>\n<div class=\"language-typescript line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"typescript\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-typescript\"><span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Greetings {{ name }}!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;  </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//Angular</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">`Greetings </span><span style=\"--shiki-light:#CA1243;--shiki-dark:#C678DD\">${</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> name</span><span style=\"--shiki-light:#CA1243;--shiki-dark:#C678DD\"> }</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">!`</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;    </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//Typescript</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">$</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Greetings { name }!\"</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //Visual basic</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">f</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Greetings { name }!\"</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //Python</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>Java 在没有 String Templates 之前，我们通常使用字符串拼接或格式化方法来构建字符串：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//concatenation</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">message </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Greetings \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> name </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//String.format()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">message </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Greetings %s!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, name);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //concatenation</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//MessageFormat</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">message </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> MessageFormat</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Greetings {0}!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(name);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//StringBuilder</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">message </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> StringBuilder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">append</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Greetings \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">).</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">append</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(name).</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">append</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">).</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这些方法或多或少都存在一些缺点，比如难以阅读、冗长、复杂。</p>\n<p>Java 使用 String Templates 进行字符串拼接，可以直接在字符串中嵌入表达式，而无需进行额外的处理：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> message </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> STR</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Greetings </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\{</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">name}!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>在上面的模板表达式中：</p>\n<ul>\n<li>STR 是模板处理器。</li>\n<li><code>\\{name}</code>为表达式，运行时，这些表达式将被相应的变量值替换。</li>\n</ul>\n<p>Java 目前支持三种模板处理器：</p>\n<ul>\n<li>STR：自动执行字符串插值，即将模板中的每个嵌入式表达式替换为其值（转换为字符串）。</li>\n<li>FMT：和 STR 类似，但是它还可以接受格式说明符，这些格式说明符出现在嵌入式表达式的左边，用来控制输出的样式。</li>\n<li>RAW：不会像 STR 和 FMT 模板处理器那样自动处理字符串模板，而是返回一个 <code>StringTemplate</code> 对象，这个对象包含了模板中的文本和表达式的信息。</li>\n</ul>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> name </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Lokesh\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//STR</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> message </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> STR</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Greetings </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\{</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">name}.\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//FMT</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> message </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> FMT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Greetings %-12s</span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\{</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">name}.\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//RAW</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">StringTemplate</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> st </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> RAW</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Greetings </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\{</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">name}.\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> message </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> STR</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">process</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(st);</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>除了 JDK 自带的三种模板处理器外，你还可以实现 <code>StringTemplate.Processor</code> 接口来创建自己的模板处理器，只需要继承 <code>StringTemplate.Processor</code>接口，然后实现 <code>process</code> 方法即可。</p>\n<p>我们可以使用局部变量、静态/非静态字段甚至方法作为嵌入表达式：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//variable</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">message </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> STR</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Greetings </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\{</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">name}!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//method</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">message </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> STR</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Greetings </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\{</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">getName()}!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//field</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">message </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> STR</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Greetings </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\{</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">this.name}!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>还可以在表达式中执行计算并打印结果：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> y </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 20</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> s </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> STR</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"</span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\{</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">x} + </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\{</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">y} = </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\{</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">x + y}\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //\"10 + 20 = 30\"</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>为了提高可读性，我们可以将嵌入的表达式分成多行:</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> time </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> STR</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"The current time is </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\{</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">    //sample comment - current time in HH:mm:ss</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">    DateTimeFormatter</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">      .ofPattern(\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">HH</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">mm</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">ss</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\")</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">      .format(LocalTime.now())</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">  }.\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JEP 431: Sequenced Collections（序列化集合）</h2>\n<p>JDK 21 引入了一种新的集合类型：<strong>Sequenced Collections（序列化集合，也叫有序集合）</strong>，这是一种具有确定出现顺序（encounter order）的集合（无论我们遍历这样的集合多少次，元素的出现顺序始终是固定的）。序列化集合提供了处理集合的第一个和最后一个元素以及反向视图（与原始集合相反的顺序）的简单方法。</p>\n<p>Sequenced Collections 包括以下三个接口：</p>\n<ul>\n<li><a href=\"https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/SequencedCollection.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>SequencedCollection</code></a></li>\n<li><a href=\"https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/SequencedSet.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>SequencedSet</code></a></li>\n<li><a href=\"https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/SequencedMap.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>SequencedMap</code></a></li>\n</ul>\n<p><code>SequencedCollection</code> 接口继承了 <code>Collection</code>接口， 提供了在集合两端访问、添加或删除元素以及获取集合的反向视图的方法。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">interface</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> SequencedCollection</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Collection</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // New Method</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  SequencedCollection</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> reversed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // Promoted methods from Deque&#x3C;E></span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> addFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> addLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> removeFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> removeLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>List</code> 和 <code>Deque</code> 接口实现了<code>SequencedCollection</code> 接口。</p>\n<p>这里以 <code>ArrayList</code> 为例，演示一下实际使用效果：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ArrayList</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> arrayList </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ArrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">arrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   // List contains: [1]</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">arrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">addFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // List contains: [0, 1]</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">arrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">addLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   // List contains: [0, 1, 2]</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> firstElement </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> arrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 0</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lastElement </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> arrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 2</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">List</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> reversed </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> arrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">reversed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(reversed);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // Prints [2, 1, 0]</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>SequencedSet</code>接口直接继承了 <code>SequencedCollection</code> 接口并重写了 <code>reversed()</code> 方法。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">interface</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> SequencedSet</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> SequencedCollection</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">>,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Set</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    SequencedSet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> reversed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>SortedSet</code> 和 <code>LinkedHashSet</code> 实现了<code>SequencedSet</code>接口。</p>\n<p>这里以 <code>LinkedHashSet</code> 为例，演示一下实际使用效果：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">LinkedHashSet</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> linkedHashSet </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedHashSet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">List</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">of</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> firstElement </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> linkedHashSet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   // 1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lastElement </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> linkedHashSet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 3</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">linkedHashSet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">addFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //List contains: [0, 1, 2, 3]</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">linkedHashSet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">addLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">4</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   //List contains: [0, 1, 2, 3, 4]</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">linkedHashSet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">reversed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   //Prints [4, 3, 2, 1, 0]</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>SequencedMap</code> 接口继承了 <code>Map</code>接口， 提供了在集合两端访问、添加或删除键值对、获取包含 key 的 <code>SequencedSet</code>、包含 value 的 <code>SequencedCollection</code>、包含 entry（键值对） 的 <code>SequencedSet</code>以及获取集合的反向视图的方法。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">interface</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> SequencedMap</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Map</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // New Methods</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  SequencedMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> reversed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  SequencedSet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> sequencedKeySet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  SequencedCollection</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> sequencedValues</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  SequencedSet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> sequencedEntrySet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  V</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> putFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  V</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> putLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // Promoted Methods from NavigableMap&#x3C;K, V></span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> firstEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> lastEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> pollFirstEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> pollLastEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>SortedMap</code> 和<code>LinkedHashMap</code> 实现了<code>SequencedMap</code> 接口。</p>\n<p>这里以 <code>LinkedHashMap</code> 为例，演示一下实际使用效果：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">LinkedHashMap</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> map </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"One\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Two\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Three\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">firstEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   //1=One</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lastEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //3=Three</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(map);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //{1=One, 2=Two, 3=Three}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Map</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> first </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">pollFirstEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   //1=One</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Map</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> last </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">pollLastEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //3=Three</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(map);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //{2=Two}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">putFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"One\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //{1=One, 2=Two}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">putLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Three\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //{1=One, 2=Two, 3=Three}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(map);</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //{1=One, 2=Two, 3=Three}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">reversed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   //{3=Three, 2=Two, 1=One}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JEP 439: Generational ZGC（分代 ZGC）</h2>\n<p>JDK21 中对 ZGC 进行了功能扩展，增加了分代 GC 功能。不过，默认是关闭的，需要通过配置打开：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">//</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 启用分代ZGC</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">java</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> -XX:+UseZGC</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> -XX:+ZGenerational</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> ...</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>在未来的版本中，官方会把 ZGenerational 设为默认值，即默认打开 ZGC 的分代 GC。在更晚的版本中，非分代 ZGC 就被移除。</p>\n<blockquote>\n<p>In a future release we intend to make Generational ZGC the default, at which point -XX:-ZGenerational will select non-generational ZGC. In an even later release we intend to remove non-generational ZGC, at which point the ZGenerational option will become obsolete.</p>\n<p>在将来的版本中，我们打算将 Generational ZGC 作为默认选项，此时-XX:-ZGenerational 将选择非分代 ZGC。在更晚的版本中，我们打算移除非分代 ZGC，此时 ZGenerational 选项将变得过时。</p>\n</blockquote>\n<p>分代 ZGC 可以显著减少垃圾回收过程中的停顿时间，并提高应用程序的响应性能。这对于大型 Java 应用程序和高并发场景下的性能优化非常有价值。</p>\n<h2>JEP 440: Record Patterns（记录模式）</h2>\n<p>记录模式在 Java 19 进行了第一次预览， 由 <a href=\"https://openjdk.org/jeps/405\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 405</a> 提出。JDK 20 中是第二次预览，由 <a href=\"https://openjdk.org/jeps/432\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 432</a> 提出。最终，记录模式在 JDK21 顺利转正。</p>\n<p><a href=\"/java/new-features/java20.html\" target=\"_blank\">Java 20 新特性概览</a>已经详细介绍过记录模式，这里就不重复了。</p>\n<h2>JEP 441: Pattern Matching for switch（switch 的模式匹配）</h2>\n<p>增强 Java 中的 switch 表达式和语句，允许在 case 标签中使用模式。当模式匹配时，执行 case 标签对应的代码。</p>\n<p>在下面的代码中，switch 表达式使用了类型模式来进行匹配。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> formatterPatternSwitch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> obj) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> switch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (obj) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"int %d\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> l    </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"long %d\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, l);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Double</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> d  </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"double %f\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, d);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> s  </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"String %s\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, s);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        default</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        -></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> obj</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JEP 442: Foreign Function &amp; Memory API（外部函数和内存 API，第三次预览）</h2>\n<p>Java 程序可以通过该 API 与 Java 运行时之外的代码和数据进行互操作。通过高效地调用外部函数（即 JVM 之外的代码）和安全地访问外部内存（即不受 JVM 管理的内存），该 API 使 Java 程序能够调用本机库并处理本机数据，而不会像 JNI 那样危险和脆弱。</p>\n<p>外部函数和内存 API 在 Java 17 中进行了第一轮孵化，由 <a href=\"https://openjdk.java.net/jeps/412\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 412</a> 提出。Java 18 中进行了第二次孵化，由<a href=\"https://openjdk.org/jeps/419\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 419</a> 提出。Java 19 中是第一次预览，由 <a href=\"https://openjdk.org/jeps/424\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 424</a> 提出。JDK 20 中是第二次预览，由 <a href=\"https://openjdk.org/jeps/434\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 434</a> 提出。JDK 21 中是第三次预览，由 <a href=\"https://openjdk.org/jeps/442\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 442</a> 提出。</p>\n<p>在 <a href=\"/java/new-features/java19.html\" target=\"_blank\">Java 19 新特性概览</a> 中，我有详细介绍到外部函数和内存 API，这里就不再做额外的介绍了。</p>\n<h2>JEP 443: Unnamed Patterns and Variables（未命名模式和变量，预览）</h2>\n<p>未命名模式和变量使得我们可以使用下划线 <code>_</code> 表示未命名的变量以及模式匹配时不使用的组件，旨在提高代码的可读性和可维护性。</p>\n<p>未命名变量的典型场景是 <code>try-with-resources</code> 语句、 <code>catch</code> 子句中的异常变量和<code>for</code>循环。当变量不需要使用的时候就可以使用下划线 <code>_</code>代替，这样清晰标识未被使用的变量。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> _ </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ScopedContext</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">acquire</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // No use of acquired resource</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> { </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Exception</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> _</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) { </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Throwable</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> _</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) { </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> _ </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> runOnce</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> arr</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>未命名模式是一个无条件的模式，并不绑定任何值。未命名模式变量出现在类型模式中。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (r </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ColoredPoint</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(_</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Color</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> c)) { </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> c</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ...</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">switch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (b) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    case</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Box</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">RedBall</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> _)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Box</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">BlueBall</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> _) </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> processBox</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(b)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    case</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Box</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">GreenBall</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> _)                </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> stopProcessing</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    case</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Box</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(_)                          </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> pickAnotherBox</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JEP 444: Virtual Threads（虚拟线程）</h2>\n<p>虚拟线程是一项重量级的更新，一定一定要重视！</p>\n<p>虚拟线程在 Java 19 中进行了第一次预览，由<a href=\"https://openjdk.org/jeps/425\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 425</a>提出。JDK 20 中是第二次预览。最终，虚拟线程在 JDK21 顺利转正。</p>\n<p><a href=\"/java/new-features/java20.html\" target=\"_blank\">Java 20 新特性概览</a>已经详细介绍过虚拟线程，这里就不重复了。</p>\n<h2>JEP 445: Unnamed Classes and Instance Main Methods（未命名类和实例 main 方法，预览）</h2>\n<p>这个特性主要简化了 <code>main</code> 方法的声明。对于 Java 初学者来说，这个 <code>main</code> 方法的声明引入了太多的 Java 语法概念，不利于初学者快速上手。</p>\n<p>没有使用该特性之前定义一个 <code>main</code> 方法：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> HelloWorld</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello, World!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>使用该新特性之后定义一个 <code>main</code> 方法：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> HelloWorld</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello, World!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>进一步精简(未命名的类允许我们不定义类名)：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">   System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello, World!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>参考</h2>\n<ul>\n<li>Java 21 String Templates：<a href=\"https://howtodoinjava.com/java/java-string-templates/\" target=\"_blank\" rel=\"noopener noreferrer\">https://howtodoinjava.com/java/java-string-templates/</a></li>\n<li>Java 21 Sequenced Collections：<a href=\"https://howtodoinjava.com/java/sequenced-collections/\" target=\"_blank\" rel=\"noopener noreferrer\">https://howtodoinjava.com/java/sequenced-collections/</a></li>\n</ul>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png",
      "date_published": "2023-10-08T07:27:41.000Z",
      "date_modified": "2026-01-26T14:45:12.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "Maven最佳实践",
      "url": "https://javaguide.cn/tools/maven/maven-best-practices.html",
      "id": "https://javaguide.cn/tools/maven/maven-best-practices.html",
      "summary": "Maven 是一种广泛使用的 Java 项目构建自动化工具。它简化了构建过程并帮助管理依赖关系，使开发人员的工作更轻松。在这篇博文中，我们将讨论一些最佳实践、提示和技巧，以优化我们在项目中对 Maven 的使用并改善我们的开发体验。",
      "content_html": "<blockquote>\n<p>本文由 JavaGuide 翻译并完善，原文地址：<a href=\"https://medium.com/@AlexanderObregon/maven-best-practices-tips-and-tricks-for-java-developers-438eca03f72b\" target=\"_blank\" rel=\"noopener noreferrer\">https://medium.com/@AlexanderObregon/maven-best-practices-tips-and-tricks-for-java-developers-438eca03f72b</a> 。</p>\n</blockquote>\n<p>Maven 是一种广泛使用的 Java 项目构建自动化工具。它简化了构建过程并帮助管理依赖关系，使开发人员的工作更轻松。Maven 详细介绍可以参考我写的这篇 <a href=\"/tools/maven/maven-core-concepts.html\" target=\"_blank\">Maven 核心概念总结</a> 。</p>\n<p>这篇文章不会涉及到 Maven 概念的介绍，主要讨论一些最佳实践、建议和技巧，以优化我们在项目中对 Maven 的使用并改善我们的开发体验。</p>\n<h2>Maven 标准目录结构</h2>\n<p>Maven 遵循标准目录结构来保持项目之间的一致性。遵循这种结构可以让其他开发人员更轻松地理解我们的项目。</p>\n<p>Maven 项目的标准目录结构如下：</p>\n<div class=\"language-groovy line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"groovy\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-groovy\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">src</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    java</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    resources</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  test</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    java</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    resources</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">pom.xml</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><ul>\n<li><code>src/main/java</code>：源代码目录</li>\n<li><code>src/main/resources</code>：资源文件目录</li>\n<li><code>src/test/java</code>：测试代码目录</li>\n<li><code>src/test/resources</code>：测试资源文件目录</li>\n</ul>\n<p>这只是一个最简单的 Maven 项目目录示例。实际项目中，我们还会根据项目规范去做进一步的细分。</p>\n<h2>指定 Maven 编译器插件</h2>\n<p>默认情况下，Maven 使用 Java5 编译我们的项目。要使用不同的 JDK 版本，请在 <code>pom.xml</code> 文件中配置 Maven 编译器插件。</p>\n<p>例如，如果你想要使用 Java8 来编译你的项目，你可以在<code>&lt;build&gt;</code>标签下添加以下的代码片段：</p>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">build</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">plugins</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">plugin</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>org.apache.maven.plugins&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>maven-compiler-plugin&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>3.8.1&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">configuration</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">source</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>1.8&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">source</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">target</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>1.8&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">target</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">configuration</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">plugin</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">plugins</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">build</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这样，Maven 就会使用 Java8 的编译器来编译你的项目。如果你想要使用其他版本的 JDK，你只需要修改<code>&lt;source&gt;</code>和<code>&lt;target&gt;</code>标签的值即可。例如，如果你想要使用 Java11，你可以将它们的值改为 11。</p>\n<h2>有效管理依赖关系</h2>\n<p>Maven 的依赖管理系统是其最强大的功能之一。在顶层 pom 文件中，通过标签 <code>dependencyManagement</code> 定义公共的依赖关系，这有助于避免冲突并确保所有模块使用相同版本的依赖项。</p>\n<p>例如，假设我们有一个父模块和两个子模块 A 和 B，我们想要在所有模块中使用 JUnit 5.7.2 作为测试框架。我们可以在父模块的<code>pom.xml</code>文件中使用<code>&lt;dependencyManagement&gt;</code>标签来定义 JUnit 的版本：</p>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependencyManagement</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependencies</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>org.junit.jupiter&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>junit-jupiter&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>5.7.2&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>test&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependencies</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependencyManagement</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>在子模块 A 和 B 的 <code>pom.xml</code> 文件中，我们只需要引用 JUnit 的 <code>groupId</code> 和 <code>artifactId</code> 即可:</p>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependencies</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>org.junit.jupiter&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>junit-jupiter&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependencies</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>针对不同环境使用配置文件</h2>\n<p>Maven 配置文件允许我们配置不同环境的构建设置，例如开发、测试和生产。在 <code>pom.xml</code> 文件中定义配置文件并使用命令行参数激活它们：</p>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">profiles</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">profile</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">id</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>development&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">id</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">activation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">activeByDefault</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>true&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">activeByDefault</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">activation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">properties</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">environment</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>dev&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">environment</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">properties</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">profile</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">profile</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">id</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>production&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">id</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">properties</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">environment</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>prod&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">environment</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">properties</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">profile</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">profiles</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>使用命令行激活配置文件：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">mvn</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> clean</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> install</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> -P</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> production</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><h2>保持 pom.xml 干净且井然有序</h2>\n<p>组织良好的 <code>pom.xml</code> 文件更易于维护和理解。以下是维护干净的 <code>pom.xml</code> 的一些技巧：</p>\n<ul>\n<li>将相似的依赖项和插件组合在一起。</li>\n<li>使用注释来描述特定依赖项或插件的用途。</li>\n<li>将插件和依赖项的版本号保留在 <code>&lt;properties&gt;</code> 标签内以便于管理。</li>\n</ul>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">properties</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">junit.version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>5.7.0&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">junit.version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">mockito.version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>3.9.0&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">mockito.version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">properties</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>使用 Maven Wrapper</h2>\n<p>Maven Wrapper 是一个用于管理和使用 Maven 的工具，它允许在没有预先安装 Maven 的情况下运行和构建 Maven 项目。</p>\n<p>Maven 官方文档是这样介绍 Maven Wrapper 的：</p>\n<blockquote>\n<p>The Maven Wrapper is an easy way to ensure a user of your Maven build has everything necessary to run your Maven build.</p>\n<p>Maven Wrapper 是一种简单的方法，可以确保 Maven 构建的用户拥有运行 Maven 构建所需的一切。</p>\n</blockquote>\n<p>Maven Wrapper 可以确保构建过程使用正确的 Maven 版本，非常方便。要使用 Maven Wrapper，请在项目目录中运行以下命令：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">mvn</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> wrapper:wrapper</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>此命令会在我们的项目中生成 Maven Wrapper 文件。现在我们可以使用 <code>./mvnw</code> （或 Windows 上的 <code>./mvnw.cmd</code>）而不是 <code>mvn</code> 来执行 Maven 命令。</p>\n<h2>通过持续集成实现构建自动化</h2>\n<p>将 Maven 项目与持续集成 (CI) 系统（例如 Jenkins 或 GitHub Actions）集成，可确保自动构建、测试和部署我们的代码。CI 有助于及早发现问题并在整个团队中提供一致的构建流程。以下是 Maven 项目的简单 GitHub Actions 工作流程示例：</p>\n<div class=\"language-groovy line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"groovy\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-groovy\"><span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">Java</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> CI</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> with </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">Maven</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">on</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: [push]</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">jobs</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">  build</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    runs</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">on</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: ubuntu</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">latest</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">    steps</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">    -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">Checkout</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">      uses</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: actions</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">checkout</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">@v2</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">    -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">Set</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> up </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">JDK</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> 11</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">      uses</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: actions</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">setup</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">java</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">@v2</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">      with</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        java</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'11'</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">        distribution</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'adopt'</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">    -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">Build</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> with </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">Maven</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">      run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: .</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">mvnw clean install</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>利用 Maven 插件获得附加功能</h2>\n<p>有许多 Maven 插件可用于扩展 Maven 的功能。一些流行的插件包括（前三个是 Maven 自带的插件，后三个是第三方提供的插件）：</p>\n<ul>\n<li>maven-surefire-plugin：配置并执行单元测试。</li>\n<li>maven-failsafe-plugin：配置并执行集成测试。</li>\n<li>maven-javadoc-plugin：生成 Javadoc 格式的项目文档。</li>\n<li>maven-checkstyle-plugin：强制执行编码标准和最佳实践。</li>\n<li>jacoco-maven-plugin: 单测覆盖率。</li>\n<li>sonar-maven-plugin：分析代码质量。</li>\n<li>……</li>\n</ul>\n<p>jacoco-maven-plugin 使用示例：</p>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">build</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">plugins</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">plugin</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>org.jacoco&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>jacoco-maven-plugin&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>0.8.8&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">executions</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">execution</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>prepare-agent&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">execution</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">execution</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">id</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>generate-code-coverage-report&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">id</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>test&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>report&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">execution</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">executions</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">plugin</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">plugins</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">build</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>如果这些已有的插件无法满足我们的需求，我们还可以自定义插件。</p>\n<p>探索可用的插件并在 <code>pom.xml</code> 文件中配置它们以增强我们的开发过程。</p>\n<h2>总结</h2>\n<p>Maven 是一个强大的工具，可以简化 Java 项目的构建过程和依赖关系管理。通过遵循这些最佳实践和技巧，我们可以优化 Maven 的使用并改善我们的 Java 开发体验。请记住使用标准目录结构，有效管理依赖关系，利用不同环境的配置文件，并将项目与持续集成系统集成，以确保构建一致。</p>\n",
      "date_published": "2023-09-15T13:14:19.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "开发工具"
      ]
    },
    {
      "title": "IoC & AOP详解（快速搞懂）",
      "url": "https://javaguide.cn/system-design/framework/spring/ioc-and-aop.html",
      "id": "https://javaguide.cn/system-design/framework/spring/ioc-and-aop.html",
      "summary": "Spring IoC与AOP核心原理详解，深入讲解控制反转、依赖注入、切面编程及动态代理的实现机制。",
      "content_html": "<p>这篇文章会从下面从以下几个问题展开对 IoC &amp; AOP 的解释</p>\n<ul>\n<li>什么是 IoC？</li>\n<li>IoC 解决了什么问题？</li>\n<li>IoC 和 DI 的区别？</li>\n<li>什么是 AOP？</li>\n<li>AOP 解决了什么问题？</li>\n<li>AOP 的应用场景有哪些？</li>\n<li>AOP 为什么叫做切面编程？</li>\n<li>AOP 实现方式有哪些？</li>\n</ul>\n<p>首先声明：IoC &amp; AOP 不是 Spring 提出来的，它们在 Spring 之前其实已经存在了，只不过当时更加偏向于理论。Spring 在技术层次将这两个思想进行了很好的实现。</p>\n<h2>IoC （Inversion of control ）</h2>\n<h3>什么是 IoC?</h3>\n<p>IoC （Inversion of Control ）即控制反转/反转控制。它是一种思想不是一个技术实现。描述的是：Java 开发领域对象的创建以及管理的问题。</p>\n<p>例如：现有类 A 依赖于类 B</p>\n<ul>\n<li><strong>传统的开发方式</strong> ：往往是在类 A 中手动通过 new 关键字来 new 一个 B 的对象出来</li>\n<li><strong>使用 IoC 思想的开发方式</strong> ：不通过 new 关键字来创建对象，而是通过 IoC 容器(Spring 框架) 来帮助我们实例化对象。我们需要哪个对象，直接从 IoC 容器里面去取即可。</li>\n</ul>\n<p>从以上两种开发方式的对比来看：我们 “丧失了一个权力” (创建、管理对象的权力)，从而也得到了一个好处（不用再考虑对象的创建、管理等一系列的事情）</p>\n<p><strong>为什么叫控制反转?</strong></p>\n<ul>\n<li><strong>控制</strong> ：指的是对象创建（实例化、管理）的权力</li>\n<li><strong>反转</strong> ：控制权交给外部环境（IoC 容器）</li>\n</ul>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/IoC&amp;Aop-ioc-illustration.png\" alt=\"IoC 图解\"></p>\n<h3>IoC 解决了什么问题?</h3>\n<p>IoC 的思想就是两方之间不互相依赖，由第三方容器来管理相关资源。这样有什么好处呢？</p>\n<ol>\n<li>对象之间的耦合度或者说依赖程度降低；</li>\n<li>资源变的容易管理；比如你用 Spring 容器提供的话很容易就可以实现一个单例。</li>\n</ol>\n<p>例如：现有一个针对 User 的操作，利用 Service 和 Dao 两层结构进行开发</p>\n<p>在没有使用 IoC 思想的情况下，Service 层想要使用 Dao 层的具体实现的话，需要通过 new 关键字在<code>UserServiceImpl</code> 中手动 new 出 <code>IUserDao</code> 的具体实现类 <code>UserDaoImpl</code>（不能直接 new 接口类）。</p>\n<p>很完美，这种方式也是可以实现的，但是我们想象一下如下场景：</p>\n<p>开发过程中突然接到一个新的需求，针对<code>IUserDao</code> 接口开发出另一个具体实现类。因为 Server 层依赖了<code>IUserDao</code>的具体实现，所以我们需要修改<code>UserServiceImpl</code>中 new 的对象。如果只有一个类引用了<code>IUserDao</code>的具体实现，可能觉得还好，修改起来也不是很费力气，但是如果有许许多多的地方都引用了<code>IUserDao</code>的具体实现的话，一旦需要更换<code>IUserDao</code> 的实现方式，那修改起来将会非常的头疼。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/IoC&amp;Aop-ioc-illustration-dao-service.png\" alt=\"IoC&amp;Aop-ioc-illustration-dao-service\"></p>\n<p>使用 IoC 的思想，我们将对象的控制权（创建、管理）交由 IoC 容器去管理，我们在使用的时候直接向 IoC 容器 “要” 就可以了</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/IoC&amp;Aop-ioc-illustration-dao.png\" alt></p>\n<h3>IoC 和 DI 有区别吗？</h3>\n<p>IoC（Inverse of Control:控制反转）是一种设计思想或者说是某种模式。这个设计思想就是 <strong>将原本在程序中手动创建对象的控制权交给第三方比如 IoC 容器。</strong> 对于我们常用的 Spring 框架来说， IoC 容器实际上就是个 Map（key，value）,Map 中存放的是各种对象。不过，IoC 在其他语言中也有应用，并非 Spring 特有。</p>\n<p>IoC 最常见以及最合理的实现方式叫做依赖注入（Dependency Injection，简称 DI）。</p>\n<p>老马（Martin Fowler）在一篇文章中提到将 IoC 改名为 DI，原文如下，原文地址：<a href=\"https://martinfowler.com/articles/injection.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://martinfowler.com/articles/injection.html</a> 。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/martin-fowler-injection.png\" alt></p>\n<p>老马的大概意思是 IoC 太普遍并且不表意，很多人会因此而迷惑，所以，使用 DI 来精确指名这个模式比较好。</p>\n<h2>AOP（Aspect oriented programming）</h2>\n<p>这里不会涉及太多专业的术语，核心目的是将 AOP 的思想说清楚。</p>\n<h3>什么是 AOP？</h3>\n<p>AOP（Aspect Oriented Programming）即面向切面编程，AOP 是 OOP（面向对象编程）的一种延续，二者互补，并不对立。</p>\n<p>AOP 的目的是将横切关注点（如日志记录、事务管理、权限控制、接口限流、接口幂等等）从核心业务逻辑中分离出来，通过动态代理、字节码操作等技术，实现代码的复用和解耦，提高代码的可维护性和可扩展性。OOP 的目的是将业务逻辑按照对象的属性和行为进行封装，通过类、对象、继承、多态等概念，实现代码的模块化和层次化（也能实现代码的复用），提高代码的可读性和可维护性。</p>\n<h3>AOP 为什么叫面向切面编程？</h3>\n<p>AOP 之所以叫面向切面编程，是因为它的核心思想就是将横切关注点从核心业务逻辑中分离出来，形成一个个的<strong>切面（Aspect）</strong>。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/aop-program-execution.jpg\" alt=\"面向切面编程图解\"></p>\n<p>这里顺带总结一下 AOP 关键术语（不理解也没关系，可以继续往下看）：</p>\n<ul>\n<li><strong>横切关注点（cross-cutting concerns）</strong> ：多个类或对象中的公共行为（如日志记录、事务管理、权限控制、接口限流、接口幂等等）。</li>\n<li><strong>切面（Aspect）</strong>：对横切关注点进行封装的类，一个切面是一个类。切面可以定义多个通知，用来实现具体的功能。</li>\n<li><strong>连接点（JoinPoint）</strong>：连接点是方法调用或者方法执行时的某个特定时刻（如方法调用、异常抛出等）。</li>\n<li><strong>通知（Advice）</strong>：通知就是切面在某个连接点要执行的操作。通知有五种类型，分别是前置通知（Before）、后置通知（After）、返回通知（AfterReturning）、异常通知（AfterThrowing）和环绕通知（Around）。前四种通知都是在目标方法的前后执行，而环绕通知可以控制目标方法的执行过程。</li>\n<li><strong>切点（Pointcut）</strong>：一个切点是一个表达式，它用来匹配哪些连接点需要被切面所增强。切点可以通过注解、正则表达式、逻辑运算等方式来定义。比如 <code>execution(* com.xyz.service..*(..))</code>匹配 <code>com.xyz.service</code> 包及其子包下的类或接口。</li>\n<li><strong>织入（Weaving）</strong>：织入是将切面和目标对象连接起来的过程，也就是将通知应用到切点匹配的连接点上。常见的织入时机有两种，分别是编译期织入（Compile-Time Weaving 如：AspectJ）和运行期织入（Runtime Weaving 如：AspectJ、Spring AOP）。</li>\n</ul>\n<h3>AOP 常见的通知类型有哪些？</h3>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/aspectj-advice-types.jpg\" alt></p>\n<ul>\n<li><strong>Before</strong>（前置通知）：目标对象的方法调用之前触发</li>\n<li><strong>After</strong> （后置通知）：目标对象的方法调用之后触发</li>\n<li><strong>AfterReturning</strong>（返回通知）：目标对象的方法调用完成，在返回结果值之后触发</li>\n<li><strong>AfterThrowing</strong>（异常通知）：目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和 AfterThrowing 两者互斥。如果方法调用成功无异常，则会有返回值；如果方法抛出了异常，则不会有返回值。</li>\n<li><strong>Around</strong> （环绕通知）：编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种，因为它可以直接拿到目标对象，以及要执行的方法，所以环绕通知可以任意的在目标对象的方法调用前后搞事，甚至不调用目标对象的方法</li>\n</ul>\n<h3>AOP 解决了什么问题？</h3>\n<p>OOP 不能很好地处理一些分散在多个类或对象中的公共行为（如日志记录、事务管理、权限控制、接口限流、接口幂等等），这些行为通常被称为 <strong>横切关注点（cross-cutting concerns）</strong> 。如果我们在每个类或对象中都重复实现这些行为，那么会导致代码的冗余、复杂和难以维护。</p>\n<p>AOP 可以将横切关注点（如日志记录、事务管理、权限控制、接口限流、接口幂等等）从 <strong>核心业务逻辑（core concerns，核心关注点）</strong> 中分离出来，实现关注点的分离。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/crosscut-logic-and-businesslogic-separation      .png\" alt></p>\n<p>以日志记录为例进行介绍，假如我们需要对某些方法进行统一格式的日志记录，没有使用 AOP 技术之前，我们需要挨个写日志记录的逻辑代码，全是重复的逻辑。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CommonResponse</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> method1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      // 业务逻辑</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">      xxService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">method1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      // 省略具体的业务处理逻辑</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      // 日志记录</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">      ServletRequestAttributes</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> attributes </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (ServletRequestAttributes) </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">RequestContextHolder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getRequestAttributes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">      HttpServletRequest</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> request </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> attributes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getRequest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      // 省略记录日志的具体逻辑 如：获取各种信息，写入数据库等操作...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">      return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> CommonResponse</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">success</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CommonResponse</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> method2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      // 业务逻辑</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">      xxService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">method2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      // 省略具体的业务处理逻辑</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      // 日志记录</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">      ServletRequestAttributes</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> attributes </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (ServletRequestAttributes) </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">RequestContextHolder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getRequestAttributes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">      HttpServletRequest</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> request </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> attributes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getRequest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      // 省略记录日志的具体逻辑 如：获取各种信息，写入数据库等操作...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">      return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> CommonResponse</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">success</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// ...</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>使用 AOP 技术之后，我们可以将日志记录的逻辑封装成一个切面，然后通过切入点和通知来指定在哪些方法需要执行日志记录的操作。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 日志注解</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Target</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">({</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ElementType</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">PARAMETER</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ElementType</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">METHOD</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">})</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Retention</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">RetentionPolicy</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">RUNTIME</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Documented</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">interface</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\"> Log</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 描述</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> description</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">default</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 方法类型 INSERT DELETE UPDATE OTHER</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    MethodType</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> methodType</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">default</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> MethodType</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">OTHER</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 日志切面</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Component</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Aspect</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LogAspect</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 切入点，所有被 Log 注解标注的方法</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Pointcut</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"@annotation(cn.javaguide.annotation.Log)\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> webLog</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   * 环绕通知</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Around</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"webLog()\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> doAround</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ProceedingJoinPoint</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> joinPoint</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> throws</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Throwable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 省略具体的处理逻辑</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 省略其他代码</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这样的话，我们一行注解即可实现日志记录：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Log</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">description</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"method1\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">methodType</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> MethodType</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">INSERT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CommonResponse</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> method1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      // 业务逻辑</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">      xxService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">method1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      // 省略具体的业务处理逻辑</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">      return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> CommonResponse</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">success</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>AOP 的应用场景有哪些？</h3>\n<ul>\n<li>日志记录：自定义日志记录注解，利用 AOP，一行代码即可实现日志记录。</li>\n<li>性能统计：利用 AOP 在目标方法的执行前后统计方法的执行时间，方便优化和分析。</li>\n<li>事务管理：<code>@Transactional</code> 注解可以让 Spring 为我们进行事务管理比如回滚异常操作，免去了重复的事务管理逻辑。<code>@Transactional</code>注解就是基于 AOP 实现的。</li>\n<li>权限控制：利用 AOP 在目标方法执行前判断用户是否具备所需要的权限，如果具备，就执行目标方法，否则就不执行。例如，SpringSecurity 利用<code>@PreAuthorize</code> 注解一行代码即可自定义权限校验。</li>\n<li>接口限流：利用 AOP 在目标方法执行前通过具体的限流算法和实现对请求进行限流处理。</li>\n<li>缓存管理：利用 AOP 在目标方法执行前后进行缓存的读取和更新。</li>\n<li>……</li>\n</ul>\n<h3>AOP 实现方式有哪些？</h3>\n<p>AOP 的常见实现方式有动态代理、字节码操作等方式。</p>\n<p>Spring AOP 就是基于动态代理的，如果要代理的对象，实现了某个接口，那么 Spring AOP 会使用 <strong>JDK Proxy</strong>，去创建代理对象，而对于没有实现接口的对象，就无法使用 JDK Proxy 去进行代理了，这时候 Spring AOP 会使用 CGLIB 生成一个被代理对象的子类来作为代理，如下图所示：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/230ae587a322d6e4d09510161987d346.jpeg\" alt=\"SpringAOPProcess\"></p>\n<p><strong>Spring Boot 和 Spring 的动态代理的策略是不是也是一样的呢？</strong>其实不一样，很多人都理解错了。</p>\n<p>Spring Boot 2.0 之前，默认使用 <strong>JDK 动态代理</strong>。如果目标类没有实现接口，会抛出异常，开发者必须显式配置（<code>spring.aop.proxy-target-class=true</code>）使用 <strong>CGLIB 动态代理</strong> 或者注入接口来解决。Spring Boot 1.5.x 自动配置 AOP 代码如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Configuration</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">ConditionalOnClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">({ </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">EnableAspectJAutoProxy</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Aspect</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Advice</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> })</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">ConditionalOnProperty</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">prefix</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"spring.aop\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"auto\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> havingValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"true\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> matchIfMissing</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AopAutoConfiguration</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Configuration</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">EnableAspectJAutoProxy</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">proxyTargetClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 该配置类只有在 spring.aop.proxy-target-class=false 或未显式配置时才会生效。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 也就是说，如果开发者未明确选择代理方式，Spring 会默认加载 JDK 动态代理。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">ConditionalOnProperty</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">prefix</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"spring.aop\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"proxy-target-class\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> havingValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"false\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> matchIfMissing</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tpublic</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> JdkDynamicAutoProxyConfiguration</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Configuration</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">EnableAspectJAutoProxy</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">proxyTargetClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 该配置类只有在 spring.aop.proxy-target-class=true 时才会生效。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 即开发者通过属性配置明确指定使用 CGLIB 动态代理时，Spring 会加载这个配置类。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">ConditionalOnProperty</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">prefix</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"spring.aop\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"proxy-target-class\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> havingValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"true\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> matchIfMissing</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tpublic</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CglibAutoProxyConfiguration</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>Spring Boot 2.0 开始，如果用户什么都不配置的话，默认使用 <strong>CGLIB 动态代理</strong>。如果需要强制使用 JDK 动态代理，可以在配置文件中添加：<code>spring.aop.proxy-target-class=false</code>。Spring Boot 2.0 自动配置 AOP 代码如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Configuration</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">ConditionalOnClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">({ </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">EnableAspectJAutoProxy</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Aspect</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Advice</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">\t\tAnnotatedElement</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> })</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">ConditionalOnProperty</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">prefix</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"spring.aop\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"auto\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> havingValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"true\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> matchIfMissing</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AopAutoConfiguration</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Configuration</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">EnableAspectJAutoProxy</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">proxyTargetClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 该配置类只有在 spring.aop.proxy-target-class=false 时才会生效。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 即开发者通过属性配置明确指定使用 JDK 动态代理时，Spring 会加载这个配置类。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">ConditionalOnProperty</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">prefix</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"spring.aop\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"proxy-target-class\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> havingValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"false\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> matchIfMissing</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tpublic</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> JdkDynamicAutoProxyConfiguration</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Configuration</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">EnableAspectJAutoProxy</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">proxyTargetClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 该配置类只有在 spring.aop.proxy-target-class=true 或未显式配置时才会生效。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 也就是说，如果开发者未明确选择代理方式，Spring 会默认加载 CGLIB 代理。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">ConditionalOnProperty</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">prefix</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"spring.aop\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> name</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"proxy-target-class\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> havingValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"true\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> matchIfMissing</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">\tpublic</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CglibAutoProxyConfiguration</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\t}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>当然你也可以使用 <strong>AspectJ</strong> ！Spring AOP 已经集成了 AspectJ ，AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。</p>\n<p><strong>Spring AOP 属于运行时增强，而 AspectJ 是编译时增强。</strong> Spring AOP 基于代理(Proxying)，而 AspectJ 基于字节码操作(Bytecode Manipulation)。</p>\n<p>Spring AOP 已经集成了 AspectJ ，AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大，但是 Spring AOP 相对来说更简单。</p>\n<p>如果我们的切面比较少，那么两者性能差异不大。但是，当切面太多的话，最好选择 AspectJ ，它比 Spring AOP 快很多。</p>\n<h2>参考</h2>\n<ul>\n<li>AOP in Spring Boot, is it a JDK dynamic proxy or a Cglib dynamic proxy?：<a href=\"https://www.springcloud.io/post/2022-01/springboot-aop/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.springcloud.io/post/2022-01/springboot-aop/</a></li>\n<li>Spring Proxying Mechanisms：<a href=\"https://docs.spring.io/spring-framework/reference/core/aop/proxying.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://docs.spring.io/spring-framework/reference/core/aop/proxying.html</a></li>\n</ul>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/IoC&Aop-ioc-illustration.png",
      "date_published": "2023-09-10T14:59:47.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [],
      "tags": [
        "框架"
      ]
    },
    {
      "title": "一文搞懂 AI Agent 核心概念：Agent Loop、Context Engineering、Tools 注册",
      "url": "https://javaguide.cn/ai/agent/agent-basis.html",
      "id": "https://javaguide.cn/ai/agent/agent-basis.html",
      "summary": "深入解析 AI Agent 核心概念，梳理从被动响应到常驻自治的六代进化史，对比 Agent、传统编程、Workflow 的本质区别。",
      "content_html": "<p><a href=\"/zhuanlan/interview-guide.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/interview-guide-banner.png\" alt=\"《SpringAI 智能面试平台+RAG 知识库》\"></a></p>\n<p>还记得第一次被 ChatGPT 震撼的时刻吗？那时它还是个需要你费尽心思写提示词的&quot;静态百科全书&quot;。然而短短三年过去，AI 的进化速度早已超越了我们的想象——它不仅长出了&quot;四肢&quot;，学会了自己调用工具、自己操作电脑屏幕，甚至正在朝着 24 小时全自动打工的&quot;数字实体&quot;狂奔！</p>\n<p><strong>AI Agent（智能体）</strong> 正在从&quot;聊天工具&quot;向&quot;超级生产力&quot;狂奔，这是当下 AI 应用开发最热门的方向之一。无论是 OpenAI 的 Assistant API、Anthropic 的 Claude Agent，还是各种低代码平台（Coze、Dify），都在围绕 Agent 这个核心概念展开。</p>\n<p>今天 Guide 就来系统梳理 AI Agent 的核心概念，帮你建立完整的知识体系。本文接近 1.5w 字，建议收藏，通过本文你将搞懂：</p>\n<ol>\n<li><strong>AI Agent 六代进化史</strong>：从 2022 年的被动响应到 2025 年的常驻自治，Agent 经历了怎样的演进？每一代的核心特征和技术突破是什么？</li>\n<li>⭐ <strong>Agent vs 传统编程 vs Workflow</strong>：三者的本质区别是什么？为什么说&quot;传统编程和 Workflow 是人在做决策，Agent 是 AI 在做决策&quot;？</li>\n<li>⭐ <strong>Agent Loop（智能体循环）</strong>：Agent 是如何通过&quot;感知-思考-行动&quot;的循环来完成复杂任务的？ReAct、Reflection 等推理模式是如何工作的？</li>\n<li>⭐ <strong>Context Engineering（上下文工程）</strong>：如何设计 System Prompt？如何管理多轮对话的上下文？如何避免上下文溢出？</li>\n<li>⭐ <strong>Tools 注册与 Function Calling</strong>：Agent 如何调用外部工具？Function Calling 的底层机制是什么？如何设计可靠的工具接口？</li>\n</ol>\n<h2>背景与演进</h2>\n<h3>AI Agent 六代进化史</h3>\n<p>还记得第一次被 ChatGPT 震撼的时刻吗？那时它还是个需要你费尽心思写提示词的“静态百科全书”。</p>\n<p>然而短短三年过去，AI 的进化速度早已超越了我们的想象——它不仅长出了“四肢”，学会了自己调用工具、自己操作电脑屏幕，甚至正在朝着 24 小时全自动打工的“数字实体”狂奔！</p>\n<p>从最初的“被动响应”到未来的“具身智能”，AI Agent（智能体）到底经历了怎样的疯狂迭代？今天，我们就来一次性硬核梳理 <strong>AI Agent 的六代进化史</strong>。带你看懂 AI 从聊天工具到超级生产力的终极演进路线图！👇</p>\n<ol>\n<li><strong>第 0 代（2022年底）：被动响应。</strong> 以 ChatGPT 为代表，依赖提示词工程（Prompt Engineering），本质是“静态知识预言机”，无法感知实时世界且缺乏行动能力。</li>\n<li><strong>第 1 代（2023年中）：工具觉醒。</strong> 引入 Function Calling （允许模型调用外部API）和 RAG 技术（增强外部知识检索，虽 2020 年提出，但 2023 年广泛应用），赋予 AI “执行四肢”与外部记忆。AutoGPT 是早期代理尝试，但确实因无限循环和缺乏可靠规划而效率低（常被称为“hallucination-prone”）。</li>\n<li><strong>第 2 代（2023年底）：工程化编排。</strong> 确立 ReAct 推理框架，推广多智能体协作模式。Coze、Dify 等低代码平台降低了开发门槛，强调流程的可控性。这代强调从混乱自治到工程化，如通过DAG（有向无环图）避免AutoGPT的低效。</li>\n<li><strong>第 3 代（2024年底）：标准化与多模态。</strong> MCP 协议（Model Context Protocol）终结了集成碎片化，Computer Use 允许 Agent 通过屏幕、鼠标、键盘交互图形界面（多模态扩展）。Cursor 等 AI 编程工具推动了“Vibe Coding”（氛围编程，使用 AI 根据自然语言提示生成功能代码）。</li>\n<li><strong>第 4 代（2025年底）：常驻自治。</strong> 核心是 Agent Skills 技能封装和 Heartbeat 心跳机制（OpenClaw、Moltbook等普及），使 Agent 成为 24 小时后台运行、具备本地数据主权的“数字实体”。</li>\n<li><strong>第 5 代（前瞻）：闭环与具身。</strong> 进化方向为内建记忆、具备预测能力的世界模型，并从数字世界扩展至物理机器人领域。</li>\n</ol>\n<h3>⭐️ Agent、传统编程、Workflow 三者的本质区别是什么？</h3>\n<p><strong>传统编程和 Workflow 是人在做决策，Agent 是 AI 在做决策。</strong> 这是最本质的区别，其他差异（灵活性、门槛、维护成本）都从这一点派生而来。</p>\n<p><strong>从决策主体看：</strong></p>\n<div class=\"language-ebnf line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"ebnf\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-ebnf\"><span class=\"line\"><span>传统编程：程序员 ──→ 代码 ──→ 执行结果</span></span>\n<span class=\"line\"><span>Workflow：产品/开发 ──→ 流程图 ──→ 执行结果</span></span>\n<span class=\"line\"><span>Agent：用户描述意图 ──→ AI 决策 ──→ 动态执行</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>一句话总结：<strong>传统编程和 Workflow 都是人在做决策、提前设计好所有逻辑，而 Agent 是 AI 在做决策</strong>。</p>\n<p><strong>从三个核心维度对比：</strong></p>\n<p><strong>1. 决策与灵活性</strong></p>\n<p>| 方式     | 遇到预设外的情况时...            |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/ai/agent/agent-core-arch.png",
      "date_published": "2023-08-07T10:15:14.000Z",
      "date_modified": "2026-04-10T08:31:20.000Z",
      "authors": [],
      "tags": [
        "AI 应用开发"
      ]
    },
    {
      "title": "万字拆解 LLM 运行机制：Token、上下文与采样参数",
      "url": "https://javaguide.cn/ai/llm-basis/llm-operation-mechanism.html",
      "id": "https://javaguide.cn/ai/llm-basis/llm-operation-mechanism.html",
      "summary": "深入剖析大语言模型（LLM）底层运行机制，详解 Token、上下文窗口、Temperature、Top-p 等核心概念与采样参数，帮助开发者真正理解并掌控大模型。",
      "content_html": "<p><a href=\"/zhuanlan/interview-guide.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/interview-guide-banner.png\" alt=\"《SpringAI 智能面试平台+RAG 知识库》\"></a></p>\n<p>在探讨 RAG、Agent 工作流、MCP 协议等复杂架构的过程中，我发现一个非常普遍的现象：很多开发者在构建 Agent 工作流或调优 RAG 检索时，往往会在最底层的 LLM 参数上踩坑。比如，为什么明明设置了温度为 0，结构化输出还是偶尔崩溃？为什么往模型里塞了长文档后，它好像失忆了，忽略了 System Prompt 里的关键指令？</p>\n<p><strong>万丈高楼平地起。</strong> 如果不搞懂底层 LLM 吞吐数据的基本原理，再高级的设计模式在生产环境中也会变得脆弱不堪。</p>\n<p>因此，有了这篇基础扫盲文章。我们将暂时放下顶层的架构设计，回到一切的起点。大模型没有魔法，底层只有纯粹的数学与工程。接下来，我们将扒开 LLM 的黑盒，把日常调用 API 时遇到的 Token、上下文窗口、Temperature 等高频词汇，还原为清晰、可控的工程概念。通过本文你将搞懂：</p>\n<ol>\n<li>大模型（LLM）到底在做什么？</li>\n<li>⭐ Token 是什么？为什么中文和英文的 Token 消耗不同？</li>\n<li>⭐ 上下文窗口是什么？为什么会有上限？</li>\n<li>⭐ Temperature、Top-p、Top-k 等采样参数如何影响输出？</li>\n<li>如何做 Token 预算？输入输出如何计费？</li>\n</ol>\n<h2>大模型（LLM）到底在做什么</h2>\n<h3>一句话理解大模型</h3>\n<p>当你在输入法里打“今天天气真”，它会自动建议“好”——大模型做的事情本质上一样，只不过它看的不是前面几个字，而是前面几千甚至几十万个字，且每次只“补”一个 Token（文本碎片），然后把刚补的内容也加入上下文，再预测下一个，如此循环，直到生成完整回答。</p>\n<p>这个过程叫做<strong>自回归生成（Autoregressive Generation）</strong>。</p>\n<p>理解了这一点，后面所有概念都有了根基：</p>\n<ul>\n<li><strong>Token</strong>：模型每一步“补”的那个文本碎片，就是一个 Token。</li>\n<li><strong>上下文窗口</strong>：模型在“补”之前能看到的最大文本量。</li>\n<li><strong>Temperature / Top-p</strong>：模型在多个候选碎片中“选哪个”的策略。</li>\n<li><strong>Max Tokens</strong>：你允许模型最多“补”多少步。</li>\n</ul>\n<p>有了这个心智模型，我们再逐一展开。</p>\n<h3>全局概念地图</h3>\n<p>在深入每个概念之前，先看一张完整的调用流程图，帮你在 30 秒内建立全局认知：</p>\n<div class=\"language- line-numbers-mode\" data-highlighter=\"shiki\" data-ext style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-\"><span class=\"line\"><span>用户输入</span></span>\n<span class=\"line\"><span>  ↓</span></span>\n<span class=\"line\"><span>[Tokenizer] → Token 序列</span></span>\n<span class=\"line\"><span>  ↓</span></span>\n<span class=\"line\"><span>塞入上下文窗口（System Prompt + User Prompt + 历史 + RAG 片段）</span></span>\n<span class=\"line\"><span>  ↓                                              ↑</span></span>\n<span class=\"line\"><span>模型推理（自注意力机制）                    [Embedding + 向量检索]</span></span>\n<span class=\"line\"><span>  ↓                                         从知识库召回相关片段</span></span>\n<span class=\"line\"><span>logits → [Temperature/Top-p/Top-k] → 采样出下一个 Token</span></span>\n<span class=\"line\"><span>  ↓</span></span>\n<span class=\"line\"><span>重复直到 EOS 或 Max Tokens</span></span>\n<span class=\"line\"><span>  ↓</span></span>\n<span class=\"line\"><span>结构化输出解析 &#x26; 校验</span></span>\n<span class=\"line\"><span>  ↓</span></span>\n<span class=\"line\"><span>业务消费</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>后续每个小节都能在这张图上找到对应位置。</p>\n<h3>Token：模型的“阅读单位”</h3>\n<p>你可以把 Token 理解为“模型的阅读单位”。我们人类读中文是一个字一个字地看，读英文是一个词一个词地看；但模型既不按字、也不按词——它用一套自己的“拆字规则”（叫 Tokenizer）把文本切成大小不等的碎片，每个碎片就是一个 Token。</p>\n<p><strong>为什么不直接按字或按词切？</strong> 因为模型需要在“词表大小”和“序列长度”之间取平衡：</p>\n<ul>\n<li>如果每个汉字都是一个 Token，词表小、但序列长（模型要“补”更多步）；</li>\n<li>如果每个词都是一个 Token，序列短、但词表会爆炸（中文词组太多了）。</li>\n</ul>\n<p>所以实际使用的是一种折中方案——<strong>子词切分算法</strong>（如 BPE、Unigram），它会把高频词保留为整体，把低频词拆成更小的片段。</p>\n<blockquote>\n<p><strong>💡 一个直觉</strong>：你可以把 Token 想象成乐高积木——常用的“积木块”比较大（比如“你好”可能是一个 Token），不常用的词会被拆成更小的基础块拼起来。</p>\n</blockquote>\n<p><strong>Token 不是“一个字”或“一个词”的严格等价物</strong>：</p>\n<ul>\n<li>英文可能一个单词被拆成多个 Token；</li>\n<li>中文可能一个词被拆成多个 Token，也可能多个字合并成一个 Token（取决于词频与词表）。</li>\n</ul>\n<p>因此，工程上通常只用 <strong>经验估算</strong> 做容量规划，而用 <strong>实际 API 返回的 usage</strong>（若供应商提供）做精确计费与监控。</p>\n<p><strong>经验估算（仅用于粗略规划）</strong>：</p>\n<ul>\n<li>英文：1 Token 大约对应 3~4 个字符（与文本类型相关）。</li>\n<li>中文：1 Token 常见在 1~2 个汉字上下波动（与混排比例强相关）。</li>\n</ul>\n<p>以 DeepSeek 官方数据为例：1 个英文字符约消耗 0.3 Token，1 个中文字符约消耗 0.6 Token。换算过来，1 个 Token 约等于 3.3 个英文字符或 1.7 个中文字符，与上述经验值吻合。</p>\n<p><strong>💡 成本趋势提示</strong>：Token 成本与编码器（Tokenizer）版本强相关。早期模型（如 GPT-3.5）中文压缩率较低（约 1 字 1.5~2 Token）。GPT-4o 使用 o200k_base Tokenizer（词表约 20 万），相比前代 cl100k_base 对中文的压缩率有进一步提升；Qwen2.5 词表约 15 万，对中文常用词同样有优化。实测数据因文本类型而异：新闻类文本约 1.5 字/Token，技术文档约 1.2 字/Token。“趋近 1 字 1 Token”仅适用于高频词汇，不建议作为成本估算基准。<strong>在做成本预算时，请务必查阅当前模型版本的官方 Tokenizer 演示，勿沿用旧模型经验。</strong></p>\n<p>Token 划分的精细度会直接影响模型的理解能力。特别是在中文处理时，分词歧义（同一字符序列的多种切分方式）和生僻字/低频专业术语的切分粒度，会直接影响模型的语义理解效果。</p>\n<p><strong>Token 化过程示例</strong>：</p>\n<ul>\n<li>原文：<code>你好，我是 Guide。</code></li>\n<li>切分：<code>[你好]</code> <code>[，]</code> <code>[我是]</code> <code>[Guide]</code> <code>[。]</code></li>\n<li>统计：原文 12 字符 → Token 数 5 个 → 压缩比约 2.4 倍</li>\n</ul>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/llm/llm-token-process.png\" alt=\"Token 化过程示例\"></p>\n<blockquote>\n<p><strong>⚠️ 注意</strong>：实际的 Token 切分由模型供应商的 Tokenizer 实现，不同供应商对相同文本可能产生不同的 Token 序列。生产环境中应使用对应供应商的 Tokenizer 工具进行精确计数。</p>\n</blockquote>\n<p><strong>特殊 Token</strong>：除了文本内容对应的 Token，模型内部还会使用一些特殊标记，这些也会计入 Token 总数：</p>\n<p>| 特殊 Token                   | 用途                            | 示例           |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/ai/llm/llm-token-process.png",
      "date_published": "2023-08-07T10:15:14.000Z",
      "date_modified": "2026-04-08T07:51:57.000Z",
      "authors": [],
      "tags": [
        "AI 应用开发"
      ]
    },
    {
      "title": "万字详解 RAG 基础概念",
      "url": "https://javaguide.cn/ai/rag/rag-basis.html",
      "id": "https://javaguide.cn/ai/rag/rag-basis.html",
      "summary": "深入解析 RAG（检索增强生成）核心概念，涵盖 RAG 工作原理、与传统搜索引擎区别、核心优势与局限性等高频面试考点。",
      "content_html": "<p><a href=\"/zhuanlan/interview-guide.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/interview-guide-banner.png\" alt=\"《SpringAI 智能面试平台+RAG 知识库》\"></a></p>\n<p>去年面字节的时候，面试官问我：“你们项目里的知识库问答是怎么做的？” 我说：“直接调 OpenAI 的 API，把文档塞进去让模型自己读。”</p>\n<p>空气突然安静了三秒。我看到面试官的眉头皱了一下，才意识到事情不对——当时我们项目的文档有 20 多万字，每次请求都超 Token 上限，而且模型根本记不住上周刚更新的接口文档。</p>\n<p>面试被挂后才懂：这叫“裸调 LLM”，而正确的做法应该是 RAG。</p>\n<p>段子归段子，RAG（检索增强生成）确实是当下 LLM 应用开发的核心技术栈，也是面试中的高频考点。今天 Guide 分享几道 RAG 基础概念相关的面试题，希望对大家有帮助：</p>\n<ol>\n<li>⭐️ 什么是 RAG？</li>\n<li>⭐️ 为什么需要 RAG？</li>\n<li>RAG 的常见用途有哪些？</li>\n<li>⭐️ 既然这些场景这么好，为什么有些企业还是宁愿用传统搜索而不是 RAG？</li>\n<li>RAG 工作原理</li>\n<li>RAG 与传统搜索引擎的区别是什么？</li>\n<li>⭐️ RAG 的核心优势和局限性分别是什么？</li>\n</ol>\n<h2>⭐️ 什么是 RAG？</h2>\n<p><strong>RAG (Retrieval-Augmented Generation，检索增强生成)</strong> 是一种将强大的<strong>信息检索 (Information Retrieval, IR)</strong> 技术与<strong>生成式大语言模型 (LLM)</strong> 相结合的框架。</p>\n<p>RAG 的核心思想是：在让 LLM 回答问题或生成文本之前，先从一个大规模的知识库（如数据库、文档集合）中检索出相关的上下文信息，然后将这些信息与原始问题一并提供给 LLM，从而“增强”其生成能力，使其能够产出更准确、更具时效性、更符合特定领域知识的回答。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/rag/rag-simplified-architecture-diagram.jpeg\" alt=\"RAG 示意图\"></p>\n<h2>⭐️ 为什么需要 RAG？</h2>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/ai/rag/rag-llm-challenges.png\" alt=\"RAG（检索增强生成）如何解决 LLM 的核心挑战\"></p>\n<p>尽管 LLM 本身拥有海量的知识，但它依然面临三个核心挑战，而 RAG 正是解决这些挑战的有效方案：</p>\n<p><strong>1. 解决知识时效性问题（对抗“知识截止”）</strong></p>\n<p>预训练的 LLM 的知识被固化在其 <strong>训练数据的截止时间点（Knowledge Cutoff）</strong>。例如，GPT-4 的知识库可能截止于 2023 年 12 月。对于此后发生的新事件、新知识，LLM 无法直接给出准确答案。RAG 通过 <strong>动态检索外部知识源</strong>，为 LLM 提供“实时”的知识补充，从而克服了知识过时的问题。</p>\n<p><strong>2. 打通私有数据访问（支撑企业级应用）</strong></p>\n<p>出于数据安全和商业机密的考虑，企业内部的 <strong>私有数据</strong>（如产品文档、内部知识库、客户数据等）无法被公开的 LLM 直接访问。RAG 技术能够安全地连接这些私有数据源，在用户提问时，仅将与问题相关的片段信息提取出来提供给 LLM，使其能够在 <strong>不泄露全部数据</strong> 的前提下，基于企业自身的知识进行回答，实现真正可用的企业级智能应用。</p>\n<p><strong>3. 提升回答的准确性与可追溯性（对抗“模型幻觉”）</strong></p>\n<p>LLM 有时会产生 <strong>“幻觉（Hallucination）”</strong> ，即编造不符合事实的信息。RAG 通过提供明确的、有据可查的参考文本，强制 LLM 的回答 <strong>基于检索到的事实</strong>，大大降低了幻觉的发生率。同时，由于可以展示引用的原文，使得答案的 <strong>来源可追溯、可验证</strong>，增强了系统的可靠性和用户的信任度。</p>\n<h2>RAG 的常见用途有哪些？</h2>\n<p>RAG（检索增强生成）最适合用在 <strong>“答案依赖外部资料、且资料会变化/很长”</strong> 的场景：先从知识库检索相关内容，再让大模型基于检索结果生成回答，从而减少胡编、提升可追溯性。</p>\n<p>下面列举几个最常见的场景：</p>\n<ul>\n<li><strong>客服机器人</strong>：基于产品知识库做问答、排障、流程引导；例：“如何退换货/开发票？”“某型号设备报错码怎么处理？”</li>\n<li><strong>研发/运维 Copilot</strong>：检索代码库、接口文档、告警手册，辅助定位问题与生成修复建议。</li>\n<li><strong>医疗助手</strong>：检索指南/药品说明/院内规范后生成辅助建议（不做最终诊断）；例：“某药禁忌是什么？”“依据指南解释检查指标含义”。</li>\n<li><strong>法律咨询</strong>：基于法规条文/案例/合同模板检索，生成条款解释与风险提示；例：“违约金如何计算？”“不可抗力条款怎么写更稳妥？”</li>\n<li><strong>教育辅导</strong>：从教材/讲义/题库检索知识点，生成讲解与例题步骤；例：“这道题对应哪个公式？怎么推导？”</li>\n<li><strong>企业内部助手</strong>：连接制度、SOP、会议纪要、技术文档做检索/总结/对比；例：“某流程最新版本是什么？”“对比两份方案差异并给结论”。</li>\n<li><strong>其他</strong>：投研/合规/审计（报告/披露/内控）；销售/方案支持（产品手册/标书模板、生成方案并标注出处）。</li>\n</ul>\n<h2>⭐️ 既然这些场景这么好，为什么有些企业还是宁愿用传统搜索而不是 RAG？</h2>\n<p>因为 RAG 存在推理成本和响应延迟的问题。在某些纯粹为了“找文件”而非“总结答案”的简单场景，传统搜索依然具备极致的效率优势。</p>\n<p>下面简单对比一下二者：</p>\n<p>| 维度          | 传统搜索（搜索框）                       | RAG（检索+生成）                                 |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/ai/rag/rag-simplified-architecture-diagram.jpeg",
      "date_published": "2023-08-07T10:15:14.000Z",
      "date_modified": "2026-04-10T08:31:20.000Z",
      "authors": [],
      "tags": [
        "AI 应用开发"
      ]
    },
    {
      "title": "SQL常见面试题总结（2）",
      "url": "https://javaguide.cn/database/sql/sql-questions-02.html",
      "id": "https://javaguide.cn/database/sql/sql-questions-02.html",
      "summary": "SQL常见面试题总结第二篇，详解INSERT、UPDATE、DELETE等DML数据操作语句，包括批量插入、从其他表导入、带更新的插入等实战技巧。",
      "content_html": "<blockquote>\n<p>题目来源于：<a href=\"https://www.nowcoder.com/exam/oj?page=1&amp;tab=SQL%E7%AF%87&amp;topicId=240\" target=\"_blank\" rel=\"noopener noreferrer\">牛客题霸 - SQL 进阶挑战</a></p>\n</blockquote>\n<h2>增删改操作</h2>\n<p>SQL 插入记录的方式汇总：</p>\n<ul>\n<li><strong>普通插入（全字段）</strong> ：<code>INSERT INTO table_name VALUES (value1, value2, ...)</code></li>\n<li><strong>普通插入（限定字段）</strong> ：<code>INSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...)</code></li>\n<li><strong>多条一次性插入</strong> ：<code>INSERT INTO table_name (column1, column2, ...) VALUES (value1_1, value1_2, ...), (value2_1, value2_2, ...), ...</code></li>\n<li><strong>从另一个表导入</strong> ：<code>INSERT INTO table_name SELECT * FROM table_name2 [WHERE key=value]</code></li>\n<li><strong>带更新的插入</strong> ：<code>REPLACE INTO table_name VALUES (value1, value2, ...)</code>（注意这种原理是检测到主键或唯一性索引键重复就删除原记录后重新插入）</li>\n</ul>\n<h3>插入记录（一）</h3>\n<p><strong>描述</strong>：牛客后台会记录每个用户的试卷作答记录到 <code>exam_record</code> 表，现在有两个用户的作答记录详情如下：</p>\n<ul>\n<li>用户 1001 在 2021 年 9 月 1 日晚上 10 点 11 分 12 秒开始作答试卷 9001，并在 50 分钟后提交，得了 90 分；</li>\n<li>用户 1002 在 2021 年 9 月 4 日上午 7 点 1 分 2 秒开始作答试卷 9002，并在 10 分钟后退出了平台。</li>\n</ul>\n<p>试卷作答记录表<code>exam_record</code>中，表已建好，其结构如下，请用一条语句将这两条记录插入表中。</p>\n<p>| Filed       | Type       | Null | Key | Extra          | Default | Comment  |<br>\n|</p>\n",
      "date_published": "2023-07-13T14:23:50.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "SQL常见面试题总结（3）",
      "url": "https://javaguide.cn/database/sql/sql-questions-03.html",
      "id": "https://javaguide.cn/database/sql/sql-questions-03.html",
      "summary": "SQL常见面试题总结第三篇，深入讲解聚合函数COUNT、SUM、AVG、MAX、MIN的使用，以及GROUP BY分组、HAVING过滤、截断平均值计算等进阶技巧。",
      "content_html": "<blockquote>\n<p>题目来源于：<a href=\"https://www.nowcoder.com/exam/oj?page=1&amp;tab=SQL%E7%AF%87&amp;topicId=240\" target=\"_blank\" rel=\"noopener noreferrer\">牛客题霸 - SQL 进阶挑战</a></p>\n</blockquote>\n<p>较难或者困难的题目可以根据自身实际情况和面试需要来决定是否要跳过。</p>\n<h2>聚合函数</h2>\n<h3>SQL 类别高难度试卷得分的截断平均值（较难）</h3>\n<p><strong>描述</strong>： 牛客的运营同学想要查看大家在 SQL 类别中高难度试卷的得分情况。</p>\n<p>请你帮她从<code>exam_record</code>数据表中计算所有用户完成 SQL 类别高难度试卷得分的截断平均值（去掉一个最大值和一个最小值后的平均值）。</p>\n<p>示例数据：<code>examination_info</code>（<code>exam_id</code> 试卷 ID, tag 试卷类别, <code>difficulty</code> 试卷难度, <code>duration</code> 考试时长, <code>release_time</code> 发布时间）</p>\n<p>| id  | exam_id | tag  | difficulty | duration | release_time        |<br>\n|</p>\n",
      "date_published": "2023-07-13T14:23:50.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "SQL常见面试题总结（4）",
      "url": "https://javaguide.cn/database/sql/sql-questions-04.html",
      "id": "https://javaguide.cn/database/sql/sql-questions-04.html",
      "summary": "SQL常见面试题总结第四篇，详解MySQL 8.0窗口函数ROW_NUMBER、RANK、DENSE_RANK、NTILE、LAG、LEAD等的用法和应用场景。",
      "content_html": "<blockquote>\n<p>题目来源于：<a href=\"https://www.nowcoder.com/exam/oj?page=1&amp;tab=SQL%E7%AF%87&amp;topicId=240\" target=\"_blank\" rel=\"noopener noreferrer\">牛客题霸 - SQL 进阶挑战</a></p>\n</blockquote>\n<p>较难或者困难的题目可以根据自身实际情况和面试需要来决定是否要跳过。</p>\n<h2>专用窗口函数</h2>\n<p>MySQL 8.0 版本引入了窗口函数的支持，下面是 MySQL 中常见的窗口函数及其用法：</p>\n<ol>\n<li><code>ROW_NUMBER()</code>: 为查询结果集中的每一行分配一个唯一的整数值。</li>\n</ol>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1, col2, </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">ROW_NUMBER</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">OVER</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">AS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> row_num</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> table</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><ol start=\"2\">\n<li><code>RANK()</code>: 计算每一行在排序结果中的排名。</li>\n</ol>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1, col2, </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">RANK</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">OVER</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1 </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">DESC</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">AS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ranking</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> table</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><ol start=\"3\">\n<li><code>DENSE_RANK()</code>: 计算每一行在排序结果中的排名，保留相同的排名。</li>\n</ol>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1, col2, </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">DENSE_RANK</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">OVER</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1 </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">DESC</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">AS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ranking</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> table</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><ol start=\"4\">\n<li><code>NTILE(n)</code>: 将结果分成 n 个基本均匀的桶，并为每个桶分配一个标识号。</li>\n</ol>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1, col2, </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">NTILE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">4</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">OVER</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">AS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> bucket</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> table</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><ol start=\"5\">\n<li><code>SUM()</code>, <code>AVG()</code>,<code>COUNT()</code>, <code>MIN()</code>, <code>MAX()</code>: 这些聚合函数也可以与窗口函数结合使用，计算窗口内指定列的汇总、平均值、计数、最小值和最大值。</li>\n</ol>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1, col2, </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">SUM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(col1) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">OVER</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> () </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">AS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> sum_col</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> table</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><ol start=\"6\">\n<li><code>LEAD()</code> 和 <code>LAG()</code>: LEAD 函数用于获取当前行之后的某个偏移量的行的值，而 LAG 函数用于获取当前行之前的某个偏移量的行的值。</li>\n</ol>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1, col2, </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">LEAD</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(col1, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">OVER</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">AS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> next_col1,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">                 LAG</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(col1, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">OVER</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">AS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> prev_col1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> table</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><ol start=\"7\">\n<li><code>FIRST_VALUE()</code> 和 <code>LAST_VALUE()</code>: FIRST_VALUE 函数用于获取窗口内指定列的第一个值，LAST_VALUE 函数用于获取窗口内指定列的最后一个值。</li>\n</ol>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1, col2, </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">FIRST_VALUE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(col2) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">OVER</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">PARTITION</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1 </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col2) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">AS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> first_val,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">                 LAST_VALUE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(col2) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">OVER</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">PARTITION</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col1 </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> col2) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">AS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> last_val</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> table</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>窗口函数通常需要配合 OVER 子句一起使用，用于定义窗口的大小、排序规则和分组方式。</p>\n<h3>每类试卷得分前三名</h3>\n<p><strong>描述</strong>：</p>\n<p>现有试卷信息表 <code>examination_info</code>（<code>exam_id</code> 试卷 ID, <code>tag</code> 试卷类别, <code>difficulty</code> 试卷难度, <code>duration</code> 考试时长, <code>release_time</code> 发布时间）：</p>\n<p>| id  | exam_id | tag  | difficulty | duration | release_time        |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/database/sql/29A377601170AB822322431FCDF7EDFE.png",
      "date_published": "2023-07-13T14:23:50.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "SQL常见面试题总结（5）",
      "url": "https://javaguide.cn/database/sql/sql-questions-05.html",
      "id": "https://javaguide.cn/database/sql/sql-questions-05.html",
      "summary": "SQL常见面试题总结第五篇，详解NULL空值处理技巧，包括IFNULL、COALESCE函数，以及使用CASE WHEN进行条件统计和完成率计算。",
      "content_html": "<blockquote>\n<p>题目来源于：<a href=\"https://www.nowcoder.com/exam/oj?page=1&amp;tab=SQL%E7%AF%87&amp;topicId=240\" target=\"_blank\" rel=\"noopener noreferrer\">牛客题霸 - SQL 进阶挑战</a></p>\n</blockquote>\n<p>较难或者困难的题目可以根据自身实际情况和面试需要来决定是否要跳过。</p>\n<h2>空值处理</h2>\n<h3>统计有未完成状态的试卷的未完成数和未完成率</h3>\n<p><strong>描述</strong>：</p>\n<p>现有试卷作答记录表 <code>exam_record</code>（<code>uid</code> 用户 ID, <code>exam_id</code> 试卷 ID, <code>start_time</code> 开始作答时间, <code>submit_time</code> 交卷时间, <code>score</code> 得分），数据如下：</p>\n<p>| id  | uid  | exam_id | start_time          | submit_time         | score  |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/database/sql/D2B491866B85826119EE3474F10D3636.png",
      "date_published": "2023-07-13T14:23:50.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "LinkedHashMap 源码分析",
      "url": "https://javaguide.cn/java/collection/linkedhashmap-source-code.html",
      "id": "https://javaguide.cn/java/collection/linkedhashmap-source-code.html",
      "summary": "LinkedHashMap源码深度剖析：详解LinkedHashMap维护双向链表实现插入/访问有序、LRU缓存实现、与HashMap区别及遍历效率优化。",
      "content_html": "<h2>LinkedHashMap 简介</h2>\n<p><code>LinkedHashMap</code> 是 Java 提供的一个集合类，它继承自 <code>HashMap</code>，并在 <code>HashMap</code> 基础上维护一条双向链表，使得具备如下特性:</p>\n<ol>\n<li>支持遍历时会按照插入顺序有序进行迭代。</li>\n<li>支持按照元素访问顺序排序,适用于封装 LRU 缓存工具。</li>\n<li>因为内部使用双向链表维护各个节点，所以遍历时的效率和元素个数成正比，相较于和容量成正比的 HashMap 来说，迭代效率会高很多。</li>\n</ol>\n<p><code>LinkedHashMap</code> 逻辑结构如下图所示，它是在 <code>HashMap</code> 基础上在各个节点之间维护一条双向链表，使得原本散列在不同 bucket 上的节点、链表、红黑树有序关联起来。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/linkhashmap-structure-overview.png\" alt=\"LinkedHashMap 逻辑结构\"></p>\n<h2>LinkedHashMap 使用示例</h2>\n<h3>插入顺序遍历</h3>\n<p>如下所示，我们按照顺序往 <code>LinkedHashMap</code> 添加元素然后进行遍历。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">HashMap</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> ></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> map </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> ()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"a\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"2\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"g\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"3\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"r\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"1\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"e\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"23\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Map</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> ></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> entry</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">entrySet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \":\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>输出：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">a</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">g</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">r</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">e</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">23</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>可以看出，<code>LinkedHashMap</code> 的迭代顺序是和插入顺序一致的,这一点是 <code>HashMap</code> 所不具备的。</p>\n<h3>访问顺序遍历</h3>\n<p><code>LinkedHashMap</code> 定义了排序模式 <code>accessOrder</code>(boolean 类型，默认为 false)，访问顺序则为 true，插入顺序则为 false。</p>\n<p>为了实现访问顺序遍历，我们可以使用传入 <code>accessOrder</code> 属性的 <code>LinkedHashMap</code> 构造方法，并将 <code>accessOrder</code> 设置为 true，表示其具备访问有序性。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">LinkedHashMap</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> map </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">16</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0.75f</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"one\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"two\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"three\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">4</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"four\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">5</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"five\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//访问元素2,该元素会被移动至链表末端</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//访问元素3,该元素会被移动至链表末端</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Map</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> entry </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">entrySet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \" : \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>输出：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> :</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> one</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">4</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> :</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> four</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">5</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> :</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> five</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> :</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> two</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> :</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> three</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>可以看出，<code>LinkedHashMap</code> 的迭代顺序是和访问顺序一致的。</p>\n<h3>LRU 缓存</h3>\n<p>从上一个我们可以了解到通过 <code>LinkedHashMap</code> 我们可以封装一个简易版的 LRU（<strong>L</strong>east <strong>R</strong>ecently <strong>U</strong>sed，最近最少使用） 缓存，确保当存放的元素超过容器容量时，将最近最少访问的元素移除。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/lru-cache.png\" alt></p>\n<p>具体实现思路如下：</p>\n<ul>\n<li>继承 <code>LinkedHashMap</code>;</li>\n<li>构造方法中指定 <code>accessOrder</code> 为 true ，这样在访问元素时就会把该元素移动到链表尾部，链表首元素就是最近最少被访问的元素；</li>\n<li>重写<code>removeEldestEntry</code> 方法，该方法会返回一个 boolean 值，告知 <code>LinkedHashMap</code> 是否需要移除链表首元素（缓存容量有限）。</li>\n</ul>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LRUCache</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedHashMap</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> capacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> LRUCache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> capacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        super</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(capacity, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0.75f</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">capacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> capacity;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 判断size超过容量时返回true，告知LinkedHashMap移除最老的缓存项(即链表的第一个元素)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    protected</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> removeEldestEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">eldest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> capacity;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>测试代码如下，笔者初始化缓存容量为 3，然后按照次序先后添加 4 个元素。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">LRUCache</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> cache </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LRUCache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">cache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"one\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">cache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"two\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">cache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"three\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">cache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">4</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"four\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">cache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">5</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"five\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 5</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">cache</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(i));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>输出：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">null</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">null</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">three</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">four</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">five</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>从输出结果来看，由于缓存容量为 3 ，因此，添加第 4 个元素时，第 1 个元素会被删除。添加第 5 个元素时，第 2 个元素会被删除。</p>\n<h2>LinkedHashMap 源码解析</h2>\n<h3>Node 的设计</h3>\n<p>在正式讨论 <code>LinkedHashMap</code> 前，我们先来聊聊 <code>LinkedHashMap</code> 节点 <code>Entry</code> 的设计,我们都知道 <code>HashMap</code> 的 bucket 上的因为冲突转为链表的节点会在符合以下两个条件时会将链表转为红黑树:</p>\n<ol>\n<li><s>链表上的节点个数达到树化的阈值 7，即<code>TREEIFY_THRESHOLD - 1</code>。</s></li>\n<li>bucket 的容量达到最小的树化容量即<code>MIN_TREEIFY_CAPACITY</code>。</li>\n</ol>\n<blockquote>\n<p><strong>🐛 修正（参见：<a href=\"https://github.com/Snailclimb/JavaGuide/issues/2147\" target=\"_blank\" rel=\"noopener noreferrer\">issue#2147</a>）</strong>：</p>\n<p>链表上的节点个数达到树化的阈值是 8 而非 7。因为源码的判断是从链表初始元素开始遍历，下标是从 0 开始的，所以判断条件设置为 8-1=7，其实是迭代到尾部元素时再判断整个链表长度大于等于 8 才进行树化操作。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/jvm/LinkedHashMap-putval-TREEIFY.png\" alt></p>\n</blockquote>\n<p>而 <code>LinkedHashMap</code> 是在 <code>HashMap</code> 的基础上为 bucket 上的每一个节点建立一条双向链表，这就使得转为红黑树的树节点也需要具备双向链表节点的特性，即每一个树节点都需要拥有两个引用存储前驱节点和后继节点的地址,所以对于树节点类 <code>TreeNode</code> 的设计就是一个比较棘手的问题。</p>\n<p>对此我们不妨来看看两者之间节点类的类图，可以看到:</p>\n<ol>\n<li><code>LinkedHashMap</code> 的节点内部类 <code>Entry</code> 基于 <code>HashMap</code> 的基础上，增加 <code>before</code> 和 <code>after</code> 指针使节点具备双向链表的特性。</li>\n<li><code>HashMap</code> 的树节点 <code>TreeNode</code> 继承了具备双向链表特性的 <code>LinkedHashMap</code> 的 <code>Entry</code>。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/map-hashmap-linkedhashmap.png\" alt=\"LinkedHashMap 和 HashMap 之间的关系\"></p>\n<p>很多读者此时就会有这样一个疑问，为什么 <code>HashMap</code> 的树节点 <code>TreeNode</code> 要通过 <code>LinkedHashMap</code> 获取双向链表的特性呢?为什么不直接在 <code>Node</code> 上实现前驱和后继指针呢?</p>\n<p>先来回答第一个问题，我们都知道 <code>LinkedHashMap</code> 是在 <code>HashMap</code> 基础上对节点增加双向指针实现双向链表的特性,所以 <code>LinkedHashMap</code> 内部链表转红黑树时，对应的节点会转为树节点 <code>TreeNode</code>,为了保证使用 <code>LinkedHashMap</code> 时树节点具备双向链表的特性，所以树节点 <code>TreeNode</code> 需要继承 <code>LinkedHashMap</code> 的 <code>Entry</code>。</p>\n<p>再来说说第二个问题，我们直接在 <code>HashMap</code> 的节点 <code>Node</code> 上直接实现前驱和后继指针,然后 <code>TreeNode</code> 直接继承 <code>Node</code> 获取双向链表的特性为什么不行呢？其实这样做也是可以的。只不过这种做法会使得使用 <code>HashMap</code> 时存储键值对的节点类 <code>Node</code> 多了两个没有必要的引用，占用没必要的内存空间。</p>\n<p>所以，为了保证 <code>HashMap</code> 底层的节点类 <code>Node</code> 没有多余的引用，又要保证 <code>LinkedHashMap</code> 的节点类 <code>Entry</code> 拥有存储链表的引用，设计者就让 <code>LinkedHashMap</code> 的节点 <code>Entry</code> 去继承 Node 并增加存储前驱后继节点的引用 <code>before</code>、<code>after</code>，让需要用到链表特性的节点去实现需要的逻辑。然后树节点 <code>TreeNode</code> 再通过继承 <code>Entry</code> 获取 <code>before</code>、<code>after</code> 两个指针。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Entry</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> HashMap</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Entry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> before</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> after</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> hash</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            super</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(hash, key, value, next);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>但是这样做，不也使得使用 <code>HashMap</code> 时的 <code>TreeNode</code> 多了两个没有必要的引用吗?这不也是一种空间的浪费吗？</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> TreeNode</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedHashMap</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //略</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>对于这个问题,引用作者的一段注释，作者们认为在良好的 <code>hashCode</code> 算法时，<code>HashMap</code> 转红黑树的概率不大。就算转为红黑树变为树节点，也可能会因为移除或者扩容将 <code>TreeNode</code> 变为 <code>Node</code>，所以 <code>TreeNode</code> 的使用概率不算很大，对于这一点资源空间的浪费是可以接受的。</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">Because</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> TreeNodes</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> are</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> about</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> twice</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> the</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> size</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> of</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> regular</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> nodes,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> we</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">use</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> them</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> only</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> when</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> bins</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> contain</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> enough</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> nodes</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> to</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> warrant</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> use</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">see</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> TREEIFY_THRESHOLD</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> And</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> when</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> they</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> become</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> too</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> small</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (due </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">to</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">removal</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> or</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> resizing</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) they are converted back to plain bins.  In</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">usages</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> with</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> well-distributed</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> user</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> hashCodes,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> tree</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> bins</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> are</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">rarely</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> used.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">  Ideally,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> under</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> random</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> hashCodes,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> the</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> frequency</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> of</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">nodes</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> in</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> bins</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> follows</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> a</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Poisson</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> distribution</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>构造方法</h3>\n<p><code>LinkedHashMap</code> 构造方法有 4 个实现也比较简单，直接调用父类即 <code>HashMap</code> 的构造方法完成初始化。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> LinkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    super</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    accessOrder </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> LinkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> initialCapacity) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    super</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(initialCapacity)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    accessOrder </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> LinkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> initialCapacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> loadFactor) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    super</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(initialCapacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> loadFactor)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    accessOrder </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> LinkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> initialCapacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> loadFactor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    boolean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> accessOrder) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    super</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(initialCapacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> loadFactor)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">accessOrder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> accessOrder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>我们上面也提到了，默认情况下 <code>accessOrder</code> 为 false，如果我们要让 <code>LinkedHashMap</code> 实现键值对按照访问顺序排序(即将最近未访问的元素排在链表首部、最近访问的元素移动到链表尾部)，需要调用第 4 个构造方法将 <code>accessOrder</code> 设置为 true。</p>\n<h3>get 方法</h3>\n<p><code>get</code> 方法是 <code>LinkedHashMap</code> 增删改查操作中唯一一个重写的方法， <code>accessOrder</code> 为 true 的情况下， 它会在元素查询完成之后，将当前访问的元素移到链表的末尾。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">     Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> ></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //获取key的键值对,若为空直接返回</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> ((e </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">hash</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(key)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key)) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">         return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //若accessOrder为true，则调用afterNodeAccess将当前元素移到链表末尾</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (accessOrder)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">         afterNodeAccess</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //返回键值对的值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>从源码可以看出，<code>get</code> 的执行步骤非常简单:</p>\n<ol>\n<li>调用父类即 <code>HashMap</code> 的 <code>getNode</code> 获取键值对，若为空则直接返回。</li>\n<li>判断 <code>accessOrder</code> 是否为 true，若为 true 则说明需要保证 <code>LinkedHashMap</code> 的链表访问有序性，执行步骤 3。</li>\n<li>调用 <code>LinkedHashMap</code> 重写的 <code>afterNodeAccess</code> 将当前元素添加到链表末尾。</li>\n</ol>\n<p>关键点在于 <code>afterNodeAccess</code> 方法的实现，这个方法负责将元素移动到链表末尾。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> afterNodeAccess</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> V </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) { </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// move node to last</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    LinkedHashMap</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> ></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> last</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //如果accessOrder 且当前节点不为链表尾节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (accessOrder </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x26;&#x26;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (last </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> tail) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //获取当前节点、以及前驱节点和后继节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        LinkedHashMap</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> ></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">LinkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> V </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> ) e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">before</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">after</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //将当前节点的后继节点指针指向空，使其和后继节点断开联系</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">after</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //如果前驱节点为空，则说明当前节点是链表的首节点，故将后继节点设置为首节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (b </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            head </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //如果前驱节点不为空，则让前驱节点指向后继节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">after</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //如果后继节点不为空，则让后继节点指向前驱节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (a </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">before</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //如果后继节点为空，则说明当前节点在链表最末尾，直接让last 指向前驱节点,这个 else其实 没有意义，因为最开头if已经确保了p不是尾结点了，自然after不会是null</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            last </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //如果last为空，则说明当前链表只有一个节点p，则将head指向p</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (last </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            head </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //反之让p的前驱指针指向尾节点，再让尾节点的前驱指针指向p</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">before</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> last</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            last</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">after</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //tail指向p，自此将节点p移动到链表末尾</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        tail </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        ++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">modCount</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>从源码可以看出， <code>afterNodeAccess</code> 方法完成了下面这些操作:</p>\n<ol>\n<li>如果 <code>accessOrder</code> 为 true 且链表尾部不为当前节点 p，我们则需要将当前节点移到链表尾部。</li>\n<li>获取当前节点 p、以及它的前驱节点 b 和后继节点 a。</li>\n<li>将当前节点 p 的后继指针设置为 null，使其和后继节点 p 断开联系。</li>\n<li>尝试将前驱节点指向后继节点，若前驱节点为空，则说明当前节点 p 就是链表首节点，故直接将后继节点 a 设置为首节点，随后我们再将 p 追加到 a 的末尾。</li>\n<li>再尝试让后继节点 a 指向前驱节点 b。</li>\n<li>上述操作让前驱节点和后继节点完成关联，并将当前节点 p 独立出来，这一步则是将当前节点 p 追加到链表末端，如果链表末端为空，则说明当前链表只有一个节点 p，所以直接让 head 指向 p 即可。</li>\n<li>上述操作已经将 p 成功到达链表末端，最后我们将 tail 指针即指向链表末端的指针指向 p 即可。</li>\n</ol>\n<p>可以结合这张图理解，展示了 key 为 13 的元素被移动到了链表尾部。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/linkedhashmap-get.png\" alt=\"LinkedHashMap 移动元素 13 到链表尾部\"></p>\n<p>看不太懂也没关系，知道这个方法的作用就够了，后续有时间再慢慢消化。</p>\n<h3>newNode——新节点尾插链表</h3>\n<p>上文介绍了 <code>afterNodeAccess</code> 如何将<strong>已存在的节点</strong>移动到链表尾部，那么<strong>新插入的节点</strong>是如何被添加到链表中的呢？</p>\n<p>答案在于 <code>LinkedHashMap</code> 重写了 <code>HashMap</code> 的 <code>newNode</code> 方法。当 <code>HashMap</code> 插入新键值对时，会调用 <code>newNode</code> 创建节点对象，<code>LinkedHashMap</code> 在重写的方法中不仅创建了 <code>Entry</code> 节点，还额外调用了 <code>linkNodeLast</code> 将其链接到双向链表的尾部：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// HashMap 的 newNode 是普通实现</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> hash</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> next) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(hash</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> next)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// LinkedHashMap 重写 newNode，额外调用 linkNodeLast</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> hash</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    LinkedHashMap</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(hash</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    linkNodeLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(p)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 关键：将新节点链接到链表尾部</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>linkNodeLast</code> 方法的实现如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 将节点链接到双向链表尾部</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> linkNodeLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">LinkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> p) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    LinkedHashMap</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> last </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> tail</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    tail </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // tail 指向新节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (last </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        head </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 链表为空，head 也指向新节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">before</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> last</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 新节点的前驱指向原尾节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        last</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">after</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   // 原尾节点的后继指向新节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>这就是 LinkedHashMap 实现插入有序的核心机制</strong>：每次插入新节点时，通过重写 <code>newNode</code> 并调用 <code>linkNodeLast</code>，将新节点追加到双向链表尾部。这样遍历时从头节点 <code>head</code> 开始沿着 <code>after</code> 指针遍历，就能按插入顺序获取所有元素。</p>\n<p>同理，<code>LinkedHashMap</code> 也重写了 <code>newTreeNode</code> 方法，确保树节点插入时同样会被链接到链表尾部：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">TreeNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> newTreeNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> hash</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> next) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    TreeNode</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> TreeNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(hash</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> next)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    linkNodeLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(p)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>remove 方法后置操作——afterNodeRemoval</h3>\n<p><code>LinkedHashMap</code> 并没有对 <code>remove</code> 方法进行重写，而是直接继承 <code>HashMap</code> 的 <code>remove</code> 方法，为了保证键值对移除后双向链表中的节点也会同步被移除，<code>LinkedHashMap</code> 重写了 <code>HashMap</code> 的空实现方法 <code>afterNodeRemoval</code>。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> removeNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> hash</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                               boolean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> matchValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> movable) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //略</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (node </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">matchValue </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">||</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (v </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">||</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                                 (value </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">equals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(v)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">))) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (node </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> TreeNode)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                    ((</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">TreeNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)node)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">removeTreeNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, tab, movable);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (node </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> p)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                    tab[index] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                    p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                ++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">modCount</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                --</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                //HashMap的removeNode完成元素移除后会调用afterNodeRemoval进行移除后置操作</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">                afterNodeRemoval</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(node)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//空实现</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> afterNodeRemoval</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> p) { }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>我们可以看到从 <code>HashMap</code> 继承来的 <code>remove</code> 方法内部调用的 <code>removeNode</code> 方法将节点从 bucket 删除后，调用了 <code>afterNodeRemoval</code>。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> afterNodeRemoval</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) { </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// unlink</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //获取当前节点p、以及e的前驱节点b和后继节点a</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        LinkedHashMap</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> p </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">LinkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">before</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">after</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //将p的前驱和后继指针都设置为null，使其和前驱、后继节点断开联系</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">before</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> p</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">after</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //如果前驱节点为空，则说明当前节点p是链表首节点，让head指针指向后继节点a即可</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (b </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            head </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //如果前驱节点b不为空，则让b直接指向后继节点a</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">after</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //如果后继节点为空，则说明当前节点p在链表末端，所以直接让tail指针指向前驱节点a即可</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (a </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            tail </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //反之后继节点的前驱指针直接指向前驱节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">before</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>从源码可以看出， <code>afterNodeRemoval</code> 方法的整体操作就是让当前节点 p 和前驱节点、后继节点断开联系，等待 gc 回收，整体步骤为:</p>\n<ol>\n<li>获取当前节点 p、以及 p 的前驱节点 b 和后继节点 a。</li>\n<li>让当前节点 p 和其前驱、后继节点断开联系。</li>\n<li>尝试让前驱节点 b 指向后继节点 a，若 b 为空则说明当前节点 p 在链表首部，我们直接将 head 指向后继节点 a 即可。</li>\n<li>尝试让后继节点 a 指向前驱节点 b，若 a 为空则说明当前节点 p 在链表末端，所以直接让 tail 指针指向前驱节点 b 即可。</li>\n</ol>\n<p>可以结合这张图理解，展示了 key 为 13 的元素被删除，也就是从链表中移除了这个元素。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/linkedhashmap-remove.png\" alt=\"LinkedHashMap 删除元素 13\"></p>\n<p>看不太懂也没关系，知道这个方法的作用就够了，后续有时间再慢慢消化。</p>\n<h3>put 方法后置操作——afterNodeInsertion</h3>\n<p>同样的 <code>LinkedHashMap</code> 并没有实现插入方法，而是直接继承 <code>HashMap</code> 的所有插入方法交由用户使用，但为了维护双向链表访问的有序性，它做了这样两件事:</p>\n<ol>\n<li>重写 <code>afterNodeAccess</code>(上文提到过),如果当前被插入的 key 已存在与 <code>map</code> 中，因为 <code>LinkedHashMap</code> 的插入操作会将新节点追加至链表末尾，所以对于存在的 key 则调用 <code>afterNodeAccess</code> 将其放到链表末端。</li>\n<li>重写了 <code>HashMap</code> 的 <code>afterNodeInsertion</code> 方法，当 <code>removeEldestEntry</code> 返回 true 时，会将链表首节点移除。</li>\n</ol>\n<p>这一点我们可以在 <code>HashMap</code> 的插入操作核心方法 <code>putVal</code> 中看到。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> putVal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> hash</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> onlyIfAbsent</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                   boolean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> evict) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">          //略</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (e </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) { </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// existing mapping for key</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">                V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> oldValue </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">onlyIfAbsent </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">||</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> oldValue </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                    e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                 //如果当前的key在map中存在，则调用afterNodeAccess</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">                afterNodeAccess</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> oldValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        ++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">modCount</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">size </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> threshold)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">            resize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">         //调用插入后置方法，该方法被LinkedHashMap重写</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        afterNodeInsertion</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(evict)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>上述步骤的源码上文已经解释过了，所以这里我们着重了解一下 <code>afterNodeInsertion</code> 的工作流程，假设我们的重写了 <code>removeEldestEntry</code>，当链表 <code>size</code> 超过 <code>capacity</code> 时，就返回 true。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 判断size超过容量时返回true，告知LinkedHashMap移除最老的缓存项(即链表的第一个元素)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">protected</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> removeEldestEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> V </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> eldest) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> capacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>以下图为例，假设笔者最后新插入了一个不存在的节点 19,假设 <code>capacity</code> 为 4，所以 <code>removeEldestEntry</code> 返回 true，我们要将链表首节点移除。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/linkedhashmap-after-insert-1.png\" alt=\"LinkedHashMap 中插入新元素 19\"></p>\n<p>移除的步骤很简单，查看链表首节点是否存在，若存在则断开首节点和后继节点的关系，并让首节点指针指向下一节点，所以 head 指针指向了 12，节点 10 成为没有任何引用指向的空对象，等待 GC。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/linkedhashmap-after-insert-2.png\" alt=\"LinkedHashMap 中插入新元素 19\"></p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> afterNodeInsertion</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">boolean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> evict) { </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// possibly remove eldest</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        LinkedHashMap</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">K</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> first</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //如果evict为true且队首元素不为空以及removeEldestEntry返回true，则说明我们需要最老的元素(即在链表首部的元素)移除。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (evict </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x26;&#x26;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (first </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> head) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> removeEldestEntry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(first)) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">          //获取链表首部的键值对的key</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            K</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> key </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> first</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //调用removeNode将元素从HashMap的bucket中移除，并和LinkedHashMap的双向链表断开，等待gc回收</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">            removeNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">hash</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(key)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>从源码可以看出， <code>afterNodeInsertion</code> 方法完成了下面这些操作:</p>\n<ol>\n<li>判断 <code>eldest</code> 是否为 true，只有为 true 才能说明可能需要将最年长的键值对(即链表首部的元素)进行移除，具体是否具体要进行移除，还得确定链表是否为空<code>((first = head) != null)</code>，以及 <code>removeEldestEntry</code> 方法是否返回 true，只有这两个方法返回 true 才能确定当前链表不为空，且链表需要进行移除操作了。</li>\n<li>获取链表第一个元素的 key。</li>\n<li>调用 <code>HashMap</code> 的 <code>removeNode</code> 方法，该方法我们上文提到过，它会将节点从 <code>HashMap</code> 的 bucket 中移除，并且 <code>LinkedHashMap</code> 还重写了 <code>removeNode</code> 中的 <code>afterNodeRemoval</code> 方法，所以这一步将通过调用 <code>removeNode</code> 将元素从 <code>HashMap</code> 的 bucket 中移除，并和 <code>LinkedHashMap</code> 的双向链表断开，等待 gc 回收。</li>\n</ol>\n<h2>LinkedHashMap 和 HashMap 遍历性能比较</h2>\n<p><code>LinkedHashMap</code> 维护了一个双向链表来记录数据插入的顺序，因此在迭代遍历生成的迭代器的时候，是按照双向链表的路径进行遍历的。这一点相比于 <code>HashMap</code> 那种遍历整个 bucket 的方式来说，高效许多。</p>\n<p>这一点我们可以从两者的迭代器中得以印证，先来看看 <code>HashMap</code> 的迭代器，可以看到 <code>HashMap</code> 迭代键值对时会用到一个 <code>nextNode</code> 方法，该方法会返回 next 指向的下一个元素，并会从 next 开始遍历 bucket 找到下一个 bucket 中不为空的元素 Node。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> EntryIterator</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> HashIterator</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Iterator</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Map</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\"> >></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">     V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">         return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> nextNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">     }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> //获取下一个Node</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> V </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> nextNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">     Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> ></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> [] t</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //获取下一个元素next</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">     Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> ></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> e </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (modCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> expectedModCount)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">         throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ConcurrentModificationException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (e </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">         throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> NoSuchElementException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //将next指向bucket中下一个不为空的Node</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> ((next </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (current </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (t </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> table) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">         do</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {} </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (index </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> t</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (next </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> t[index</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">]) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">     }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>相比之下 <code>LinkedHashMap</code> 的迭代器则是直接使用通过 <code>after</code> 指针快速定位到当前节点的后继节点，简洁高效许多。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedEntryIterator</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedHashIterator</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Iterator</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Map</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\"> >></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Map</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">     V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">         return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> nextNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">     }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> //获取下一个Node</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> LinkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> K</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> V </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> nextNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //获取下一个节点next</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">     LinkedHashMap</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Entry</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> K</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> ></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> e </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (modCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> expectedModCount)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">         throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ConcurrentModificationException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (e </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">         throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> NoSuchElementException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //current 指针指向当前节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">     current </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //next直接当前节点的after指针快速定位到下一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">     next </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">after</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>为了验证笔者所说的观点，笔者对这两个容器进行了压测，测试插入 1000w 和迭代 1000w 条数据的耗时，代码如下:</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> count </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1000_0000</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Map</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> hashMap </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> HashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Map</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> linkedHashMap </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> start</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> end</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">start </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> count</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    hashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ThreadLocalRandom</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">current</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">nextInt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, count), </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ThreadLocalRandom</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">current</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">nextInt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, count));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">end </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"map time putVal: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (end </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> start));</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">start </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> count</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    linkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ThreadLocalRandom</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">current</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">nextInt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, count), </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ThreadLocalRandom</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">current</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">nextInt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, count));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">end </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"linkedHashMap putVal time: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (end </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> start));</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">start </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> num </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> v </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> hashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">values</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    num </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> num </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> v</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">end </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"map get time: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (end </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> start));</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">start </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> v </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> linkedHashMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">values</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    num </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> num </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> v</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">end </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"linkedHashMap get time: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (end </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> start));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(num);</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>从输出结果来看，因为 <code>LinkedHashMap</code> 需要维护双向链表的缘故，插入元素相较于 <code>HashMap</code> 会更耗时，但是有了双向链表明确的前后节点关系，迭代效率相对于前者高效了许多。不过，总体来说却别不大，毕竟数据量这么庞大。</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">map</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> time</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> putVal:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 5880</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">linkedHashMap</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> putVal</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> time:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 7567</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">map</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> get</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> time:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 143</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">linkedHashMap</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> get</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> time:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 67</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">63208969074998</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>LinkedHashMap 常见面试题</h2>\n<h3>什么是 LinkedHashMap？</h3>\n<p><code>LinkedHashMap</code> 是 Java 集合框架中 <code>HashMap</code> 的一个子类，它继承了 <code>HashMap</code> 的所有属性和方法，并且在 <code>HashMap</code> 的基础重写了 <code>afterNodeRemoval</code>、<code>afterNodeInsertion</code>、<code>afterNodeAccess</code> 方法。使之拥有顺序插入和访问有序的特性。</p>\n<h3>LinkedHashMap 如何按照插入顺序迭代元素？</h3>\n<p><code>LinkedHashMap</code> 按照插入顺序迭代元素是它的默认行为。<code>LinkedHashMap</code> 内部维护了一个双向链表，用于记录元素的插入顺序。因此，当使用迭代器迭代元素时，元素的顺序与它们最初插入的顺序相同。</p>\n<h3>LinkedHashMap 如何按照访问顺序迭代元素？</h3>\n<p><code>LinkedHashMap</code> 可以通过构造函数中的 <code>accessOrder</code> 参数指定按照访问顺序迭代元素。当 <code>accessOrder</code> 为 true 时，每次访问一个元素时，该元素会被移动到链表的末尾，因此下次访问该元素时，它就会成为链表中的最后一个元素，从而实现按照访问顺序迭代元素。</p>\n<h3>LinkedHashMap 如何实现 LRU 缓存？</h3>\n<p>将 <code>accessOrder</code> 设置为 true 并重写 <code>removeEldestEntry</code> 方法当链表大小超过容量时返回 true，使得每次访问一个元素时，该元素会被移动到链表的末尾。一旦插入操作让 <code>removeEldestEntry</code> 返回 true 时，视为缓存已满，<code>LinkedHashMap</code> 就会将链表首元素移除，由此我们就能实现一个 LRU 缓存。</p>\n<h3>LinkedHashMap 和 HashMap 有什么区别？</h3>\n<p><code>LinkedHashMap</code> 和 <code>HashMap</code> 都是 Java 集合框架中的 Map 接口的实现类。它们的最大区别在于迭代元素的顺序。<code>HashMap</code> 迭代元素的顺序是不确定的，而 <code>LinkedHashMap</code> 提供了按照插入顺序或访问顺序迭代元素的功能。此外，<code>LinkedHashMap</code> 内部维护了一个双向链表，用于记录元素的插入顺序或访问顺序，而 <code>HashMap</code> 则没有这个链表。因此，<code>LinkedHashMap</code> 的插入性能可能会比 <code>HashMap</code> 略低，但它提供了更多的功能并且迭代效率相较于 <code>HashMap</code> 更加高效。</p>\n<h2>参考文献</h2>\n<ul>\n<li>LinkedHashMap 源码详细分析（JDK1.8）:<a href=\"https://www.imooc.com/article/22931\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.imooc.com/article/22931</a></li>\n<li>HashMap 与 LinkedHashMap:<a href=\"https://www.cnblogs.com/Spground/p/8536148.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.cnblogs.com/Spground/p/8536148.html</a></li>\n<li>源于 LinkedHashMap 源码: <a href=\"https://leetcode.cn/problems/lru-cache/solution/yuan-yu-linkedhashmapyuan-ma-by-jeromememory/\" target=\"_blank\" rel=\"noopener noreferrer\">https://leetcode.cn/problems/lru-cache/solution/yuan-yu-linkedhashmapyuan-ma-by-jeromememory/</a></li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/collection/linkhashmap-structure-overview.png",
      "date_published": "2023-07-11T13:02:50.000Z",
      "date_modified": "2026-03-11T02:51:59.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "DelayQueue 源码分析",
      "url": "https://javaguide.cn/java/collection/delayqueue-source-code.html",
      "id": "https://javaguide.cn/java/collection/delayqueue-source-code.html",
      "summary": "DelayQueue源码深度解析：详解延迟队列实现原理、Delayed接口使用、延时任务调度、订单超时取消等应用场景、基于PriorityQueue的线程安全设计。",
      "content_html": "<h2>DelayQueue 简介</h2>\n<p><code>DelayQueue</code> 是 JUC 包(<code>java.util.concurrent)</code>为我们提供的延迟队列，用于实现延时任务比如订单下单 15 分钟未支付直接取消。它是 <code>BlockingQueue</code> 的一种，底层是一个基于 <code>PriorityQueue</code> 实现的一个无界队列，是线程安全的。关于<code>PriorityQueue</code>可以参考笔者编写的这篇文章：<a href=\"/java/collection/priorityqueue-source-code.html\" target=\"_blank\">PriorityQueue 源码分析</a> 。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/blocking-queue-hierarchy.png\" alt=\"BlockingQueue 的实现类\"></p>\n<p><code>DelayQueue</code> 中存放的元素必须实现 <code>Delayed</code> 接口，并且需要重写 <code>getDelay()</code>方法（计算是否到期）。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> interface</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Delayed</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Comparable</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Delayed</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    long</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getDelay</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> unit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>默认情况下, <code>DelayQueue</code> 会按照到期时间升序编排任务。只有当元素过期时（<code>getDelay()</code>方法返回值小于等于 0），才能从队列中取出。</p>\n<h2>DelayQueue 发展史</h2>\n<ul>\n<li><code>DelayQueue</code> 最早是在 Java 5 中引入的，作为 <code>java.util.concurrent</code> 包中的一部分，用于支持基于时间的任务调度和缓存过期删除等场景，该版本仅仅支持延迟功能的实现，还未解决线程安全问题。</li>\n<li>在 Java 6 中，<code>DelayQueue</code> 的实现进行了优化，通过使用 <code>ReentrantLock</code> 和 <code>Condition</code> 解决线程安全及线程间交互的效率，提高了其性能和可靠性。</li>\n<li>在 Java 7 中，<code>DelayQueue</code> 的实现进行了进一步的优化，通过使用 CAS 操作实现元素的添加和移除操作，提高了其并发操作性能。</li>\n<li>在 Java 8 中，<code>DelayQueue</code> 的实现没有进行重大变化，但是在 <code>java.time</code> 包中引入了新的时间类，如 <code>Duration</code> 和 <code>Instant</code>，使得使用 <code>DelayQueue</code> 进行基于时间的调度更加方便和灵活。</li>\n<li>在 Java 9 中，<code>DelayQueue</code> 的实现进行了一些微小的改进，主要是对代码进行了一些优化和精简。</li>\n</ul>\n<p>总的来说，<code>DelayQueue</code> 的发展史主要是通过优化其实现方式和提高其性能和可靠性，使其更加适用于基于时间的调度和缓存过期删除等场景。</p>\n<h2>DelayQueue 常见使用场景示例</h2>\n<p>我们这里希望任务可以按照我们预期的时间执行，例如提交 3 个任务，分别要求 1s、2s、3s 后执行，即使是乱序添加，1s 后要求 1s 执行的任务会准时执行。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/delayed-task.png\" alt=\"延迟任务\"></p>\n<p>对此我们可以使用 <code>DelayQueue</code> 来实现,所以我们首先需要继承 <code>Delayed</code> 实现 <code>DelayedTask</code>，实现 <code>getDelay</code> 方法以及优先级比较 <code>compareTo</code>。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 延迟任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> DelayedTask</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Delayed</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 任务到期时间</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> executeTime</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Runnable</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> task</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> DelayedTask</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> delay</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Runnable</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> task</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">executeTime</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> delay;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">task</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> task;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 查看当前任务还有多久到期</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * </span><span style=\"--shiki-light:#A626A4;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic\">@param</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> unit</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * </span><span style=\"--shiki-light:#A626A4;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic\">@return</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getDelay</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> unit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> unit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">convert</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(executeTime </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(), </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">MILLISECONDS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    /**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * 延迟队列需要到期时间升序入队，所以我们需要实现compareTo进行到期时间比较</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * </span><span style=\"--shiki-light:#A626A4;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic\">@param</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> o</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     * </span><span style=\"--shiki-light:#A626A4;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic\">@return</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> compareTo</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Delayed</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">compare</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">executeTime</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, ((DelayedTask) o).</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">executeTime</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> execute</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        task</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>完成任务的封装之后，使用就很简单了，设置好多久到期然后将任务提交到延迟队列中即可。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建延迟队列，并添加任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">DelayQueue</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> DelayedTask</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\"> ></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> delayQueue </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> DelayQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> &#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> ()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//分别添加1s、2s、3s到期的任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">delayQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> DelayedTask</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2000</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, () </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Task 2\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">delayQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> DelayedTask</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1000</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, () </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Task 1\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">delayQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> DelayedTask</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3000</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, () </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Task 3\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)));</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 取出任务并执行</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">delayQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isEmpty</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //阻塞获取最先到期的任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  DelayedTask</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> task </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> delayQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">take</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (task </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    task</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">execute</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>从输出结果可以看出，即使笔者先提到 2s 到期的任务，1s 到期的任务 Task1 还是优先执行的。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">Task </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">Task </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">Task </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>DelayQueue 源码解析</h2>\n<p>这里以 JDK1.8 为例，分析一下 <code>DelayQueue</code> 的底层核心源码。</p>\n<p><code>DelayQueue</code> 的类定义如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> DelayQueue</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Delayed</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AbstractQueue</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> BlockingQueue</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">{</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>DelayQueue</code> 继承了 <code>AbstractQueue</code> 类，实现了 <code>BlockingQueue</code> 接口。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/delayqueue-class-diagram.png\" alt=\"DelayQueue类图\"></p>\n<h3>核心成员变量</h3>\n<p><code>DelayQueue</code> 的 4 个核心成员变量如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//可重入锁，实现线程安全的关键</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> transient</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ReentrantLock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//延迟队列底层存储数据的集合,确保元素按照到期时间升序排列</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> PriorityQueue</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> q </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> PriorityQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//指向准备执行优先级最高的线程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> leader </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//实现多线程之间等待唤醒的交互</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Condition</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> available </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newCondition</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><ul>\n<li><code>lock</code> : 我们都知道 <code>DelayQueue</code> 存取是线程安全的，所以为了保证存取元素时线程安全，我们就需要在存取时上锁，而 <code>DelayQueue</code> 就是基于 <code>ReentrantLock</code> 独占锁确保存取操作的线程安全。</li>\n<li><code>q</code> : 延迟队列要求元素按照到期时间进行升序排列，所以元素添加时势必需要进行优先级排序,所以 <code>DelayQueue</code> 底层元素的存取都是通过这个优先队列 <code>PriorityQueue</code> 的成员变量 <code>q</code> 来管理的。</li>\n<li><code>leader</code> : 延迟队列的任务只有到期之后才会执行,对于没有到期的任务只有等待,为了确保优先级最高的任务到期后可以即刻被执行,设计者就用 <code>leader</code> 来管理延迟任务，只有 <code>leader</code> 所指向的线程才具备定时等待任务到期执行的权限，而其他那些优先级低的任务只能无限期等待，直到 <code>leader</code> 线程执行完手头的延迟任务后唤醒它。</li>\n<li><code>available</code> : 上文讲述 <code>leader</code> 线程时提到的等待唤醒操作的交互就是通过 <code>available</code> 实现的，假如线程 1 尝试在空的 <code>DelayQueue</code> 获取任务时，<code>available</code> 就会将其放入等待队列中。直到有一个线程添加一个延迟任务后通过 <code>available</code> 的 <code>signal</code> 方法将其唤醒。</li>\n</ul>\n<h3>构造方法</h3>\n<p>相较于其他的并发容器，延迟队列的构造方法比较简单，它只有两个构造方法，因为所有成员变量在类加载时都已经初始完成了，所以默认构造方法什么也没做。还有一个传入 <code>Collection</code> 对象的构造方法，它会将调用 <code>addAll()</code>方法将集合元素存到优先队列 <code>q</code> 中。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> DelayQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> DelayQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Collection</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> extends E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">addAll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(c);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>添加元素</h3>\n<p><code>DelayQueue</code> 添加元素的方法无论是 <code>add</code>、<code>put</code> 还是 <code>offer</code>,本质上就是调用一下 <code>offer</code> ,所以了解延迟队列的添加逻辑我们只需阅读 offer 方法即可。</p>\n<p><code>offer</code> 方法的整体逻辑为:</p>\n<ol>\n<li>尝试获取 <code>lock</code> 。</li>\n<li>如果上锁成功,则调 <code>q</code> 的 <code>offer</code> 方法将元素存放到优先队列中。</li>\n<li>调用 <code>peek</code> 方法看看当前队首元素是否就是本次入队的元素,如果是则说明当前这个元素是即将到期的任务(即优先级最高的元素)，于是将 <code>leader</code> 设置为空,通知因为队列为空时调用 <code>take</code> 等方法导致阻塞的线程来争抢元素。</li>\n<li>上述步骤执行完成，释放 <code>lock</code>。</li>\n<li>返回 true。</li>\n</ol>\n<p>源码如下，笔者已详细注释，读者可自行参阅:</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> offer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //尝试获取lock</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //如果上锁成功,则调q的offer方法将元素存放到优先队列中</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        q</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">offer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(e);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //调用peek方法看看当前队首元素是否就是本次入队的元素,如果是则说明当前这个元素是即将到期的任务(即优先级最高的元素)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">q</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">peek</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ==</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //将leader设置为空,通知调用取元素方法而阻塞的线程来争抢这个任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            leader </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            available</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">signal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //上述步骤执行完成，释放lock</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>获取元素</h3>\n<p><code>DelayQueue</code> 中获取元素的方式分为阻塞式和非阻塞式，先来看看逻辑比较复杂的阻塞式获取元素方法 <code>take</code>,为了让读者可以更直观的了解阻塞式获取元素的全流程，笔者将以 3 个线程并发获取元素为例讲述 <code>take</code> 的工作流程。</p>\n<blockquote>\n<p>想要理解下面的内容，需要用到 AQS 相关的知识，推荐阅读下面这两篇文章：</p>\n<ul>\n<li><a href=\"https://xie.infoq.cn/article/5a3cc0b709012d40cb9f41986\" target=\"_blank\" rel=\"noopener noreferrer\">图文讲解 AQS ，一起看看 AQS 的源码……(图文较长)</a></li>\n<li><a href=\"https://xie.infoq.cn/article/0223d5e5f19726b36b084b10d\" target=\"_blank\" rel=\"noopener noreferrer\">AQS 都看完了，Condition 原理可不能少！</a></li>\n</ul>\n</blockquote>\n<p>1、首先， 3 个线程会尝试获取可重入锁 <code>lock</code>,假设我们现在有 3 个线程分别是 t1、t2、t3,随后 t1 得到了锁，而 t2、t3 没有抢到锁，故将这两个线程存入等待队列中。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/delayqueue-take-0.png\" alt></p>\n<p>2、紧接着 t1 开始进行元素获取的逻辑。</p>\n<p>3、线程 t1 首先会查看 <code>DelayQueue</code> 队列首元素是否为空。</p>\n<p>4、如果元素为空，则说明当前队列没有任何元素，故 t1 就会被阻塞存到 <code>conditionWaiter</code> 这个队列中。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/delayqueue-take-1.png\" alt></p>\n<p>注意，调用 <code>await</code> 之后 t1 就会释放 <code>lcok</code> 锁，假如 <code>DelayQueue</code> 持续为空，那么 t2、t3 也会像 t1 一样执行相同的逻辑并进入 <code>conditionWaiter</code> 队列中。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/delayqueue-take-2.png\" alt></p>\n<p>如果元素不为空，则判断当前任务是否到期，如果元素到期，则直接返回出去。如果元素未到期，则判断当前 <code>leader</code> 线程(<code>DelayQueue</code> 中唯一一个可以等待并获取元素的线程引用)是否为空，若不为空，则说明当前 <code>leader</code> 正在等待执行一个优先级比当前元素还高的元素到期，故当前线程 t1 只能调用 <code>await</code> 进入无限期等待，等到 <code>leader</code> 取得元素后唤醒。反之，若 <code>leader</code> 线程为空，则将当前线程设置为 leader 并进入有限期等待,到期后取出元素并返回。</p>\n<p>自此我们阻塞式获取元素的逻辑都已完成后,源码如下，读者可自行参阅:</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> take</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() throws InterruptedException {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 尝试获取可重入锁,将底层AQS的state设置为1,并设置为独占锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lockInterruptibly</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //查看队列第一个元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> first </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> q</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">peek</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //若为空,则将当前线程放入ConditionObject的等待队列中，并将底层AQS的state设置为0，表示释放锁并进入无限期等待</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (first </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                available</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">await</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                //若元素不为空，则查看当前元素多久到期</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                long</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> delay </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> first</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getDelay</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(NANOSECONDS);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                //如果小于0则说明已到期直接返回出去</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (delay </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> q</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">poll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                //如果大于0则说明任务还没到期，首先需要释放对这个元素的引用</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                first </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // don't retain ref while waiting</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                //判断leader是否为空，如果不为空，则说明正有线程作为leader并等待一个任务到期，则当前线程进入无限期等待</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (leader </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                    available</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">await</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                    //反之将我们的线程成为leader</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">                    Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> thisThread </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                    leader </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> thisThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                        //并进入有限期等待</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                        available</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">awaitNanos</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(delay);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                        //等待任务到期时，释放leader引用，进入下一次循环将任务return出去</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (leader </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> thisThread)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                            leader </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 收尾逻辑:当leader为null，并且队列中有任务时，唤醒等待的获取元素的线程。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (leader </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> &#x26;&#x26;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> q</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">peek</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> !=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            available</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">signal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //释放锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>我们再来看看非阻塞的获取元素方法 <code>poll</code> ，逻辑比较简单，整体步骤如下:</p>\n<ol>\n<li>尝试获取可重入锁。</li>\n<li>查看队列第一个元素,判断元素是否为空。</li>\n<li>若元素为空，或者元素未到期，则直接返回空。</li>\n<li>若元素不为空且到期了，直接调用 <code>poll</code> 返回出去。</li>\n<li>释放可重入锁 <code>lock</code> 。</li>\n</ol>\n<p>源码如下,读者可自行参阅源码及注释:</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> poll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //尝试获取可重入锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //查看队列第一个元素,判断元素是否为空</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> first </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> q</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">peek</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //若元素为空，或者元素未到期，则直接返回空</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (first </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ||</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> first</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getDelay</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(NANOSECONDS)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //若元素不为空且到期了，直接调用poll返回出去</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> q</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">poll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //释放可重入锁lock</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>查看元素</h3>\n<p>上文获取元素时都会调用到 <code>peek</code> 方法，peek 顾名思义仅仅窥探一下队列中的元素，它的步骤就 4 步:</p>\n<ol>\n<li>上锁。</li>\n<li>调用优先队列 q 的 peek 方法查看索引 0 位置的元素。</li>\n<li>释放锁。</li>\n<li>将元素返回出去。</li>\n</ol>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> peek</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> q</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">peek</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>DelayQueue 常见面试题</h2>\n<h3>DelayQueue 的实现原理是什么？</h3>\n<p><code>DelayQueue</code> 底层是使用优先队列 <code>PriorityQueue</code> 来存储元素，而 <code>PriorityQueue</code> 采用二叉小顶堆的思想确保值小的元素排在最前面，这就使得 <code>DelayQueue</code> 对于延迟任务优先级的管理就变得十分方便了。同时 <code>DelayQueue</code> 为了保证线程安全还用到了可重入锁 <code>ReentrantLock</code>,确保单位时间内只有一个线程可以操作延迟队列。最后，为了实现多线程之间等待和唤醒的交互效率，<code>DelayQueue</code> 还用到了 <code>Condition</code>，通过 <code>Condition</code> 的 <code>await</code> 和 <code>signal</code> 方法完成多线程之间的等待唤醒。</p>\n<h3>DelayQueue 的实现是否线程安全？</h3>\n<p><code>DelayQueue</code> 的实现是线程安全的，它通过 <code>ReentrantLock</code> 实现了互斥访问和 <code>Condition</code> 实现了线程间的等待和唤醒操作，可以保证多线程环境下的安全性和可靠性。</p>\n<h3>DelayQueue 的使用场景有哪些？</h3>\n<p><code>DelayQueue</code> 通常用于实现定时任务调度和缓存过期删除等场景。在定时任务调度中，需要将需要执行的任务封装成延迟任务对象，并将其添加到 <code>DelayQueue</code> 中，<code>DelayQueue</code> 会自动按照剩余延迟时间进行升序排序(默认情况)，以保证任务能够按照时间先后顺序执行。对于缓存过期这个场景而言，在数据被缓存到内存之后，我们可以将缓存的 key 封装成一个延迟的删除任务，并将其添加到 <code>DelayQueue</code> 中，当数据过期时，拿到这个任务的 key，将这个 key 从内存中移除。</p>\n<h3>DelayQueue 中 Delayed 接口的作用是什么？</h3>\n<p><code>Delayed</code> 接口定义了元素的剩余延迟时间(<code>getDelay</code>)和元素之间的比较规则(该接口继承了 <code>Comparable</code> 接口)。若希望元素能够存放到 <code>DelayQueue</code> 中，就必须实现 <code>Delayed</code> 接口的 <code>getDelay()</code> 方法和 <code>compareTo()</code> 方法，否则 <code>DelayQueue</code> 无法得知当前任务剩余时长和任务优先级的比较。</p>\n<h3>DelayQueue 和 Timer/TimerTask 的区别是什么？</h3>\n<p><code>DelayQueue</code> 和 <code>Timer/TimerTask</code> 都可以用于实现定时任务调度，但是它们的实现方式不同。<code>DelayQueue</code> 是基于优先级队列和堆排序算法实现的，可以实现多个任务按照时间先后顺序执行；而 <code>Timer/TimerTask</code> 是基于单线程实现的，只能按照任务的执行顺序依次执行，如果某个任务执行时间过长，会影响其他任务的执行。另外，<code>DelayQueue</code> 还支持动态添加和移除任务，而 <code>Timer/TimerTask</code> 只能在创建时指定任务。</p>\n<h2>参考文献</h2>\n<ul>\n<li>《深入理解高并发编程：JDK 核心技术》:</li>\n<li>一口气说出 Java 6 种延时队列的实现方法(面试官也得服):<a href=\"https://www.jb51.net/article/186192.htm\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.jb51.net/article/186192.htm</a></li>\n<li>图解 DelayQueue 源码（java 8）——延时队列的小九九: <a href=\"https://blog.csdn.net/every__day/article/details/113810985\" target=\"_blank\" rel=\"noopener noreferrer\">https://blog.csdn.net/every__day/article/details/113810985</a></li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/collection/blocking-queue-hierarchy.png",
      "date_published": "2023-06-30T11:46:59.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "常见加密算法总结",
      "url": "https://javaguide.cn/system-design/security/encryption-algorithms.html",
      "id": "https://javaguide.cn/system-design/security/encryption-algorithms.html",
      "summary": "常见加密算法详解，涵盖AES、RSA等对称与非对称加密算法及MD5、SHA等哈希算法的原理与应用场景。",
      "content_html": "<p>加密算法是一种用数学方法对数据进行变换的技术，目的是保护数据的安全，防止被未经授权的人读取或修改。加密算法可以分为三大类：对称加密算法、非对称加密算法和哈希算法（也叫摘要算法）。</p>\n<p>日常开发中常见的需要用到加密算法的场景：</p>\n<ol>\n<li>保存在数据库中的密码需要加盐之后使用哈希算法（比如 BCrypt）进行加密。</li>\n<li>保存在数据库中的银行卡号、身份号这类敏感数据需要使用对称加密算法（比如 AES）保存。</li>\n<li>网络传输的敏感数据比如银行卡号、身份号需要用 HTTPS + 非对称加密算法（如 RSA）来保证传输数据的安全性。</li>\n<li>……</li>\n</ol>\n<p>ps: 严格上来说，哈希算法其实不属于加密算法，只是可以用到某些加密场景中（例如密码加密），两者可以看作是并列关系。加密算法通常指的是可以将明文转换为密文，并且能够通过某种方式（如密钥）再将密文还原为明文的算法。而哈希算法是一种单向过程，它将输入信息转换成一个固定长度的、看似随机的哈希值，但这个过程是不可逆的，也就是说，不能从哈希值还原出原始信息。</p>\n<h2>哈希算法</h2>\n<p>哈希算法也叫散列函数或摘要算法，它的作用是对任意长度的数据生成一个固定长度的唯一标识，也叫哈希值、散列值或消息摘要（后文统称为哈希值）。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/encryption-algorithms/hash-function-effect-demonstration.png\" alt=\"哈希算法效果演示\"></p>\n<p>哈希算法的是不可逆的，你无法通过哈希之后的值再得到原值。</p>\n<p>哈希值的作用是可以用来验证数据的完整性和一致性。</p>\n<p>举两个实际的例子：</p>\n<ul>\n<li>保存密码到数据库时使用哈希算法进行加密，可以通过比较用户输入密码的哈希值和数据库保存的哈希值是否一致，来判断密码是否正确。</li>\n<li>我们下载一个文件时，可以通过比较文件的哈希值和官方提供的哈希值是否一致，来判断文件是否被篡改或损坏；</li>\n</ul>\n<p>这种算法的特点是不可逆：</p>\n<ul>\n<li>不能从哈希值还原出原始数据。</li>\n<li>原始数据的任何改变都会导致哈希值的巨大变化。</li>\n</ul>\n<p>哈希算法可以简单分为两类：</p>\n<ol>\n<li><strong>加密哈希算法</strong>：安全性较高的哈希算法，它可以提供一定的数据完整性保护和数据防篡改能力，能够抵御一定的攻击手段，安全性相对较高，但性能较差，适用于对安全性要求较高的场景。例如 SHA2、SHA3、SM3、RIPEMD-160、BLAKE2 等等。</li>\n<li><strong>非加密哈希算法</strong>：安全性相对较低的哈希算法，易受到暴力破解、冲突攻击等攻击手段的影响，但性能较高，适用于对安全性没有要求的业务场景。例如 CRC32、MurMurHash3 等等。</li>\n</ol>\n<p>除了这两种之外，还有一些特殊的哈希算法，例如安全性更高的<strong>慢哈希算法</strong>。</p>\n<p>常见的哈希算法有：</p>\n<ul>\n<li>MD（Message Digest，消息摘要算法）：MD2、MD4、MD5 等，已经不被推荐使用。</li>\n<li>SHA（Secure Hash Algorithm，安全哈希算法）：SHA-1 系列安全性低，SHA2，SHA3 系列安全性较高。</li>\n<li>国密算法：例如 SM2、SM3、SM4，其中 SM2 为非对称加密算法，SM4 为对称加密算法，SM3 为哈希算法（安全性及效率和 SHA-256 相当，但更适合国内的应用环境）。</li>\n<li>Bcrypt（密码哈希算法）：基于 Blowfish 加密算法的密码哈希算法，专门为密码加密而设计，安全性高，属于慢哈希算法。</li>\n<li>MAC（Message Authentication Code，消息认证码算法）：HMAC 是一种基于哈希的 MAC，可以与任何安全的哈希算法结合使用，例如 SHA-256。</li>\n<li>CRC：（Cyclic Redundancy Check，循环冗余校验）：CRC32 是一种 CRC 算法，它的特点是生成 32 位的校验值，通常用于数据完整性校验、文件校验等场景。</li>\n<li>SipHash：它不是传统的无密钥加密哈希函数（如 SHA-256），而是带密钥的 PRF（Pseudo-Random Function）。必须配合一个随机密钥使用，才能真正具备抗碰撞攻击的能力。它的设计目的是在速度和安全性之间达到一个平衡，用于防御<a href=\"https://aumasson.jp/siphash/siphashdos_29c3_slides.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">哈希泛洪 DoS 攻击</a>。Rust 默认使用 SipHash 作为哈希算法（目前是 SipHash-1-3 ），从 Redis 4.0 版本开始，字典（dict）的哈希算法从原来的 MurmurHash2 切换为 SipHash（目前是 SipHash-1-2）。</li>\n<li>MurMurHash：经典快速的非加密哈希算法，目前最新的版本是 MurMurHash3，可以生成 32 位或者 128 位哈希值；</li>\n<li>……</li>\n</ul>\n<p>哈希算法一般是不需要密钥的，但也存在部分特殊哈希算法需要密钥。例如，MAC 和 SipHash 就是一种基于密钥的哈希算法，它在哈希算法的基础上增加了一个密钥，使得只有知道密钥的人才能验证数据的完整性和来源。</p>\n<h3>MD</h3>\n<p>MD 算法有多个版本，包括 MD2、MD4、MD5 等，其中 MD5 是最常用的版本，它可以生成一个 128 位（16 字节）的哈希值。从安全性上说：MD5 &gt; MD4 &gt; MD2。除了这些版本，还有一些基于 MD4 或 MD5 改进的算法，如 RIPEMD、HAVAL 等。</p>\n<p>即使是最安全 MD 算法 MD5 也存在被破解的风险，攻击者可以通过暴力破解或彩虹表攻击等方式，找到与原始数据相同的哈希值，从而破解数据。</p>\n<p>为了增加破解难度，通常可以选择加盐。盐（Salt）在密码学中，是指通过在密码任意固定位置插入特定的字符串，让哈希后的结果和使用原始密码的哈希结果不相符，这种过程称之为“加盐”。</p>\n<p>加盐之后就安全了吗？并不一定，这只是增加了破解难度，不代表无法破解。而且，MD5 算法本身就存在弱碰撞（Collision）问题，即多个不同的输入产生相同的 MD5 值。</p>\n<p>因此，MD 算法已经不被推荐使用，建议使用更安全的哈希算法比如 SHA-2、Bcrypt。</p>\n<p>Java 提供了对 MD 算法系列的支持，包括 MD2、MD5。</p>\n<p>MD5 代码示例（未加盐）：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> originalString </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Java学习 + 面试指南：javaguide.cn\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建MD5摘要对象</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">MessageDigest</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> messageDigest </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> MessageDigest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getInstance</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"MD5\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">messageDigest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">update</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">originalString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getBytes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">StandardCharsets</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">UTF_8</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 计算哈希值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">byte</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] result </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> messageDigest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">digest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 将哈希值转换为十六进制字符串</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> hexString </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> HexBinaryAdapter</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">marshal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(result);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Original String: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> originalString);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"MD5 Hash: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> hexString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toLowerCase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>输出：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">Original</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> String:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Java学习</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> +</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 面试指南：javaguide.cn</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">MD5</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Hash:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> fb246796f5b1b60d4d0268c817c608fa</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>SHA</h3>\n<p>SHA（Secure Hash Algorithm）系列算法是一组密码哈希算法，用于将任意长度的数据映射为固定长度的哈希值。SHA 系列算法由美国国家安全局（NSA）于 1993 年设计，目前共有 SHA-1、SHA-2、SHA-3 三种版本。</p>\n<p>SHA-1 算法将任意长度的数据映射为 160 位的哈希值。然而，SHA-1 算法存在一些严重的缺陷，比如安全性低，容易受到碰撞攻击和长度扩展攻击。因此，SHA-1 算法已经不再被推荐使用。 SHA-2 家族（如 SHA-256、SHA-384、SHA-512 等）和 SHA-3 系列是 SHA-1 算法的替代方案，它们都提供了更高的安全性和更长的哈希值长度。</p>\n<p>SHA-2 家族是在 SHA-1 算法的基础上改进而来的，它们采用了更复杂的运算过程和更多的轮次，使得攻击者更难以通过预计算或巧合找到碰撞。</p>\n<p>为了寻找一种更安全和更先进的密码哈希算法，美国国家标准与技术研究院（National Institute of Standards and Technology，简称 NIST）在 2007 年公开征集 SHA-3 的候选算法。NIST 一共收到了 64 个算法方案，经过多轮的评估和筛选，最终在 2012 年宣布 Keccak 算法胜出，成为 SHA-3 的标准算法（SHA-3 与 SHA-2 算法没有直接的关系）。 Keccak 算法具有与 MD 和 SHA-1/2 完全不同的设计思路，即海绵结构（Sponge Construction），使得传统攻击方法无法直接应用于 SHA-3 的攻击中（能够抵抗目前已知的所有攻击方式包括碰撞攻击、长度扩展攻击、差分攻击等）。</p>\n<p>由于 SHA-2 算法还没有出现重大的安全漏洞，而且在软件中的效率更高，所以大多数人还是倾向于使用 SHA-2 算法。</p>\n<p>相比 MD5 算法，SHA-2 算法之所以更强，主要有两个原因：</p>\n<ul>\n<li>哈希值长度更长：例如 SHA-256 算法的哈希值长度为 256 位，而 MD5 算法的哈希值长度为 128 位，这就提高了攻击者暴力破解或者彩虹表攻击的难度。</li>\n<li>更强的碰撞抗性：SHA 算法采用了更复杂的运算过程和更多的轮次，使得攻击者更难以通过预计算或巧合找到碰撞。目前还没有找到任何两个不同的数据，它们的 SHA-256 哈希值相同。</li>\n</ul>\n<p>当然，SHA-2 也不是绝对安全的，也有被暴力破解或者彩虹表攻击的风险，所以，在实际的应用中，加盐还是必不可少的。</p>\n<p>Java 提供了对 SHA 算法系列的支持，包括 SHA-1、SHA-256、SHA-384 和 SHA-512。</p>\n<p>SHA-256 代码示例（未加盐）：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> originalString </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Java学习 + 面试指南：javaguide.cn\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建SHA-256摘要对象</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">MessageDigest</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> messageDigest </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> MessageDigest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getInstance</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"SHA-256\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">messageDigest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">update</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">originalString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getBytes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 计算哈希值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">byte</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] result </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> messageDigest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">digest</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 将哈希值转换为十六进制字符串</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> hexString </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> HexBinaryAdapter</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">marshal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(result);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Original String: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> originalString);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"SHA-256 Hash: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> hexString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toLowerCase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>输出：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">Original</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> String:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Java学习</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> +</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 面试指南：javaguide.cn</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">SHA-256</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Hash:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 184eb7e1d7fb002444098c9bde3403c6f6722c93ecfac242c0e35cd9ed3b41cd</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>Bcrypt</h3>\n<p>Bcrypt 算法是一种基于 Blowfish 加密算法的密码哈希算法，专门为密码加密而设计，安全性高。</p>\n<p>由于 Bcrypt 采用了 salt（盐） 和 cost（成本） 两种机制，它可以有效地防止彩虹表攻击和暴力破解攻击，从而保证密码的安全性。salt 是一个随机生成的字符串，用于和密码混合，增加密码的复杂度和唯一性。cost 是一个数值参数，用于控制 Bcrypt 算法的迭代次数，增加密码哈希的计算时间和资源消耗。</p>\n<p>Bcrypt 算法可以根据实际情况进行调整加密的复杂度，可以设置不同的 cost 值和 salt 值，从而满足不同的安全需求，灵活性很高。</p>\n<p>Java 应用程序的安全框架 Spring Security 支持多种密码编码器，其中 <code>BCryptPasswordEncoder</code> 是官方推荐的一种，它使用 BCrypt 算法对用户的密码进行加密存储。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Bean</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> PasswordEncoder</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> passwordEncoder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(){</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> BCryptPasswordEncoder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>对称加密</h2>\n<p>对称加密算法是指加密和解密使用同一个密钥的算法，也叫共享密钥加密算法。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/encryption-algorithms/symmetric-encryption.png\" alt=\"对称加密\"></p>\n<p>常见的对称加密算法有 DES、3DES、AES 等。</p>\n<h3>DES 和 3DES</h3>\n<p>DES（Data Encryption Standard）使用 64 位的密钥(有效秘钥长度为 56 位,8 位奇偶校验位)和 64 位的明文进行加密。</p>\n<p>虽然 DES 一次只能加密 64 位，但我们只需要把明文划分成 64 位一组的块，就可以实现任意长度明文的加密。如果明文长度不是 64 位的倍数，必须进行填充，常用的模式有 PKCS5Padding, PKCS7Padding, NOPADDING。</p>\n<p>DES 加密算法的基本思想是将 64 位的明文分成两半，然后对每一半进行多轮的变换，最后再合并成 64 位的密文。这些变换包括置换、异或、选择、移位等操作，每一轮都使用了一个子密钥，而这些子密钥都是由同一个 56 位的主密钥生成的。DES 加密算法总共进行了 16 轮变换，最后再进行一次逆置换，得到最终的密文。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/des-steps.jpg\" alt=\"DES（Data Encryption Standard）\"></p>\n<p>这是一个经典的对称加密算法，但也有明显的缺陷，即 56 位的密钥安全性不足，已被证实可以在短时间内破解。</p>\n<p>为了提高 DES 算法的安全性，人们提出了一些变种或者替代方案，例如 3DES（Triple DES）。</p>\n<p>3DES（Triple DES）是 DES 向 AES 过渡的加密算法，它使用 2 个或者 3 个 56 位的密钥对数据进行三次加密。3DES 相当于是对每个数据块应用三次 DES 的对称加密算法。</p>\n<p>为了兼容普通的 DES，3DES 并没有直接使用 加密-&gt;加密-&gt;加密 的方式，而是采用了加密-&gt;解密-&gt;加密 的方式。当三种密钥均相同时，前两步相互抵消，相当于仅实现了一次加密，因此可实现对普通 DES 加密算法的兼容。3DES 比 DES 更为安全，但其处理速度不高。</p>\n<h3>AES</h3>\n<p>AES（Advanced Encryption Standard）算法是一种更先进的对称密钥加密算法，它使用 128 位、192 位或 256 位的密钥对数据进行加密或解密，密钥越长，安全性越高。</p>\n<p>AES 也是一种分组(或者叫块)密码，分组长度只能是 128 位，也就是说，每个分组为 16 个字节。AES 加密算法有多种工作模式（mode of operation），如：ECB、CBC、OFB、CFB、CTR、XTS、OCB、GCM（目前使用最广泛的模式）。不同的模式参数和加密流程不同，但是核心仍然是 AES 算法。</p>\n<p>和 DES 类似，对于不是 128 位倍数的明文需要进行填充，常用的填充模式有 PKCS5Padding, PKCS7Padding, NOPADDING。不过，AES-GCM 是流加密算法，可以对任意长度的明文进行加密，所以对应的填充模式为 NoPadding，即无需填充。</p>\n<p>AES 的速度比 3DES 快，而且更安全。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/aes-steps.jpg\" alt=\"AES（Advanced Encryption Standard）\"></p>\n<p>DES 算法和 AES 算法简单对比（图片来自于：<a href=\"https://cheapsslweb.com/blog/rsa-vs-aes-encryption\" target=\"_blank\" rel=\"noopener noreferrer\">RSA vs. AES Encryption: Key Differences Explained</a>）：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/des-vs-aes.png\" alt=\"DES 和 AES 对比\"></p>\n<p>基于 Java 实现 AES 算法代码示例：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> AES_ALGORITHM </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"AES\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// AES密钥</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> AES_SECRET_KEY </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"4128D9CDAC7E2F82951CBAF7FDFE675B\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// AES加密模式为GCM，填充方式为NoPadding</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// AES-GCM 是流加密（Stream cipher）算法，所以对应的填充模式为 NoPadding，即无需填充。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> AES_TRANSFORMATION </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"AES/GCM/NoPadding\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 加密器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Cipher</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> encryptionCipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 解密器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Cipher</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> decryptionCipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 完成一些初始化工作</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> init</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() throws Exception {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 将AES密钥转换为SecretKeySpec对象</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    SecretKeySpec</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> secretKeySpec </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> SecretKeySpec</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">AES_SECRET_KEY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getBytes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(),</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> AES_ALGORITHM)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 使用指定的AES加密模式和填充方式获取对应的加密器并初始化</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    encryptionCipher </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Cipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getInstance</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(AES_TRANSFORMATION);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    encryptionCipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">init</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Cipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ENCRYPT_MODE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, secretKeySpec);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 使用指定的AES加密模式和填充方式获取对应的解密器并初始化</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    decryptionCipher </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Cipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getInstance</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(AES_TRANSFORMATION);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    decryptionCipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">init</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Cipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">DECRYPT_MODE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, secretKeySpec, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> GCMParameterSpec</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">128</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">encryptionCipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getIV</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 加密</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> encrypt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> data) throws Exception {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">    byte</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] dataInBytes </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getBytes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 加密数据</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">    byte</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] encryptedBytes </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> encryptionCipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">doFinal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(dataInBytes);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Base64</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getEncoder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">encodeToString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(encryptedBytes);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 解密</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> decrypt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> encryptedData) throws Exception {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">    byte</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] dataInBytes </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Base64</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getDecoder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">decode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(encryptedData);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 解密数据</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">    byte</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] decryptedBytes </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> decryptionCipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">doFinal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(dataInBytes);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(decryptedBytes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> StandardCharsets</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">UTF_8</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] args) throws Exception {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> originalString </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Java学习 + 面试指南：javaguide.cn\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    init</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> encryptedData </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> encrypt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(originalString)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> decryptedData </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> decrypt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(encryptedData)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Original String: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> originalString);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"AES Encrypted Data : \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> encryptedData);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"AES Decrypted Data : \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> decryptedData);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>输出：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">Original</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> String:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Java学习</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> +</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 面试指南：javaguide.cn</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">AES</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Encrypted</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Data</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> :</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> E1qTkK91suBqToag7WCyoFP9uK5hR1nSfM6p+oBlYj71bFiIVnk5TsQRT+zpjv8stha7oyKi3jQ=</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">AES</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Decrypted</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Data</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> :</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Java学习</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> +</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 面试指南：javaguide.cn</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>非对称加密</h2>\n<p>非对称加密算法是指加密和解密使用不同的密钥的算法，也叫公开密钥加密算法。这两个密钥互不相同，一个称为公钥，另一个称为私钥。公钥可以公开给任何人使用，私钥则要保密。</p>\n<p>如果用公钥加密数据，只能用对应的私钥解密（加密）；如果用私钥加密数据，只能用对应的公钥解密（签名）。这样就可以实现数据的安全传输和身份认证。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/encryption-algorithms/asymmetric-encryption.png\" alt=\"非对称加密\"></p>\n<p>常见的非对称加密算法有 RSA、DSA、ECC 等。</p>\n<h3>RSA</h3>\n<p>RSA（Rivest–Shamir–Adleman algorithm）算法是一种基于大数分解的困难性的非对称加密算法，它需要选择两个大素数作为私钥的一部分，然后计算出它们的乘积作为公钥的一部分（寻求两个大素数比较简单，而将它们的乘积进行因式分解却极其困难）。RSA 算法原理的详细介绍，可以参考这篇文章：<a href=\"https://www.cnblogs.com/xiaofuge/p/16954187.html\" target=\"_blank\" rel=\"noopener noreferrer\">你真的了解 RSA 加密算法吗？ - 小傅哥</a>。</p>\n<p>RSA 算法的安全性依赖于大数分解的难度，目前已经有 512 位和 768 位的 RSA 公钥被成功分解，因此建议使用 2048 位或以上的密钥长度。</p>\n<p>RSA 算法的优点是简单易用，可以用于数据加密和数字签名；缺点是运算速度慢，不适合大量数据的加密。</p>\n<p>RSA 算法是是目前应用最广泛的非对称加密算法，像 SSL/TLS、SSH 等协议中就用到了 RSA 算法。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/encryption-algorithms/https-rsa-sha-256.png\" alt=\"HTTPS 证书签名算法中带RSA 加密的SHA-256 \"></p>\n<p>基于 Java 实现 RSA 算法代码示例：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> RSA_ALGORITHM </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"RSA\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 生成RSA密钥对</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> KeyPair</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> generateKeyPair</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() throws NoSuchAlgorithmException {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    KeyPairGenerator</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> keyPairGenerator </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> KeyPairGenerator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getInstance</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(RSA_ALGORITHM);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 密钥大小为2048位</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    keyPairGenerator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">initialize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2048</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> keyPairGenerator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">generateKeyPair</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 使用公钥加密数据</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> encrypt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> PublicKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> publicKey) throws Exception {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Cipher</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> cipher </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Cipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getInstance</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(RSA_ALGORITHM);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    cipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">init</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Cipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ENCRYPT_MODE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, publicKey);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">    byte</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] encryptedData </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> cipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">doFinal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">data</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getBytes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">StandardCharsets</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">UTF_8</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Base64</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getEncoder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">encodeToString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(encryptedData);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * 使用私钥解密数据</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> decrypt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> encryptedData</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> PrivateKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> privateKey) throws Exception {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">    byte</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] decodedData </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Base64</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getDecoder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">decode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(encryptedData);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Cipher</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> cipher </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Cipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getInstance</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(RSA_ALGORITHM);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    cipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">init</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Cipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">DECRYPT_MODE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, privateKey);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">    byte</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] decryptedData </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> cipher</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">doFinal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(decodedData);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(decryptedData</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> StandardCharsets</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">UTF_8</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] args) throws Exception {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    KeyPair</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> keyPair </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> generateKeyPair</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    PublicKey</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> publicKey </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> keyPair</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getPublic</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    PrivateKey</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> privateKey </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> keyPair</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getPrivate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> originalString </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Java学习 + 面试指南：javaguide.cn\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> encryptedData </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> encrypt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(originalString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> publicKey)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> decryptedData </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> decrypt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(encryptedData</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> privateKey)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Original String: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> originalString);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"RSA Encrypted Data : \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> encryptedData);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"RSA Decrypted Data : \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> decryptedData);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>输出：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">Original</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> String:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Java学习</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> +</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 面试指南：javaguide.cn</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">RSA</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Encrypted</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Data</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> :</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> T9ey/CEPUAhZm4UJjuVNIg8RPd1fQ32S9w6+rvOKxmuMumkJY2daFfWuCn8A73Mk5bL6TigOJI0GHfKOt/W2x968qLM3pBGCcPX17n4pR43f32IIIz9iPdgF/INOqDxP5ZAtCDvTiuzcSgDHXqiBSK5TDjtj7xoGjfudYAXICa8pWitnqDgJYoo2J0F8mKzxoi8D8eLE455MEx8ZT1s7FUD/z7/H8CfShLRbO9zq/zFI06TXn123ufg+F4lDaq/5jaIxGVEUB/NFeX4N6OZCFHtAV32mw71BYUadzI9TgvkkUr1rSKmQ0icNhnRdKedJokGUh8g9QQ768KERu92Ibg==</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">RSA</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Decrypted</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Data</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> :</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Java学习</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> +</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 面试指南：javaguide.cn</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>DSA</h3>\n<p>DSA（Digital Signature Algorithm）算法是一种基于离散对数的困难性的非对称加密算法，它需要选择一个素数 q 和一个 q 的倍数 p 作为私钥的一部分，然后计算出一个模 p 的原根 g 和一个模 q 的整数 y 作为公钥的一部分。DSA 算法的安全性依赖于离散对数的难度，目前已经有 1024 位的 DSA 公钥被成功破解，因此建议使用 2048 位或以上的密钥长度。</p>\n<p>DSA 算法的优点是数字签名速度快，适合生成数字证书；缺点是不能用于数据加密，且签名过程需要随机数。</p>\n<p>DSA 算法签名过程：</p>\n<ol>\n<li>使用消息摘要算法对要发送的数据进行加密，生成一个信息摘要，也就是一个短的、唯一的、不可逆的数据表示。</li>\n<li>发送方用自己的 DSA 私钥对信息摘要再进行加密，形成一个数字签名，也就是一个可以证明数据来源和完整性的数据附加。</li>\n<li>将原始数据和数字签名一起通过互联网传送给接收方。</li>\n<li>接收方用发送方的公钥对数字签名进行解密，得到信息摘要。同时，接收方也用消息摘要算法对收到的原始数据进行加密，得到另一个信息摘要。接收方将两个信息摘要进行比较，如果两者一致，则说明在传送过程中数据没有被篡改或损坏；否则，则说明数据已经失去了安全性和保密性。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/security/encryption-algorithms/dsa-algorithm-signing-process.png\" alt=\"DSA 算法签名过程\"></p>\n<h2>总结</h2>\n<p>这篇文章介绍了三种加密算法：哈希算法、对称加密算法和非对称加密算法。</p>\n<ul>\n<li>哈希算法是一种用数学方法对数据生成一个固定长度的唯一标识的技术，可以用来验证数据的完整性和一致性，常见的哈希算法有 MD、SHA、MAC 等。</li>\n<li>对称加密算法是一种加密和解密使用同一个密钥的算法，可以用来保护数据的安全性和保密性，常见的对称加密算法有 DES、3DES、AES 等。</li>\n<li>非对称加密算法是一种加密和解密使用不同的密钥的算法，可以用来实现数据的安全传输和身份认证，常见的非对称加密算法有 RSA、DSA、ECC 等。</li>\n</ul>\n<h2>参考</h2>\n<ul>\n<li>深入理解完美哈希 - 腾讯技术工程：<a href=\"https://mp.weixin.qq.com/s/M8Wcj8sZ7UF1CMr887Puog\" target=\"_blank\" rel=\"noopener noreferrer\">https://mp.weixin.qq.com/s/M8Wcj8sZ7UF1CMr887Puog</a></li>\n<li>写给开发人员的实用密码学（二）—— 哈希函数：<a href=\"https://thiscute.world/posts/practical-cryptography-basics-2-hash/\" target=\"_blank\" rel=\"noopener noreferrer\">https://thiscute.world/posts/practical-cryptography-basics-2-hash/</a></li>\n<li>奇妙的安全旅行之 DSA 算法：<a href=\"https://zhuanlan.zhihu.com/p/347025157\" target=\"_blank\" rel=\"noopener noreferrer\">https://zhuanlan.zhihu.com/p/347025157</a></li>\n<li>AES-GCM 加密简介：<a href=\"https://juejin.cn/post/6844904122676690951\" target=\"_blank\" rel=\"noopener noreferrer\">https://juejin.cn/post/6844904122676690951</a></li>\n<li>Java AES 256 GCM Encryption and Decryption Example | JCE Unlimited Strength：<a href=\"https://www.javainterviewpoint.com/java-aes-256-gcm-encryption-and-decryption/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.javainterviewpoint.com/java-aes-256-gcm-encryption-and-decryption/</a></li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/system-design/security/encryption-algorithms/hash-function-effect-demonstration.png",
      "date_published": "2023-06-27T12:05:39.000Z",
      "date_modified": "2026-03-21T06:28:26.000Z",
      "authors": [],
      "tags": [
        "系统设计"
      ]
    },
    {
      "title": "Java NIO 核心知识总结",
      "url": "https://javaguide.cn/java/io/nio-basis.html",
      "id": "https://javaguide.cn/java/io/nio-basis.html",
      "summary": "Java NIO核心知识全面总结：详解Channel通道、Buffer缓冲区、Selector选择器三大核心组件、非阻塞IO实现、零拷贝技术、与传统IO性能对比。",
      "content_html": "<p>在学习 NIO 之前，需要先了解一下计算机 I/O 模型的基础理论知识。还不了解的话，可以参考我写的这篇文章：<a href=\"https://javaguide.cn/java/io/io-model.html\" target=\"_blank\" rel=\"noopener noreferrer\">Java IO 模型详解</a>。</p>\n<h2>NIO 简介</h2>\n<p>在传统的 Java I/O 模型（BIO）中，I/O 操作是以阻塞的方式进行的。也就是说，当一个线程执行一个 I/O 操作时，它会被阻塞直到操作完成。这种阻塞模型在处理多个并发连接时可能会导致性能瓶颈，因为需要为每个连接创建一个线程，而线程的创建和切换都是有开销的。</p>\n<p>为了解决这个问题，在 Java1.4 版本引入了一种新的 I/O 模型 — <strong>NIO</strong> （New IO，也称为 Non-blocking IO） 。NIO 弥补了同步阻塞 I/O 的不足，它在标准 Java 代码中提供了非阻塞、面向缓冲、基于通道的 I/O，可以使用少量的线程来处理多个连接，大大提高了 I/O 效率和并发。</p>\n<p>下图是 BIO、NIO 和 AIO 处理客户端请求的简单对比图（关于 AIO 的介绍，可以看我写的这篇文章：<a href=\"https://javaguide.cn/java/io/io-model.html\" target=\"_blank\" rel=\"noopener noreferrer\">Java IO 模型详解</a>，不是重点，了解即可）。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/nio/bio-aio-nio.png\" alt=\"BIO、NIO 和 AIO 对比\"></p>\n<p>⚠️需要注意：使用 NIO 并不一定意味着高性能，它的性能优势主要体现在高并发和高延迟的网络环境下。当连接数较少、并发程度较低或者网络传输速度较快时，NIO 的性能并不一定优于传统的 BIO 。</p>\n<h2>NIO 核心组件</h2>\n<p>NIO 主要包括以下三个核心组件：</p>\n<ul>\n<li><strong>Buffer（缓冲区）</strong>：NIO 读写数据都是通过缓冲区进行操作的。读操作的时候将 Channel 中的数据填充到 Buffer 中，而写操作时将 Buffer 中的数据写入到 Channel 中。</li>\n<li><strong>Channel（通道）</strong>：Channel 是一个双向的、可读可写的数据传输通道，NIO 通过 Channel 来实现数据的输入输出。通道是一个抽象的概念，它可以代表文件、套接字或者其他数据源之间的连接。</li>\n<li><strong>Selector（选择器）</strong>：允许一个线程处理多个 Channel，基于事件驱动的 I/O 多路复用模型。所有的 Channel 都可以注册到 Selector 上，由 Selector 来分配线程来处理事件。</li>\n</ul>\n<p>三者的关系如下图所示（暂时不理解没关系，后文会详细介绍）：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/nio/channel-buffer-selector.png\" alt=\"Buffer、Channel和Selector三者之间的关系\"></p>\n<p>下面详细介绍一下这三个组件。</p>\n<h3>Buffer（缓冲区）</h3>\n<p>在传统的 BIO 中，数据的读写是面向流的， 分为字节流和字符流。</p>\n<p>在 Java 1.4 的 NIO 库中，所有数据都是用缓冲区处理的，这是新库和之前的 BIO 的一个重要区别，有点类似于 BIO 中的缓冲流。NIO 在读取数据时，它是直接读到缓冲区中的。在写入数据时，写入到缓冲区中。 使用 NIO 在读写数据时，都是通过缓冲区进行操作。</p>\n<p><code>Buffer</code> 的子类如下图所示。其中，最常用的是 <code>ByteBuffer</code>，它可以用来存储和操作字节数据。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/nio/buffer-subclasses.png\" alt=\"Buffer 的子类\"></p>\n<p>你可以将 Buffer 理解为一个数组，<code>IntBuffer</code>、<code>FloatBuffer</code>、<code>CharBuffer</code> 等分别对应 <code>int[]</code>、<code>float[]</code>、<code>char[]</code> 等。</p>\n<p>为了更清晰地认识缓冲区，我们来简单看看<code>Buffer</code> 类中定义的四个成员变量：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> abstract</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // Invariants: mark &#x3C;= position &#x3C;= limit &#x3C;= capacity</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> mark </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> position </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> limit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> capacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这四个成员变量的具体含义如下：</p>\n<ol>\n<li>容量（<code>capacity</code>）：<code>Buffer</code>可以存储的最大数据量，<code>Buffer</code>创建时设置且不可改变；</li>\n<li>界限（<code>limit</code>）：<code>Buffer</code> 中可以读/写数据的边界。写模式下，<code>limit</code> 代表最多能写入的数据，一般等于 <code>capacity</code>（可以通过<code>limit(int newLimit)</code>方法设置）；读模式下，<code>limit</code> 等于 Buffer 中实际写入的数据大小。</li>\n<li>位置（<code>position</code>）：下一个可以被读写的数据的位置（索引）。从写操作模式到读操作模式切换的时候（flip），<code>position</code> 都会归零，这样就可以从头开始读写了。</li>\n<li>标记（<code>mark</code>）：<code>Buffer</code>允许将位置直接定位到该标记处，这是一个可选属性；</li>\n</ol>\n<p>并且，上述变量满足如下的关系：<strong>0 &lt;= mark &lt;= position &lt;= limit &lt;= capacity</strong> 。</p>\n<p>另外，Buffer 有读模式和写模式这两种模式，分别用于从 Buffer 中读取数据或者向 Buffer 中写入数据。Buffer 被创建之后默认是写模式，调用 <code>flip()</code> 可以切换到读模式。如果要再次切换回写模式，可以调用 <code>clear()</code> 或者 <code>compact()</code> 方法。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/nio/JavaNIOBuffer.png\" alt=\"position 、limit 和 capacity 之前的关系\"></p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/nio/NIOBufferClassAttributes.png\" alt=\"position 、limit 和 capacity 之前的关系\"></p>\n<p><code>Buffer</code> 对象不能通过 <code>new</code> 调用构造方法创建对象 ，只能通过静态方法实例化 <code>Buffer</code>。</p>\n<p>这里以 <code>ByteBuffer</code>为例进行介绍：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 分配堆内存</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ByteBuffer</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> allocate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> capacity)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 分配直接内存</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ByteBuffer</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> allocateDirect</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> capacity)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>Buffer 最核心的两个方法：</p>\n<ol>\n<li><code>get</code> : 读取缓冲区的数据</li>\n<li><code>put</code> ：向缓冲区写入数据</li>\n</ol>\n<p>除上述两个方法之外，其他的重要方法：</p>\n<ul>\n<li><code>flip</code> ：将缓冲区从写模式切换到读模式，它会将 <code>limit</code> 的值设置为当前 <code>position</code> 的值，将 <code>position</code> 的值设置为 0。</li>\n<li><code>clear</code>: 清空缓冲区，将缓冲区从读模式切换到写模式，并将 <code>position</code> 的值设置为 0，将 <code>limit</code> 的值设置为 <code>capacity</code> 的值。</li>\n<li>……</li>\n</ul>\n<p>Buffer 中数据变化的过程：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.nio.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CharBufferDemo</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 分配一个容量为8的CharBuffer</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        CharBuffer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> CharBuffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">allocate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">8</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"初始状态：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        printState</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(buffer);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 向buffer写入3个字符</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'a'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">).</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'b'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">).</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'c'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"写入3个字符后的状态：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        printState</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(buffer);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 调用flip()方法，准备读取buffer中的数据，将 position 置 0,limit 的置 3</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">flip</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"调用flip()方法后的状态：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        printState</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(buffer);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 读取字符</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">hasRemaining</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">print</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 调用clear()方法，清空缓冲区，将 position 的值置为 0，将 limit 的值置为 capacity 的值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">clear</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"调用clear()方法后的状态：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        printState</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(buffer);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 打印buffer的capacity、limit、position、mark的位置</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> printState</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">CharBuffer</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">print</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"capacity: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">capacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">print</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\", limit: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">limit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">print</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\", position: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">position</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">print</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\", mark 开始读取的字符: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">mark</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"</span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\n</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>输出:</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">初始状态：</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">capacity:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 8,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> limit:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 8,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> position:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">写入3个字符后的状态：</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">capacity:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 8,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> limit:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 8,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> position:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 3</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">准备读取buffer中的数据！</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">调用flip</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()方法后的状态：</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">capacity:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 8,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> limit:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 3,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> position:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">读取到的数据：abc</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">调用clear</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()方法后的状态：</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">capacity:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 8,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> limit:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 8,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> position:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>为了帮助理解，我绘制了一张图片展示 <code>capacity</code>、<code>limit</code>和<code>position</code>每一阶段的变化。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/nio/NIOBufferClassAttributesDataChanges.png\" alt=\"capacity、limit和position每一阶段的变化\"></p>\n<h3>Channel（通道）</h3>\n<p>Channel 是一个通道，它建立了与数据源（如文件、网络套接字等）之间的连接。我们可以利用它来读取和写入数据，就像打开了一条自来水管，让数据在 Channel 中自由流动。</p>\n<p>BIO 中的流是单向的，分为各种 <code>InputStream</code>（输入流）和 <code>OutputStream</code>（输出流），数据只是在一个方向上传输。通道与流的不同之处在于通道是双向的，它可以用于读、写或者同时用于读写。</p>\n<p>Channel 与前面介绍的 Buffer 打交道，读操作的时候将 Channel 中的数据填充到 Buffer 中，而写操作时将 Buffer 中的数据写入到 Channel 中。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/nio/channel-buffer.png\" alt=\"Channel 和 Buffer之间的关系\"></p>\n<p>另外，因为 Channel 是全双工的，所以它可以比流更好地映射底层操作系统的 API。特别是在 UNIX 网络编程模型中，底层操作系统的通道都是全双工的，同时支持读写操作。</p>\n<p><code>Channel</code> 的子类如下图所示。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/nio/channel-subclasses.png\" alt=\"Channel 的子类\"></p>\n<p>其中，最常用的是以下几种类型的通道：</p>\n<ul>\n<li><code>FileChannel</code>：文件访问通道；</li>\n<li><code>SocketChannel</code>、<code>ServerSocketChannel</code>：TCP 通信通道；</li>\n<li><code>DatagramChannel</code>：UDP 通信通道；</li>\n</ul>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/nio/channel-inheritance-relationship.png\" alt=\"Channel继承关系图\"></p>\n<p>Channel 最核心的两个方法：</p>\n<ol>\n<li><code>read</code> ：读取数据并写入到 Buffer 中。</li>\n<li><code>write</code> ：将 Buffer 中的数据写入到 Channel 中。</li>\n</ol>\n<p>这里我们以 <code>FileChannel</code> 为例演示一下是读取文件数据的。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">RandomAccessFile</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> reader </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> RandomAccessFile</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"/Users/guide/Documents/test_read.in\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"r\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">FileChannel</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> channel </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> reader</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getChannel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ByteBuffer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> buffer </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ByteBuffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">allocate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1024</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">channel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">read</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(buffer);</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>Selector（选择器）</h3>\n<p>Selector（选择器） 是 NIO 中的一个关键组件，它允许一个线程处理多个 Channel。Selector 是基于事件驱动的 I/O 多路复用模型，主要运作原理是：通过 Selector 注册通道的事件，Selector 会不断地轮询注册在其上的 Channel。当事件发生时，比如：某个 Channel 上面有新的 TCP 连接接入、读和写事件，这个 Channel 就处于就绪状态，会被 Selector 轮询出来。Selector 会将相关的 Channel 加入到就绪集合中。通过 SelectionKey 可以获取就绪 Channel 的集合，然后对这些就绪的 Channel 进行相应的 I/O 操作。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/nio/selector-channel-selectionkey.png\" alt=\"Selector 选择器工作示意图\"></p>\n<p>一个多路复用器 Selector 可以同时轮询多个 Channel，由于 JDK 使用了 <code>epoll()</code> 代替传统的 <code>select</code> 实现，所以它并没有最大连接句柄 <code>1024/2048</code> 的限制。这也就意味着只需要一个线程负责 Selector 的轮询，就可以接入成千上万的客户端。</p>\n<p>Selector 可以监听以下四种事件类型：</p>\n<ol>\n<li><code>SelectionKey.OP_ACCEPT</code>：表示通道接受连接的事件，这通常用于 <code>ServerSocketChannel</code>。</li>\n<li><code>SelectionKey.OP_CONNECT</code>：表示通道完成连接的事件，这通常用于 <code>SocketChannel</code>。</li>\n<li><code>SelectionKey.OP_READ</code>：表示通道准备好进行读取的事件，即有数据可读。</li>\n<li><code>SelectionKey.OP_WRITE</code>：表示通道准备好进行写入的事件，即可以写入数据。</li>\n</ol>\n<p><code>Selector</code>是抽象类，可以通过调用此类的 <code>open()</code> 静态方法来创建 Selector 实例。Selector 可以同时监控多个 <code>SelectableChannel</code> 的 <code>IO</code> 状况，是非阻塞 <code>IO</code> 的核心。</p>\n<p>一个 Selector 实例有三个 <code>SelectionKey</code> 集合：</p>\n<ol>\n<li>所有的 <code>SelectionKey</code> 集合：代表了注册在该 Selector 上的 <code>Channel</code>，这个集合可以通过 <code>keys()</code> 方法返回。</li>\n<li>被选择的 <code>SelectionKey</code> 集合：代表了所有可通过 <code>select()</code> 方法获取的、需要进行 <code>IO</code> 处理的 Channel，这个集合可以通过 <code>selectedKeys()</code> 返回。</li>\n<li>被取消的 <code>SelectionKey</code> 集合：代表了所有被取消注册关系的 <code>Channel</code>，在下一次执行 <code>select()</code> 方法时，这些 <code>Channel</code> 对应的 <code>SelectionKey</code> 会被彻底删除，程序通常无须直接访问该集合，也没有暴露访问的方法。</li>\n</ol>\n<p>简单演示一下如何遍历被选择的 <code>SelectionKey</code> 集合并进行处理：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Set</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">SelectionKey</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> selectedKeys </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> selector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">selectedKeys</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Iterator</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">SelectionKey</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> keyIterator </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> selectedKeys</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">iterator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">keyIterator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">hasNext</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    SelectionKey</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> key </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> keyIterator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (key </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isAcceptable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // ServerSocketChannel 接收了一个新连接</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isConnectable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 表示一个新连接建立</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isReadable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // Channel 有准备好的数据，可以读取</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isWritable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // Channel 有空闲的 Buffer，可以写入数据</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    keyIterator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">remove</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>Selector 还提供了一系列和 <code>select()</code> 相关的方法：</p>\n<ul>\n<li><code>int select()</code>：监控所有注册的 <code>Channel</code>，当它们中间有需要处理的 <code>IO</code> 操作时，该方法返回，并将对应的 <code>SelectionKey</code> 加入被选择的 <code>SelectionKey</code> 集合中，该方法返回这些 <code>Channel</code> 的数量。</li>\n<li><code>int select(long timeout)</code>：可以设置超时时长的 <code>select()</code> 操作。</li>\n<li><code>int selectNow()</code>：执行一个立即返回的 <code>select()</code> 操作，相对于无参数的 <code>select()</code> 方法而言，该方法不会阻塞线程。</li>\n<li><code>Selector wakeup()</code>：使一个还未返回的 <code>select()</code> 方法立刻返回。</li>\n<li>……</li>\n</ul>\n<p>使用 Selector 实现网络读写的简单示例：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.io.IOException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.net.InetSocketAddress</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.nio.ByteBuffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.nio.channels.SelectionKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.nio.channels.Selector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.nio.channels.ServerSocketChannel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.nio.channels.SocketChannel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.util.Iterator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">import</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> java.util.Set</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> NioSelectorExample</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">      ServerSocketChannel</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> serverSocketChannel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ServerSocketChannel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">open</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">      serverSocketChannel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">configureBlocking</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">      serverSocketChannel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">socket</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">bind</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> InetSocketAddress</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">8080</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">));</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">      Selector</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> selector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Selector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">open</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      // 将 ServerSocketChannel 注册到 Selector 并监听 OP_ACCEPT 事件</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">      serverSocketChannel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">register</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(selector, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">SelectionKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">OP_ACCEPT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">      while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> readyChannels</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> selector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">select</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (readyChannels </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">          continue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Set</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">SelectionKey</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">selectedKeys</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> selector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">selectedKeys</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Iterator</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">SelectionKey</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">keyIterator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> selectedKeys</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">iterator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">keyIterator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">hasNext</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">          SelectionKey</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> keyIterator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">          if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isAcceptable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 处理连接事件</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            ServerSocketChannel</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> server</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (ServerSocketChannel) </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">channel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            SocketChannel</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> server</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">accept</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">configureBlocking</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 将客户端通道注册到 Selector 并监听 OP_READ 事件</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">register</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(selector, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">SelectionKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">OP_READ</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isReadable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 处理读事件</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            SocketChannel</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (SocketChannel) </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">channel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            ByteBuffer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ByteBuffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">allocate</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1024</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> bytesRead</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">read</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(buffer);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (bytesRead </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">              buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">flip</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">              System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"收到数据：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">array</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(), </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, bytesRead));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">              // 将客户端通道注册到 Selector 并监听 OP_WRITE 事件</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">              client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">register</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(selector, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">SelectionKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">OP_WRITE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (bytesRead </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">              // 客户端断开连接</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">              client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">close</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isWritable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 处理写事件</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            SocketChannel</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (SocketChannel) </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">key</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">channel</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            ByteBuffer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> buffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ByteBuffer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">wrap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Hello, Client!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getBytes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">write</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(buffer);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 将客户端通道注册到 Selector 并监听 OP_READ 事件</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">register</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(selector, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">SelectionKey</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">OP_READ</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">          keyIterator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">remove</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">IOException</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">      e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">printStackTrace</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>在示例中，我们创建了一个简单的服务器，监听 8080 端口，使用 Selector 处理连接、读取和写入事件。当接收到客户端的数据时，服务器将读取数据并将其打印到控制台，然后向客户端回复 &quot;Hello, Client!&quot;。</p>\n<h2>NIO 零拷贝</h2>\n<p>零拷贝是提升 IO 操作性能的一个常用手段，像 ActiveMQ、Kafka 、RocketMQ、QMQ、Netty 等顶级开源项目都用到了零拷贝。</p>\n<p>零拷贝是指计算机执行 IO 操作时，CPU 不需要将数据从一个存储区域复制到另一个存储区域，从而可以减少上下文切换以及 CPU 的拷贝时间。也就是说，零拷贝主要解决操作系统在处理 I/O 操作时频繁复制数据的问题。零拷贝的常见实现技术有： <code>mmap+write</code>、<code>sendfile</code>和 <code>sendfile + DMA gather copy</code> 。</p>\n<p>下图展示了各种零拷贝技术的对比图：</p>\n<p>|                            | CPU 拷贝 | DMA 拷贝 | 系统调用   | 上下文切换 |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/nio/bio-aio-nio.png",
      "date_published": "2023-06-26T15:16:09.000Z",
      "date_modified": "2026-01-27T07:16:00.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "ArrayBlockingQueue 源码分析",
      "url": "https://javaguide.cn/java/collection/arrayblockingqueue-source-code.html",
      "id": "https://javaguide.cn/java/collection/arrayblockingqueue-source-code.html",
      "summary": "ArrayBlockingQueue源码深度解析：详解有界阻塞队列实现、生产者消费者模式应用、ReentrantLock+Condition并发控制、线程池工作队列机制。",
      "content_html": "<h2>阻塞队列简介</h2>\n<h3>阻塞队列的历史</h3>\n<p>Java 阻塞队列的历史可以追溯到 JDK1.5 版本，当时 Java 平台增加了 <code>java.util.concurrent</code>，即我们常说的 JUC 包，其中包含了各种并发流程控制工具、并发容器、原子类等。这其中自然也包含了我们这篇文章所讨论的阻塞队列。</p>\n<p>为了解决高并发场景下多线程之间数据共享的问题，JDK1.5 版本中出现了 <code>ArrayBlockingQueue</code> 和 <code>LinkedBlockingQueue</code>，它们是带有生产者-消费者模式实现的并发容器。其中，<code>ArrayBlockingQueue</code> 是有界队列，即添加的元素达到上限之后，再次添加就会被阻塞或者抛出异常。而 <code>LinkedBlockingQueue</code> 则由链表构成的队列，正是因为链表的特性，所以 <code>LinkedBlockingQueue</code> 在添加元素上并不会向 <code>ArrayBlockingQueue</code> 那样有着较多的约束，所以 <code>LinkedBlockingQueue</code> 设置队列是否有界是可选的(注意这里的无界并不是指可以添加任意数量的元素，而是说队列的大小默认为 <code>Integer.MAX_VALUE</code>，近乎于无限大)。</p>\n<p>随着 Java 的不断发展，JDK 后续的几个版本又对阻塞队列进行了不少的更新和完善:</p>\n<ol>\n<li>JDK1.6 版本:增加 <code>SynchronousQueue</code>，一个不存储元素的阻塞队列。</li>\n<li>JDK1.7 版本:增加 <code>TransferQueue</code>，一个支持更多操作的阻塞队列。</li>\n<li>JDK1.8 版本:增加 <code>DelayQueue</code>，一个支持延迟获取元素的阻塞队列。</li>\n</ol>\n<h3>阻塞队列的思想</h3>\n<p>阻塞队列就是典型的生产者-消费者模型，它可以做到以下几点:</p>\n<ol>\n<li>当阻塞队列数据为空时，所有的消费者线程都会被阻塞，等待队列非空。</li>\n<li>当生产者往队列里填充数据后，队列就会通知消费者队列非空，消费者此时就可以进来消费。</li>\n<li>当阻塞队列因为消费者消费过慢或者生产者存放元素过快导致队列填满时无法容纳新元素时，生产者就会被阻塞，等待队列非满时继续存放元素。</li>\n<li>当消费者从队列中消费一个元素之后，队列就会通知生产者队列非满，生产者可以继续填充数据了。</li>\n</ol>\n<p>总结一下：阻塞队列就说基于非空和非满两个条件实现生产者和消费者之间的交互，尽管这些交互流程和等待通知的机制实现非常复杂，好在 Doug Lea 的操刀之下已将阻塞队列的细节屏蔽，我们只需调用 <code>put</code>、<code>take</code>、<code>offer</code>、<code>poll</code> 等 API 即可实现多线程之间的生产和消费。</p>\n<p>这也使得阻塞队列在多线程开发中有着广泛的运用，最常见的例子无非是我们的线程池,从源码中我们就能看出当核心线程无法及时处理任务时，这些任务都会扔到 <code>workQueue</code> 中。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ThreadPoolExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> corePoolSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                            int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> maximumPoolSize</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                            long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> keepAliveTime</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">                            TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> unit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">                            BlockingQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">Runnable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> workQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">                            ThreadFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> threadFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">                            RejectedExecutionHandler</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> handler) {</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// ...}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>ArrayBlockingQueue 常见方法及测试</h2>\n<p>简单了解了阻塞队列的历史之后，我们就开始重点讨论本篇文章所要介绍的并发容器——<code>ArrayBlockingQueue</code>。为了后续更加深入的了解 <code>ArrayBlockingQueue</code>，我们不妨基于下面几个实例了解以下 <code>ArrayBlockingQueue</code> 的使用。</p>\n<p>先看看第一个例子，我们这里会用两个线程分别模拟生产者和消费者，生产者生产完会使用 <code>put</code> 方法生产 10 个元素给消费者进行消费，当队列元素达到我们设置的上限 5 时，<code>put</code> 方法就会阻塞。<br>\n同理消费者也会通过 <code>take</code> 方法消费元素，当队列为空时，<code>take</code> 方法就会阻塞消费者线程。这里笔者为了保证消费者能够在消费完 10 个元素后及时退出。便通过倒计时门闩，来控制消费者结束，生产者在这里只会生产 10 个元素。当消费者将 10 个元素消费完成之后，按下倒计时门闩，所有线程都会停止。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ProducerConsumerExample</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> throws</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> InterruptedException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 创建一个大小为 5 的 ArrayBlockingQueue</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        ArrayBlockingQueue</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ArrayBlockingQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;>(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">5</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 创建生产者线程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> producer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(() </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">; i++) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                    // 向队列中添加元素，如果队列已满则阻塞等待</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                    queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"生产者添加元素：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">InterruptedException</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">printStackTrace</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        });</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        CountDownLatch</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> countDownLatch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> CountDownLatch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 创建消费者线程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> consumer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(() </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> count</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                    // 从队列中取出元素，如果队列为空则阻塞等待</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> element</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">take</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"消费者取出元素：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> element);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                    ++count;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (count </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                        break</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                countDownLatch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">countDown</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">InterruptedException</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">printStackTrace</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        });</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 启动线程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        producer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        consumer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 等待线程结束</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        producer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">join</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        consumer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">join</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        countDownLatch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">await</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        producer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">interrupt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        consumer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">interrupt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>代码输出结果如下，可以看到只有生产者往队列中投放元素之后消费者才能消费，这也就意味着当队列中没有数据的时消费者就会阻塞，等待队列非空再继续消费。</p>\n<div class=\"language-cpp line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"cpp\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-cpp\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">生产者添加元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">生产者添加元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">消费者取出元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">消费者取出元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">生产者添加元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">消费者取出元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">生产者添加元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">4</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">生产者添加元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">5</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">消费者取出元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">4</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">生产者添加元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">6</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">消费者取出元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">5</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">生产者添加元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">7</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">生产者添加元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">8</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">生产者添加元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">9</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">生产者添加元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">消费者取出元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">6</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">消费者取出元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">7</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">消费者取出元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">8</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">消费者取出元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">9</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">消费者取出元素：</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>了解了 <code>put</code>、<code>take</code> 这两个会阻塞的存和取方法之后，我我们再来看看阻塞队列中非阻塞的入队和出队方法 <code>offer</code> 和 <code>poll</code>。</p>\n<p>如下所示，我们设置了一个大小为 3 的阻塞队列，我们会尝试在队列用 offer 方法存放 4 个元素，然后再从队列中用 <code>poll</code> 尝试取 4 次。</p>\n<div class=\"language-cpp line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"cpp\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-cpp\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">public </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> OfferPollExample</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    public </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(String[] args) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 创建一个大小为 3 的 ArrayBlockingQueue</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        ArrayBlockingQueue</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">String</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> queue </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ArrayBlockingQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;>(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 向队列中添加元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">offer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"A\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">offer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"B\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">offer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"C\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">));</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 尝试向队列中添加元素，但队列已满，返回 false</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">offer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"D\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">));</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 从队列中取出元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">poll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">poll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">poll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 尝试从队列中取出元素，但队列已空，返回 null</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">poll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>最终代码的输出结果如下，可以看到因为队列的大小为 3 的缘故，我们前 3 次存放到队列的结果为 true，第 4 次存放时，由于队列已满，所以存放结果返回 false。这也是为什么我们后续的 <code>poll</code> 方法只得到了 3 个元素的值。</p>\n<div class=\"language-cpp line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"cpp\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-cpp\"><span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">true</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">true</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">true</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">false</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">A</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">B</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">C</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">null</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>了解了阻塞存取和非阻塞存取，我们再来看看阻塞队列的一个比较特殊的操作，某些场景下，我们希望能够一次性将阻塞队列的结果存到列表中再进行批量操作，我们就可以使用阻塞队列的 <code>drainTo</code> 方法，这个方法会一次性将队列中所有元素存放到列表，如果队列中有元素，且成功存到 list 中则 <code>drainTo</code> 会返回本次转移到 list 中的元素数，反之若队列为空，<code>drainTo</code> 则直接返回 0。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> DrainToExample</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> main</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">args</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 创建一个大小为 5 的 ArrayBlockingQueue</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        ArrayBlockingQueue</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ArrayBlockingQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;>(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">5</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 向队列中添加元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">4</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">5</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 创建一个 List，用于存储从队列中取出的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        List</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ArrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;>();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 从队列中取出所有元素，并添加到 List 中</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        queue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">drainTo</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(list);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 输出 List 中的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(list);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>代码输出结果如下</p>\n<div class=\"language-cpp line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"cpp\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-cpp\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">4</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">5</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">]</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><h2>ArrayBlockingQueue 源码分析</h2>\n<p>自此我们对阻塞队列的使用有了基本的印象，接下来我们就可以进一步了解一下 <code>ArrayBlockingQueue</code> 的工作机制了。</p>\n<h3>整体设计</h3>\n<p>在了解 <code>ArrayBlockingQueue</code> 的具体细节之前，我们先来看看 <code>ArrayBlockingQueue</code> 的类图。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/arrayblockingqueue-class-diagram.png\" alt=\"ArrayBlockingQueue 类图\"></p>\n<p>从图中我们可以看出，<code>ArrayBlockingQueue</code> 实现了阻塞队列 <code>BlockingQueue</code> 这个接口，不难猜出通过实现 <code>BlockingQueue</code> 这个接口之后，<code>ArrayBlockingQueue</code> 就拥有了阻塞队列那些常见的操作行为。</p>\n<p>同时， <code>ArrayBlockingQueue</code> 还继承了 <code>AbstractQueue</code> 这个抽象类，这个继承了 <code>AbstractCollection</code> 和 <code>Queue</code> 的抽象类，从抽象类的特定和语义我们也可以猜出，这个继承关系使得 <code>ArrayBlockingQueue</code> 拥有了队列的常见操作。</p>\n<p>所以我们是否可以得出这样一个结论，通过继承 <code>AbstractQueue</code> 获得队列所有的操作模板，其实现的入队和出队操作的整体框架。然后 <code>ArrayBlockingQueue</code> 通过实现 <code>BlockingQueue</code> 获取到阻塞队列的常见操作并将这些操作实现，填充到 <code>AbstractQueue</code> 模板方法的细节中，由此 <code>ArrayBlockingQueue</code> 成为一个完整的阻塞队列。</p>\n<p>为了印证这一点，我们到源码中一探究竟。首先我们先来看看 <code>AbstractQueue</code>，从类的继承关系我们可以大致得出，它通过 <code>AbstractCollection</code> 获得了集合的常见操作方法，然后通过 <code>Queue</code> 接口获得了队列的特性。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> abstract</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AbstractQueue</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AbstractCollection</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Queue</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">       //...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>对于集合的操作无非是增删改查，所以我们不妨从添加方法入手，从源码中我们可以看到，它实现了 <code>AbstractCollection</code> 的 <code>add</code> 方法，其内部逻辑如下:</p>\n<ol>\n<li>调用继承 <code>Queue</code> 接口得来的 <code>offer</code> 方法，如果 <code>offer</code> 成功则返回 <code>true</code>。</li>\n<li>如果 <code>offer</code> 失败，即代表当前元素入队失败直接抛异常。</li>\n</ol>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">offer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e))</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">      return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">      throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> IllegalStateException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Queue full\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>而 <code>AbstractQueue</code> 中并没有对 <code>Queue</code> 的 <code>offer</code> 的实现，很明显这样做的目的是定义好了 <code>add</code> 的核心逻辑，将 <code>offer</code> 的细节交由其子类即我们的 <code>ArrayBlockingQueue</code> 实现。</p>\n<p>到此，我们对于抽象类 <code>AbstractQueue</code> 的分析就结束了，我们继续看看 <code>ArrayBlockingQueue</code> 中实现的另一个重要接口 <code>BlockingQueue</code>。</p>\n<p>点开 <code>BlockingQueue</code> 之后，我们可以看到这个接口同样继承了 <code>Queue</code> 接口，这就意味着它也具备了队列所拥有的所有行为。同时，它还定义了自己所需要实现的方法。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> interface</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> BlockingQueue</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Queue</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //元素入队成功返回true，反之则会抛出异常IllegalStateException</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //元素入队成功返回true，反之返回false</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> offer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //元素入队成功则直接返回，如果队列已满元素不可入队则将线程阻塞，因为阻塞期间可能会被打断，所以这里方法签名抛出了InterruptedException</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> throws</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> InterruptedException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   //和上一个方法一样,只不过队列满时只会阻塞单位为unit，时间为timeout的时长，如果在等待时长内没有入队成功则直接返回false。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> offer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> timeout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> unit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        throws</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> InterruptedException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //从队头取出一个元素，如果队列为空则阻塞等待，因为会阻塞线程的缘故，所以该方法可能会被打断，所以签名定义了InterruptedException</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> take</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> throws</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> InterruptedException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      //取出队头的元素并返回，如果当前队列为空则阻塞等待timeout且单位为unit的时长，如果这个时间段没有元素则直接返回null。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> poll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> timeout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> unit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        throws</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> InterruptedException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      //获取队列剩余元素个数</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> remainingCapacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //删除我们指定的对象，如果成功返回true，反之返回false。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> remove</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //判断队列中是否包含指定元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> contains</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     //将队列中的元素全部存到指定的集合中</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> drainTo</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Collection</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> super</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">c</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //转移maxElements个元素到集合中</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> drainTo</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Collection</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> super</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">c</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> maxElements</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>了解了 <code>BlockingQueue</code> 的常见操作后，我们就知道了 <code>ArrayBlockingQueue</code> 通过实现 <code>BlockingQueue</code> 的方法并重写后，填充到 <code>AbstractQueue</code> 的方法上，由此我们便知道了上文中 <code>AbstractQueue</code> 的 <code>add</code> 方法的 <code>offer</code> 方法是哪里是实现的了。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //AbstractQueue的offer来自下层的ArrayBlockingQueue从BlockingQueue实现并重写的offer方法</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">offer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e))</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">      return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">      throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> IllegalStateException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Queue full\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>初始化</h3>\n<p>了解 <code>ArrayBlockingQueue</code> 的细节前，我们不妨先看看其构造函数，了解一下其初始化过程。从源码中我们可以看出 <code>ArrayBlockingQueue</code> 有 3 个构造方法，而最核心的构造方法就是下方这一个。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// capacity 表示队列初始容量，fair 表示 锁的公平性</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ArrayBlockingQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> capacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> fair) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //如果设置的队列大小小于0，则直接抛出IllegalArgumentException</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (capacity </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">      throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> IllegalArgumentException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //初始化一个数组用于存放队列的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">  this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">items</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[capacity]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //创建阻塞队列流程控制的锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">  lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ReentrantLock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(fair)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //用lock锁创建两个条件控制队列生产和消费</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">  notEmpty </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newCondition</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">  notFull </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">  lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newCondition</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这个构造方法里面有两个比较核心的成员变量 <code>notEmpty</code>(非空) 和 <code>notFull</code> （非满） ，需要我们格外留意，它们是实现生产者和消费者有序工作的关键所在，这一点笔者会在后续的源码解析中详细说明，这里我们只需初步了解一下阻塞队列的构造即可。</p>\n<p>另外两个构造方法都是基于上述的构造方法，默认情况下，我们会使用下面这个构造方法，该构造方法就意味着 <code>ArrayBlockingQueue</code> 用的是非公平锁，即各个生产者或者消费者线程收到通知后，对于锁的争抢是随机的。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ArrayBlockingQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> capacity) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(capacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>还有一个不怎么常用的构造方法，在初始化容量和锁的非公平性之后，它还提供了一个 <code>Collection</code> 参数，从源码中不难看出这个构造方法是将外部传入的集合的元素在初始化时直接存放到阻塞队列中。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ArrayBlockingQueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> capacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> fair</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">                              Collection</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> extends E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //初始化容量和锁的公平性</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">  this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(capacity</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> fair)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //上锁并将c中的元素存放到ArrayBlockingQueue底层的数组中</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">  lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">      int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">      try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                //遍历并添加元素到数组中</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">          for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> e </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">              checkNotNull</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">              items[i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">          }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">      } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">catch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ArrayIndexOutOfBoundsException</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">          throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> IllegalArgumentException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">      }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      //记录当前队列容量</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">      count </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                      //更新下一次put或者offer或用add方法添加到队列底层数组的位置</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">      putIndex </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> capacity) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> :</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">  } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      //完成遍历后释放锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">      lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>阻塞式获取和新增元素</h3>\n<p><code>ArrayBlockingQueue</code> 阻塞式获取和新增元素对应的就是生产者-消费者模型，虽然它也支持非阻塞式获取和新增元素（例如 <code>poll()</code> 和 <code>offer(E e)</code> 方法，后文会介绍到），但一般不会使用。</p>\n<p><code>ArrayBlockingQueue</code> 阻塞式获取和新增元素的方法为：</p>\n<ul>\n<li><code>put(E e)</code>：将元素插入队列中，如果队列已满，则该方法会一直阻塞，直到队列有空间可用或者线程被中断。</li>\n<li><code>take()</code> ：获取并移除队列头部的元素，如果队列为空，则该方法会一直阻塞，直到队列非空或者线程被中断。</li>\n</ul>\n<p>这两个方法实现的关键就是在于两个条件对象 <code>notEmpty</code>(非空) 和 <code>notFull</code> （非满），这个我们在上文的构造方法中有提到。</p>\n<p>接下来笔者就通过两张图让大家了解一下这两个条件是如何在阻塞队列中运用的。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/ArrayBlockingQueue-notEmpty-take.png\" alt=\"ArrayBlockingQueue 非空条件\"></p>\n<p>假设我们的代码消费者先启动，当它发现队列中没有数据，那么非空条件就会将这个线程挂起，即等待条件非空时挂起。然后 CPU 执行权到达生产者，生产者发现队列中可以存放数据，于是将数据存放进去，通知此时条件非空，此时消费者就会被唤醒到队列中使用 <code>take</code> 等方法获取值了。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/ArrayBlockingQueue-notFull-put.png\" alt=\"ArrayBlockingQueue 非满条件\"></p>\n<p>随后的执行中，生产者生产速度远远大于消费者消费速度，于是生产者将队列塞满后再次尝试将数据存入队列，发现队列已满，于是阻塞队列就将当前线程挂起，等待非满。然后消费者拿着 CPU 执行权进行消费，于是队列可以存放新数据了，发出一个非满的通知，此时挂起的生产者就会等待 CPU 执行权到来时再次尝试将数据存到队列中。</p>\n<p>简单了解阻塞队列的基于两个条件的交互流程之后，我们不妨看看 <code>put</code> 和 <code>take</code> 方法的源码。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) throws InterruptedException {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //确保插入的元素不为null</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    checkNotNull</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //加锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //这里使用lockInterruptibly()方法而不是lock()方法是为了能够响应中断操作，如果在等待获取锁的过程中被打断则该方法会抛出InterruptedException异常。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lockInterruptibly</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //如果count等数组长度则说明队列已满，当前线程将被挂起放到AQS队列中，等待队列非满时插入（非满条件）。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">       //在等待期间，锁会被释放，其他线程可以继续对队列进行操作。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (count </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> items</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            notFull</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">await</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">           //如果队列可以存放元素，则调用enqueue将元素入队</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        enqueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //释放锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>put</code>方法内部调用了 <code>enqueue</code> 方法来实现元素入队，我们继续深入查看一下 <code>enqueue</code> 方法的实现细节：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> enqueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   //获取队列底层的数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] items </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">items</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //将putindex位置的值设置为我们传入的x</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    items[putIndex] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //更新putindex，如果putindex等于数组长度，则更新为0</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">putIndex </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> items</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        putIndex </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //队列长度+1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    count</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //通知队列非空，那些因为获取元素而阻塞的线程可以继续工作了</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    notEmpty</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">signal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>从源码中可以看到入队操作的逻辑就是在数组中追加一个新元素，整体执行步骤为:</p>\n<ol>\n<li>获取 <code>ArrayBlockingQueue</code> 底层的数组 <code>items</code>。</li>\n<li>将元素存到 <code>putIndex</code> 位置。</li>\n<li>更新 <code>putIndex</code> 到下一个位置，如果 <code>putIndex</code> 等于队列长度，则说明 <code>putIndex</code> 已经到达数组末尾了，下一次插入则需要 0 开始。(<code>ArrayBlockingQueue</code> 用到了循环队列的思想，即从头到尾循环复用一个数组)</li>\n<li>更新 <code>count</code> 的值，表示当前队列长度+1。</li>\n<li>调用 <code>notEmpty.signal()</code> 通知队列非空，消费者可以从队列中获取值了。</li>\n</ol>\n<p>自此我们了解了 <code>put</code> 方法的流程，为了更加完整的了解 <code>ArrayBlockingQueue</code> 关于生产者-消费者模型的设计，我们继续看看阻塞获取队列元素的 <code>take</code> 方法。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> take</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() throws InterruptedException {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">       //获取锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">     lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lockInterruptibly</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">     try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">             //如果队列中元素个数为0，则将当前线程打断并存入AQS队列中，等待队列非空时获取并移除元素（非空条件）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">         while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (count </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">             notEmpty</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">await</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //如果队列不为空则调用dequeue获取元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">         return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> dequeue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">     } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">          //释放锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">         lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">     }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>理解了 <code>put</code> 方法再看<code>take</code> 方法就很简单了，其核心逻辑和<code>put</code> 方法正好是相反的，比如<code>put</code> 方法在队列满的时候等待队列非满时插入元素（非满条件），而<code>take</code> 方法等待队列非空时获取并移除元素（非空条件）。</p>\n<p><code>take</code>方法内部调用了 <code>dequeue</code> 方法来实现元素出队，其核心逻辑和 <code>enqueue</code> 方法也是相反的。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> dequeue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //获取阻塞队列底层的数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] items </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">items</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">SuppressWarnings</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"unchecked\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //从队列中获取takeIndex位置的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (E) items[takeIndex]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //将takeIndex置空</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">  items[takeIndex] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //takeIndex向后挪动，如果等于数组长度则更新为0</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">takeIndex </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> items</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">      takeIndex </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //队列长度减1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">  count</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">--;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (itrs </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">      itrs</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">elementDequeued</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //通知那些被打断的线程当前队列状态非满，可以继续存放元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">  notFull</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">signal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>由于<code>dequeue</code> 方法（出队）和上面介绍的 <code>enqueue</code> 方法（入队）的步骤大致类似，这里就不重复介绍了。</p>\n<p>为了帮助理解，我专门画了一张图来展示 <code>notEmpty</code>(非空) 和 <code>notFull</code> （非满）这两个条件对象是如何控制 <code>ArrayBlockingQueue</code> 的存和取的。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/ArrayBlockingQueue-notEmpty-notFull.png\" alt=\"ArrayBlockingQueue 非空非满\"></p>\n<ul>\n<li><strong>消费者</strong>：当消费者从队列中 <code>take</code> 或者 <code>poll</code> 等操作取出一个元素之后，就会通知队列非满，此时那些等待非满的生产者就会被唤醒等待获取 CPU 时间片进行入队操作。</li>\n<li><strong>生产者</strong>：当生产者将元素存到队列中后，就会触发通知队列非空，此时消费者就会被唤醒等待 CPU 时间片尝试获取元素。如此往复，两个条件对象就构成一个环路，控制着多线程之间的存和取。</li>\n</ul>\n<h3>非阻塞式获取和新增元素</h3>\n<p><code>ArrayBlockingQueue</code> 非阻塞式获取和新增元素的方法为：</p>\n<ul>\n<li><code>offer(E e)</code>：将元素插入队列尾部。如果队列已满，则该方法会直接返回 false，不会等待并阻塞线程。</li>\n<li><code>poll()</code>：获取并移除队列头部的元素，如果队列为空，则该方法会直接返回 null，不会等待并阻塞线程。</li>\n<li><code>add(E e)</code>：将元素插入队列尾部。如果队列已满则会抛出 <code>IllegalStateException</code> 异常，底层基于 <code>offer(E e)</code> 方法。</li>\n<li><code>remove()</code>：移除队列头部的元素，如果队列为空则会抛出 <code>NoSuchElementException</code> 异常，底层基于 <code>poll()</code>。</li>\n<li><code>peek()</code>：获取但不移除队列头部的元素，如果队列为空，则该方法会直接返回 null，不会等待并阻塞线程。</li>\n</ul>\n<p>先来看看 <code>offer</code> 方法，逻辑和 <code>put</code> 差不多，唯一的区别就是入队失败时不会阻塞当前线程，而是直接返回 <code>false</code>。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> offer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //确保插入的元素不为null</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        checkNotNull</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //获取锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">             //队列已满直接返回false</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (count </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> items</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                //反之将元素入队并直接返回true</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">                enqueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //释放锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>poll</code> 方法同理，获取元素失败也是直接返回空，并不会阻塞获取元素的线程。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> poll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //上锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //如果队列为空直接返回null，反之出队返回元素值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (count </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> :</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> dequeue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>add</code> 方法其实就是对于 <code>offer</code> 做了一层封装，如下代码所示，可以看到 <code>add</code> 会调用没有规定时间的 <code>offer</code>，如果入队失败则直接抛异常。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> super</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(e);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //调用offer方法如果失败直接抛出异常</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">offer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e))</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> IllegalStateException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Queue full\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>remove</code> 方法同理，调用 <code>poll</code>，如果返回 <code>null</code> 则说明队列没有元素，直接抛出异常。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> remove</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> poll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> NoSuchElementException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>peek()</code> 方法的逻辑也很简单，内部调用了 <code>itemAt</code> 方法。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> peek</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //加锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //当队列为空时返回 null</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> itemAt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(takeIndex)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //释放锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//返回队列中指定位置的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">SuppressWarnings</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"unchecked\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> itemAt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (E) items[i]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>指定超时时间内阻塞式获取和新增元素</h3>\n<p>在 <code>offer(E e)</code> 和 <code>poll()</code> 非阻塞获取和新增元素的基础上，设计者提供了带有等待时间的 <code>offer(E e, long timeout, TimeUnit unit)</code> 和 <code>poll(long timeout, TimeUnit unit)</code> ，用于在指定的超时时间内阻塞式地添加和获取元素。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> offer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> timeout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> unit)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        throws InterruptedException {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        checkNotNull</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        long</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> nanos </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> unit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toNanos</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(timeout);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lockInterruptibly</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //队列已满，进入循环</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (count </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> items</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //时间到了队列还是满的，则直接返回false</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (nanos </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                    return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                 //阻塞nanos时间，等待非满</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                nanos </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> notFull</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">awaitNanos</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(nanos);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">            enqueue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>可以看到，带有超时时间的 <code>offer</code> 方法在队列已满的情况下，会等待用户所传的时间段，如果规定时间内还不能存放元素则直接返回 <code>false</code>。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> poll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> timeout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> unit) throws InterruptedException {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        long</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> nanos </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> unit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toNanos</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(timeout);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lockInterruptibly</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">          //队列为空，循环等待，若时间到还是空的，则直接返回null</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (count </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (nanos </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                    return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                nanos </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> notEmpty</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">awaitNanos</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(nanos);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> dequeue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>同理，带有超时时间的 <code>poll</code> 也一样，队列为空则在规定时间内等待，若时间到了还是空的，则直接返回 null。</p>\n<h3>判断元素是否存在</h3>\n<p><code>ArrayBlockingQueue</code> 提供了 <code>contains(Object o)</code> 来判断指定元素是否存在于队列中。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> contains</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> o) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //若目标元素为空，则直接返回 false</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //获取当前队列的元素数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] items </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">items</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //加锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 如果队列非空</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (count </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> putIndex </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">putIndex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //从队列头部开始遍历</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> takeIndex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            do</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">equals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(items[i])</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                    return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> items</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">                    i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">while</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> putIndex)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //释放锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>ArrayBlockingQueue 获取和新增元素的方法对比</h2>\n<p>为了帮助理解 <code>ArrayBlockingQueue</code> ，我们再来对比一下上面提到的这些获取和新增元素的方法。</p>\n<p>新增元素：</p>\n<p>| 方法                                      | 队列满时处理方式                                         | 方法返回值 |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/collection/arrayblockingqueue-class-diagram.png",
      "date_published": "2023-06-21T05:03:13.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "CopyOnWriteArrayList 源码分析",
      "url": "https://javaguide.cn/java/collection/copyonwritearraylist-source-code.html",
      "id": "https://javaguide.cn/java/collection/copyonwritearraylist-source-code.html",
      "summary": "CopyOnWriteArrayList源码深度解析：详解写时复制COW机制、适用读多写少场景、线程安全List实现、快照一致性保证及内存开销权衡。",
      "content_html": "<h2>CopyOnWriteArrayList 简介</h2>\n<p>在 JDK1.5 之前，如果想要使用并发安全的 <code>List</code> 只能选择 <code>Vector</code>。而 <code>Vector</code> 是一种老旧的集合，已经被淘汰。<code>Vector</code> 对于增删改查等方法基本都加了 <code>synchronized</code>，这种方式虽然能够保证同步，但这相当于对整个 <code>Vector</code> 加上了一把大锁，使得每个方法执行的时候都要去获得锁，导致性能非常低下。</p>\n<p>JDK1.5 引入了 <code>Java.util.concurrent</code>（JUC）包，其中提供了很多线程安全且并发性能良好的容器，其中唯一的线程安全 <code>List</code> 实现就是 <code>CopyOnWriteArrayList</code> 。关于<code>java.util.concurrent</code> 包下常见并发容器的总结，可以看我写的这篇文章：<a href=\"https://javaguide.cn/java/concurrent/java-concurrent-collections.html\" target=\"_blank\" rel=\"noopener noreferrer\">Java 常见并发容器总结</a> 。</p>\n<h3>CopyOnWriteArrayList 到底有什么厉害之处？</h3>\n<p>对于大部分业务场景来说，读取操作往往是远大于写入操作的。由于读取操作不会对原有数据进行修改，因此，对于每次读取都进行加锁其实是一种资源浪费。相比之下，我们应该允许多个线程同时访问 <code>List</code> 的内部数据，毕竟对于读取操作来说是安全的。</p>\n<p>这种思路与 <code>ReentrantReadWriteLock</code> 读写锁的设计思想非常类似，即读读不互斥、读写互斥、写写互斥（只有读读不互斥）。<code>CopyOnWriteArrayList</code> 更进一步地实现了这一思想。为了将读操作性能发挥到极致，<code>CopyOnWriteArrayList</code> 中的读取操作是完全无需加锁的。更加厉害的是，写入操作也不会阻塞读取操作，只有写写才会互斥。这样一来，读操作的性能就可以大幅度提升。</p>\n<p><code>CopyOnWriteArrayList</code> 线程安全的核心在于其采用了 <strong>写时复制（Copy-On-Write）</strong> 的策略，从 <code>CopyOnWriteArrayList</code> 的名字就能看出了。</p>\n<h3>Copy-On-Write 的思想是什么？</h3>\n<p><code>CopyOnWriteArrayList</code>名字中的“Copy-On-Write”即写时复制，简称 COW。</p>\n<p>下面是维基百科对 Copy-On-Write 的介绍，介绍的挺不错：</p>\n<blockquote>\n<p>写入时复制（英语：Copy-on-write，简称 COW）是一种计算机程序设计领域的优化策略。其核心思想是，如果有多个调用者（callers）同时请求相同资源（如内存或磁盘上的数据存储），他们会共同获取相同的指针指向相同的资源，直到某个调用者试图修改资源的内容时，系统才会真正复制一份专用副本（private copy）给该调用者，而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的。此作法主要的优点是如果调用者没有修改该资源，就不会有副本（private copy）被创建，因此多个调用者只是读取操作时可以共享同一份资源。</p>\n</blockquote>\n<p>这里再以 <code>CopyOnWriteArrayList</code>为例介绍：当需要修改（ <code>add</code>，<code>set</code>、<code>remove</code> 等操作） <code>CopyOnWriteArrayList</code> 的内容时，不会直接修改原数组，而是会先创建底层数组的副本，对副本数组进行修改，修改完之后再将修改后的数组赋值回去，这样就可以保证写操作不会影响读操作了。</p>\n<p>可以看出，写时复制机制非常适合读多写少的并发场景，能够极大地提高系统的并发性能。</p>\n<p>不过，写时复制机制并不是银弹，其依然存在一些缺点，下面列举几点：</p>\n<ol>\n<li>内存占用：每次写操作都需要复制一份原始数据，会占用额外的内存空间，在数据量比较大的情况下，可能会导致内存资源不足。</li>\n<li>写操作开销：每一次写操作都需要复制一份原始数据，然后再进行修改和替换，所以写操作的开销相对较大，在写入比较频繁的场景下，性能可能会受到影响。</li>\n<li>数据一致性问题：修改操作不会立即反映到最终结果中，还需要等待复制完成，这可能会导致一定的数据一致性问题。</li>\n<li>……</li>\n</ol>\n<h2>CopyOnWriteArrayList 源码分析</h2>\n<p>这里以 JDK1.8 为例，分析一下 <code>CopyOnWriteArrayList</code> 的底层核心源码。</p>\n<p><code>CopyOnWriteArrayList</code> 的类定义如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CopyOnWriteArrayList</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> List</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">>,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> RandomAccess</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Cloneable</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Serializable</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">{</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>CopyOnWriteArrayList</code> 实现了以下接口：</p>\n<ul>\n<li><code>List</code> : 表明它是一个列表，支持添加、删除、查找等操作，并且可以通过下标进行访问。</li>\n<li><code>RandomAccess</code> ：这是一个标志接口，表明实现这个接口的 <code>List</code> 集合是支持 <strong>快速随机访问</strong> 的。</li>\n<li><code>Cloneable</code> ：表明它具有拷贝能力，可以进行深拷贝或浅拷贝操作。</li>\n<li><code>Serializable</code> : 表明它可以进行序列化操作，也就是可以将对象转换为字节流进行持久化存储或网络传输，非常方便。</li>\n</ul>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/copyonwritearraylist-class-diagram.png\" alt=\"CopyOnWriteArrayList 类图\"></p>\n<h3>初始化</h3>\n<p><code>CopyOnWriteArrayList</code> 中有一个无参构造函数和两个有参构造函数。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建一个空的 CopyOnWriteArrayList</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> CopyOnWriteArrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    setArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">])</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 按照集合的迭代器返回的顺序创建一个包含指定集合元素的 CopyOnWriteArrayList</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> CopyOnWriteArrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Collection</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> extends E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] elements</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">c</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ==</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> CopyOnWriteArrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        elements </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> ((</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">CopyOnWriteArrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)c)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        elements </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> c</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // c.toArray might (incorrectly) not return Object[] (see 6260652)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">elements</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getClass</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> !=</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            elements </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Arrays</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">copyOf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(elements, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">elements</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    setArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(elements)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建一个包含指定数组的副本的列表</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> CopyOnWriteArrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] toCopyIn) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    setArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Arrays</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">copyOf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(toCopyIn, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">toCopyIn</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[].</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">class</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>插入元素</h3>\n<p><code>CopyOnWriteArrayList</code> 的 <code>add()</code>方法有三个版本：</p>\n<ul>\n<li><code>add(E e)</code>：在 <code>CopyOnWriteArrayList</code> 的尾部插入元素。</li>\n<li><code>add(int index, E element)</code>：在 <code>CopyOnWriteArrayList</code> 的指定位置插入元素。</li>\n<li><code>addIfAbsent(E e)</code>：如果指定元素不存在，那么添加该元素。如果成功添加元素则返回 true。</li>\n</ul>\n<p>这里以<code>add(E e)</code>为例进行介绍：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 插入元素到 CopyOnWriteArrayList 的尾部</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 加锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 获取原来的数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] elements </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 原来数组的长度</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> len </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> elements</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 创建一个长度+1的新数组，并将原来数组的元素复制给新数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] newElements </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Arrays</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">copyOf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(elements, len </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 元素放在新数组末尾</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        newElements[len] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // array指向新数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        setArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(newElements)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 解锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>从上面的源码可以看出：</p>\n<ul>\n<li><code>add</code>方法内部用到了 <code>ReentrantLock</code> 加锁，保证了同步，避免了多线程写的时候会复制出多个副本出来。锁被修饰保证了锁的内存地址肯定不会被修改，并且，释放锁的逻辑放在 <code>finally</code> 中，可以保证锁能被释放。</li>\n<li><code>CopyOnWriteArrayList</code> 通过复制底层数组的方式实现写操作，即先创建一个新的数组来容纳新添加的元素，然后在新数组中进行写操作，最后将新数组赋值给底层数组的引用，替换掉旧的数组。这也就证明了我们前面说的：<code>CopyOnWriteArrayList</code> 线程安全的核心在于其采用了 <strong>写时复制（Copy-On-Write）</strong> 的策略。</li>\n<li>每次写操作都需要通过 <code>Arrays.copyOf</code> 复制底层数组，时间复杂度是 O(n) 的，且会占用额外的内存空间。因此，<code>CopyOnWriteArrayList</code> 适用于读多写少的场景，在写操作不频繁且内存资源充足的情况下，可以提升系统的性能表现。</li>\n<li><code>CopyOnWriteArrayList</code> 中并没有类似于 <code>ArrayList</code> 的 <code>grow()</code> 方法扩容的操作。</li>\n</ul>\n<blockquote>\n<p><code>Arrays.copyOf</code> 方法的时间复杂度是 O(n)，其中 n 表示需要复制的数组长度。因为这个方法的实现原理是先创建一个新的数组，然后将源数组中的数据复制到新数组中，最后返回新数组。这个方法会复制整个数组，因此其时间复杂度与数组长度成正比，即 O(n)。值得注意的是，由于底层调用了系统级别的拷贝指令，因此在实际应用中这个方法的性能表现比较优秀，但是也需要注意控制复制的数据量，避免出现内存占用过高的情况。</p>\n</blockquote>\n<h3>读取元素</h3>\n<p><code>CopyOnWriteArrayList</code> 的读取操作是基于内部数组 <code>array</code> 并没有发生实际的修改，因此在读取操作时不需要进行同步控制和锁操作，可以保证数据的安全性。这种机制下，多个线程可以同时读取列表中的元素。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 底层数组，只能通过getArray和setArray方法访问</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> transient</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> volatile</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] array</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> index) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> index)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> array</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> index) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (E) a[index]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>不过，<code>get</code>方法是弱一致性的，在某些情况下可能读到旧的元素值。</p>\n<p><code>get(int index)</code>方法是分两步进行的：</p>\n<ol>\n<li>通过<code>getArray()</code>获取当前数组的引用；</li>\n<li>直接从数组中获取下标为 index 的元素。</li>\n</ol>\n<p>这个过程并没有加锁，所以在并发环境下可能出现如下情况：</p>\n<ol>\n<li>线程 1 调用<code>get(int index)</code>方法获取值，内部通过<code>getArray()</code>方法获取到了 array 属性值；</li>\n<li>线程 2 调用<code>CopyOnWriteArrayList</code>的<code>add</code>、<code>set</code>、<code>remove</code> 等修改方法时，内部通过<code>setArray</code>方法修改了<code>array</code>属性的值；</li>\n<li>线程 1 还是从旧的 <code>array</code> 数组中取值。</li>\n</ol>\n<h3>获取列表中元素的个数</h3>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>CopyOnWriteArrayList</code>中的<code>array</code>数组每次复制都刚好能够容纳下所有元素，并不像<code>ArrayList</code>那样会预留一定的空间。因此，<code>CopyOnWriteArrayList</code>中并没有<code>size</code>属性<code>CopyOnWriteArrayList</code>的底层数组的长度就是元素个数，因此<code>size()</code>方法只要返回数组长度就可以了。</p>\n<h3>删除元素</h3>\n<p><code>CopyOnWriteArrayList</code>删除元素相关的方法一共有 4 个：</p>\n<ol>\n<li><code>remove(int index)</code>：移除此列表中指定位置上的元素。将任何后续元素向左移动（从它们的索引中减去 1）。</li>\n<li><code>boolean remove(Object o)</code>：删除此列表中首次出现的指定元素，如果不存在该元素则返回 false。</li>\n<li><code>boolean removeAll(Collection&lt;?&gt; c)</code>：从此列表中删除指定集合中包含的所有元素。</li>\n<li><code>void clear()</code>：移除此列表中的所有元素。</li>\n</ol>\n<p>这里以<code>remove(int index)</code>为例进行介绍：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> remove</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> index) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 获取可重入锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ReentrantLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 加锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">         //获取当前array数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] elements </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 获取当前array长度</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> len </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> elements</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //获取指定索引的元素(旧值)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> oldValue </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(elements</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> index)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> numMoved </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> len </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> index </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 判断删除的是否是最后一个元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (numMoved </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">             // 如果删除的是最后一个元素，直接复制该元素前的所有元素到新的数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">            setArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Arrays</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">copyOf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(elements, len </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 分段复制，将index前的元素和index+1后的元素复制到新数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 新数组长度为旧数组长度-1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">            Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] newElements </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[len </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">arraycopy</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(elements, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, newElements, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, index);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">arraycopy</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(elements, index </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, newElements, index,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                             numMoved);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            //将新数组赋值给array引用</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">            setArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(newElements)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> oldValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">         // 解锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>判断元素是否存在</h3>\n<p><code>CopyOnWriteArrayList</code>提供了两个用于判断指定元素是否在列表中的方法：</p>\n<ul>\n<li><code>contains(Object o)</code>：判断是否包含指定元素。</li>\n<li><code>containsAll(Collection&lt;?&gt; c)</code>：判断是否保证指定集合的全部元素。</li>\n</ul>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 判断是否包含指定元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> contains</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> o) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //获取当前array数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] elements </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //调用index尝试查找指定元素，如果返回值大于等于0，则返回true，否则返回false</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> indexOf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> elements</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> elements</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 判断是否保证指定集合的全部元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> containsAll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Collection</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //获取当前array数组</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] elements </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //获取数组长度</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> len </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> elements</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //遍历指定集合</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> e </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        //循环调用indexOf方法判断，只要有一个没有包含就直接返回false</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">indexOf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> elements</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> len) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    //最后表示全部包含或者制定集合为空集合，那么返回true</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>CopyOnWriteArrayList 常用方法测试</h2>\n<p>代码：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建一个 CopyOnWriteArrayList 对象</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">CopyOnWriteArrayList</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> list </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CopyOnWriteArrayList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 向列表中添加元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Java\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Python\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"C++\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"初始列表：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> list);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 使用 get 方法获取指定位置的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"列表第二个元素为：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">));</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 使用 remove 方法删除指定元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">boolean</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> result </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">remove</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"C++\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"删除结果：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> result);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"列表删除元素后为：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> list);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 使用 set 方法更新指定位置的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">set</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Golang\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"列表更新后为：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> list);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 使用 add 方法在指定位置插入元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"PHP\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"列表插入元素后为：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> list);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 使用 size 方法获取列表大小</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"列表大小为：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 使用 removeAll 方法删除指定集合中所有出现的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">result </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">removeAll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">List</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">of</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Java\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Golang\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"批量删除结果：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> result);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"列表批量删除元素后为：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> list);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 使用 clear 方法清空列表中所有元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">clear</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"列表清空后为：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> list);</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>输出：</p>\n<div class=\"language-plain line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"plain\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-plain\"><span class=\"line\"><span>列表更新后为：[Java, Golang]</span></span>\n<span class=\"line\"><span>列表插入元素后为：[PHP, Java, Golang]</span></span>\n<span class=\"line\"><span>列表大小为：3</span></span>\n<span class=\"line\"><span>批量删除结果：true</span></span>\n<span class=\"line\"><span>列表批量删除元素后为：[PHP]</span></span>\n<span class=\"line\"><span>列表清空后为：[]</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/collection/copyonwritearraylist-class-diagram.png",
      "date_published": "2023-06-08T12:34:44.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "LinkedList 源码分析",
      "url": "https://javaguide.cn/java/collection/linkedlist-source-code.html",
      "id": "https://javaguide.cn/java/collection/linkedlist-source-code.html",
      "summary": "LinkedList源码深度解析：剖析双向链表结构、Deque接口实现、头尾插入删除O(1)时间复杂度、与ArrayList性能对比及适用场景。",
      "content_html": "<p><a href=\"/zhuanlan/interview-guide.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/interview-guide-banner.png\" alt=\"《SpringAI 智能面试平台+RAG 知识库》\"></a></p>\n<h2>LinkedList 简介</h2>\n<p><code>LinkedList</code> 是一个基于双向链表实现的集合类，经常被拿来和 <code>ArrayList</code> 做比较。关于 <code>LinkedList</code> 和<code>ArrayList</code>的详细对比，我们 <a href=\"/java/collection/java-collection-questions-01.html\" target=\"_blank\">Java 集合常见面试题总结(上)</a>有详细介绍到。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/data-structure/bidirectional-linkedlist.png\" alt=\"双向链表\"></p>\n<p>不过，我们在项目中一般是不会使用到 <code>LinkedList</code> 的，需要用到 <code>LinkedList</code> 的场景几乎都可以使用 <code>ArrayList</code> 来代替，并且，性能通常会更好！就连 <code>LinkedList</code> 的作者约书亚 · 布洛克（Josh Bloch）自己都说从来不会使用 <code>LinkedList</code> 。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/redisimage-20220412110853807.png\" alt></p>\n<p>另外，不要下意识地认为 <code>LinkedList</code> 作为链表就最适合元素增删的场景。我在上面也说了，<code>LinkedList</code> 仅仅在头尾插入或者删除元素的时候时间复杂度近似 O(1)，其他情况增删元素的平均时间复杂度都是 O(n) 。</p>\n<h3>LinkedList 插入和删除元素的时间复杂度？</h3>\n<ul>\n<li>头部插入/删除：只需要修改头结点的指针即可完成插入/删除操作，因此时间复杂度为 O(1)。</li>\n<li>尾部插入/删除：只需要修改尾结点的指针即可完成插入/删除操作，因此时间复杂度为 O(1)。</li>\n<li>指定位置插入/删除：需要先移动到指定位置，再修改指定节点的指针完成插入/删除，不过由于有头尾指针，可以从较近的指针出发，因此需要遍历平均 n/4 个元素，时间复杂度为 O(n)。</li>\n</ul>\n<h3>LinkedList 为什么不能实现 RandomAccess 接口？</h3>\n<p><code>RandomAccess</code> 是一个标记接口，用来表明实现该接口的类支持随机访问（即可以通过索引快速访问元素）。由于 <code>LinkedList</code> 底层数据结构是链表，内存地址不连续，只能通过指针来定位，不支持随机快速访问，所以不能实现 <code>RandomAccess</code> 接口。</p>\n<h2>LinkedList 源码分析</h2>\n<p>这里以 JDK1.8 为例，分析一下 <code>LinkedList</code> 的底层核心源码。</p>\n<p><code>LinkedList</code> 的类定义如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedList</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    extends</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AbstractSequentialList</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> List</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">>,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Deque</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">>,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Cloneable</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E06C75\"> java.io.</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Serializable</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">{</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  //...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>LinkedList</code> 继承了 <code>AbstractSequentialList</code> ，而 <code>AbstractSequentialList</code> 又继承于 <code>AbstractList</code> 。</p>\n<p>阅读过 <code>ArrayList</code> 的源码我们就知道，<code>ArrayList</code> 同样继承了 <code>AbstractList</code> ， 所以 <code>LinkedList</code> 会有大部分方法和 <code>ArrayList</code> 相似。</p>\n<p><code>LinkedList</code> 实现了以下接口：</p>\n<ul>\n<li><code>List</code> : 表明它是一个列表，支持添加、删除、查找等操作，并且可以通过下标进行访问。</li>\n<li><code>Deque</code> ：继承自 <code>Queue</code> 接口，具有双端队列的特性，支持从两端插入和删除元素，方便实现栈和队列等数据结构。需要注意，<code>Deque</code> 的发音为 &quot;deck&quot; [dɛk]，这个大部分人都会读错。</li>\n<li><code>Cloneable</code> ：表明它具有拷贝能力，可以进行深拷贝或浅拷贝操作。</li>\n<li><code>Serializable</code> : 表明它可以进行序列化操作，也就是可以将对象转换为字节流进行持久化存储或网络传输，非常方便。</li>\n</ul>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/linkedlist--class-diagram.png\" alt=\"LinkedList 类图\"></p>\n<p><code>LinkedList</code> 中的元素是通过 <code>Node</code> 定义的：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> item</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 节点值</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 指向的下一个节点（后继节点）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> prev</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> // 指向的前一个节点（前驱结点）</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 初始化参数顺序分别是：前驱结点、本身节点值、后继节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">prev</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> element</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">item</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> element;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> next;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">prev</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> prev;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>初始化</h3>\n<p><code>LinkedList</code> 中有一个无参构造函数和一个有参构造函数。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建一个空的链表对象</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> LinkedList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 接收一个集合类型作为参数，会创建一个与传入集合相同元素的链表对象</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> LinkedList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Collection</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> extends E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    addAll</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(c)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>插入元素</h3>\n<p><code>LinkedList</code> 除了实现了 <code>List</code> 接口相关方法，还实现了 <code>Deque</code> 接口的很多方法，所以我们有很多种方式插入元素。</p>\n<p>我们这里以 <code>List</code> 接口中相关的插入方法为例进行源码讲解，对应的是<code>add()</code> 方法。</p>\n<p><code>add()</code> 方法有两个版本：</p>\n<ul>\n<li><code>add(E e)</code>：用于在 <code>LinkedList</code> 的尾部插入元素，即将新元素作为链表的最后一个元素，时间复杂度为 O(1)。</li>\n<li><code>add(int index, E element)</code>:用于在指定位置插入元素。这种插入方式需要先移动到指定位置，再修改指定节点的指针完成插入/删除，因此需要移动平均 n/4 个元素，时间复杂度为 O(n)。</li>\n</ul>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 在链表尾部插入元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    linkLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(e)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 在链表指定位置插入元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> index</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> element) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 下标越界检查</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    checkPositionIndex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(index)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 判断 index 是不是链表尾部位置</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (index </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> size)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 如果是就直接调用 linkLast 方法将元素节点插入链表尾部即可</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        linkLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(element)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 如果不是则调用 linkBefore 方法将其插入指定元素之前</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">        linkBefore</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(element</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(index))</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 将元素节点插入到链表尾部</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> linkLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 将最后一个元素赋值（引用传递）给节点 l</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> l </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> last</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 创建节点，并指定节点前驱为链表尾节点 last，后继引用为空</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> newNode </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(l</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 将 last 引用指向新节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    last </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 判断尾节点是否为空</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 如果 l 是null 意味着这是第一次添加元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (l </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 如果是第一次添加，将first赋值为新节点，此时链表只有一个元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        first </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 如果不是第一次添加，将新节点赋值给l（添加前的最后一个元素）的next</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        l</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    modCount</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 在指定元素之前插入元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> linkBefore</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> succ) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // assert succ != null;断言 succ不为 null</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 定义一个节点元素保存 succ 的 prev 引用，也就是它的前一节点信息</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> pred </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> succ</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">prev</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 初始化节点，并指明前驱和后继节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> newNode </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(pred</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> e</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> succ)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 将 succ 节点前驱引用 prev 指向新节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    succ</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">prev</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 判断前驱节点是否为空，为空表示 succ 是第一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (pred </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 新节点成为第一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        first </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // succ 节点前驱的后继引用指向新节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        pred</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> newNode</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    modCount</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>获取元素</h3>\n<p><code>LinkedList</code>获取元素相关的方法一共有 3 个：</p>\n<ol>\n<li><code>getFirst()</code>：获取链表的第一个元素。</li>\n<li><code>getLast()</code>：获取链表的最后一个元素。</li>\n<li><code>get(int index)</code>：获取链表指定位置的元素。</li>\n</ol>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 获取链表的第一个元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> f </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> first</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (f </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> NoSuchElementException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> f</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">item</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 获取链表的最后一个元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> l </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> last</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (l </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> NoSuchElementException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> l</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">item</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 获取链表指定位置的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> index) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 下标越界检查，如果越界就抛异常</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">  checkElementIndex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(index)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 返回链表中对应下标的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(index)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">item</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这里的核心在于 <code>node(int index)</code> 这个方法：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 返回指定下标的非空节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> index) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 断言下标未越界</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // assert isElementIndex(index);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 如果index小于size的二分之一  从前开始查找（向后查找）  反之向前查找</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (index </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (size </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> first</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 遍历，循环向后查找，直至 i == index</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> index</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> last</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> size </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> index</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">--</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">prev</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>get(int index)</code> 或 <code>remove(int index)</code> 等方法内部都调用了该方法来获取对应的节点。</p>\n<p>从这个方法的源码可以看出，该方法通过比较索引值与链表 size 的一半大小来确定从链表头还是尾开始遍历。如果索引值小于 size 的一半，就从链表头开始遍历，反之从链表尾开始遍历。这样可以在较短的时间内找到目标节点，充分利用了双向链表的特性来提高效率。</p>\n<h3>删除元素</h3>\n<p><code>LinkedList</code>删除元素相关的方法一共有 5 个：</p>\n<ol>\n<li><code>removeFirst()</code>：删除并返回链表的第一个元素。</li>\n<li><code>removeLast()</code>：删除并返回链表的最后一个元素。</li>\n<li><code>remove(E e)</code>：删除链表中首次出现的指定元素，如果不存在该元素则返回 false。</li>\n<li><code>remove(int index)</code>：删除指定索引处的元素，并返回该元素的值。</li>\n<li><code>void clear()</code>：移除此链表中的所有元素。</li>\n</ol>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 删除并返回链表的第一个元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> removeFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> f </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> first</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (f </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> NoSuchElementException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> unlinkFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(f)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 删除并返回链表的最后一个元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> removeLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> l </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> last</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (l </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> NoSuchElementException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> unlinkLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(l)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 删除链表中首次出现的指定元素，如果不存在该元素则返回 false</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> remove</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> o) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 如果指定元素为 null，遍历链表找到第一个为 null 的元素进行删除</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> first</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">item</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> ==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">                unlink</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(x)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 如果不为 null ,遍历链表找到要删除的节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> first</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">equals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">item</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">                unlink</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(x)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 删除链表指定位置的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> remove</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> index) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 下标越界检查，如果越界就抛异常</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    checkElementIndex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(index)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> unlink</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(index))</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这里的核心在于 <code>unlink(Node&lt;E&gt; x)</code> 这个方法：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> unlink</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Node</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">E</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> x) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 断言 x 不为 null</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // assert x != null;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 获取当前节点（也就是待删除节点）的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> element </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">item</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 获取当前节点的下一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> next </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 获取当前节点的前一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> prev </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">prev</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 如果前一个节点为空，则说明当前节点是头节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (prev </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 直接让链表头指向当前节点的下一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        first </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> { </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 如果前一个节点不为空</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 将前一个节点的 next 指针指向当前节点的下一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        prev</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 将当前节点的 prev 指针置为 null，，方便 GC 回收</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">prev</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 如果下一个节点为空，则说明当前节点是尾节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (next </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 直接让链表尾指向当前节点的前一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        last </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> prev</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> { </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 如果下一个节点不为空</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 将下一个节点的 prev 指针指向当前节点的前一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">prev</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> prev</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 将当前节点的 next 指针置为 null，方便 GC 回收</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 将当前节点元素置为 null，方便 GC 回收</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    x</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">item</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">--;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    modCount</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> element</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>unlink()</code> 方法的逻辑如下：</p>\n<ol>\n<li>首先获取待删除节点 x 的前驱和后继节点；</li>\n<li>判断待删除节点是否为头节点或尾节点：\n<ul>\n<li>如果 x 是头节点，则将 first 指向 x 的后继节点 next</li>\n<li>如果 x 是尾节点，则将 last 指向 x 的前驱节点 prev</li>\n<li>如果 x 不是头节点也不是尾节点，执行下一步操作</li>\n</ul>\n</li>\n<li>将待删除节点 x 的前驱的后继指向待删除节点的后继 next，断开 x 和 x.prev 之间的链接；</li>\n<li>将待删除节点 x 的后继的前驱指向待删除节点的前驱 prev，断开 x 和 x.next 之间的链接；</li>\n<li>将待删除节点 x 的元素置空，修改链表长度。</li>\n</ol>\n<p>可以参考下图理解（图源：<a href=\"https://www.tianxiaobo.com/2018/01/31/LinkedList-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-JDK-1-8/\" target=\"_blank\" rel=\"noopener noreferrer\">LinkedList 源码分析(JDK 1.8)</a>）：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/collection/linkedlist-unlink.jpg\" alt=\"unlink 方法逻辑\"></p>\n<h3>遍历链表</h3>\n<p>推荐使用<code>for-each</code> 循环来遍历 <code>LinkedList</code> 中的元素， <code>for-each</code> 循环最终会转换成迭代器形式。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">LinkedList</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> list </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"apple\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"banana\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"pear\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> fruit </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> list) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(fruit);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>LinkedList</code> 的遍历的核心就是它的迭代器的实现。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 双向迭代器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ListItr</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ListIterator</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#C18401;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 表示上一次调用 next() 或 previous() 方法时经过的节点；</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lastReturned</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 表示下一个要遍历的节点；</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 表示下一个要遍历的节点的下标，也就是当前节点的后继节点的下标；</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> nextIndex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 表示当前遍历期望的修改计数值，用于和 LinkedList 的 modCount 比较，判断链表是否被其他线程修改过。</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> expectedModCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> modCount</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    …………</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>下面我们对迭代器 <code>ListItr</code> 中的核心方法进行详细介绍。</p>\n<p>我们先来看下从头到尾方向的迭代：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 判断还有没有下一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> hasNext</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 判断下一个节点的下标是否小于链表的大小，如果是则表示还有下一个元素可以遍历</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> nextIndex </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 获取下一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 检查在迭代过程中链表是否被修改过</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    checkForComodification</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 判断是否还有下一个节点可以遍历，如果没有则抛出 NoSuchElementException 异常</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">hasNext</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">())</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> NoSuchElementException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 将 lastReturned 指向当前节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    lastReturned </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 将 next 指向下一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    next </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    nextIndex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> lastReturned</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">item</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>再来看一下从尾到头方向的迭代：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 判断是否还有前一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> hasPrevious</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> nextIndex </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 获取前一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> E</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> previous</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 检查是否在迭代过程中链表被修改</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    checkForComodification</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 如果没有前一个节点，则抛出异常</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">hasPrevious</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">())</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> NoSuchElementException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 将 lastReturned 和 next 指针指向上一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    lastReturned </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> next </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (next </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">?</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> last </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">prev</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    nextIndex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">--;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> lastReturned</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">item</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>如果需要删除或插入元素，也可以使用迭代器进行操作。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">LinkedList</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> list </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"apple\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"banana\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//  Collection 接口的 removeIf 方法底层依然是基于迭代器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">removeIf</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(Objects</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">::</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">isNull);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> fruit </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> list) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(fruit);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>迭代器对应的移除元素的方法如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 从列表中删除上次被返回的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> remove</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 检查是否在迭代过程中链表被修改</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    checkForComodification</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 如果上次返回的节点为空，则抛出异常</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (lastReturned </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> IllegalStateException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 获取当前节点的下一个节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    Node</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">E</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lastNext </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> lastReturned</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">next</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 从链表中删除上次返回的节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">    unlink</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(lastReturned)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 修改指针</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (next </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> lastReturned)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        next </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> lastNext</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        nextIndex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">--;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 将上次返回的节点引用置为 null，方便 GC 回收</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    lastReturned </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    expectedModCount</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>LinkedList 常用方法测试</h2>\n<p>代码：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建 LinkedList 对象</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">LinkedList</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> list </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LinkedList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 添加元素到链表末尾</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"apple\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"banana\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"pear\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"链表内容：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> list);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 在指定位置插入元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"orange\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"链表内容：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> list);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 获取指定位置的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> fruit </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"索引为 2 的元素：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> fruit);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 修改指定位置的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">set</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"grape\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"链表内容：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> list);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 删除指定位置的元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">remove</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"链表内容：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> list);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 删除第一个出现的指定元素</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">remove</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"banana\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"链表内容：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> list);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 获取链表的长度</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> size </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">size</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"链表长度：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> size);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 清空链表</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">list</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">clear</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"清空后的链表：\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> list);</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>输出：</p>\n<div class=\"language-plain line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"plain\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-plain\"><span class=\"line\"><span>索引为 2 的元素：banana</span></span>\n<span class=\"line\"><span>链表内容：[apple, orange, banana, grape]</span></span>\n<span class=\"line\"><span>链表内容：[orange, banana, grape]</span></span>\n<span class=\"line\"><span>链表内容：[orange, grape]</span></span>\n<span class=\"line\"><span>链表长度：2</span></span>\n<span class=\"line\"><span>清空后的链表：[]</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/cs-basics/data-structure/bidirectional-linkedlist.png",
      "date_published": "2023-06-07T05:18:39.000Z",
      "date_modified": "2026-03-10T15:16:51.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "工作五年之后，对技术和业务的思考",
      "url": "https://javaguide.cn/high-quality-technical-articles/advanced-programmer/thinking-about-technology-and-business-after-five-years-of-work.html",
      "id": "https://javaguide.cn/high-quality-technical-articles/advanced-programmer/thinking-about-technology-and-business-after-five-years-of-work.html",
      "summary": "工作五年之后，对技术和业务的思考：围绕技术知识与面试总结梳理关键概念、常见问题与实践要点，帮助你高效学习与备战面试。",
      "content_html": "<blockquote>\n<p><strong>推荐语</strong>：这是我在两年前看到的一篇对我触动比较深的文章。确实要学会适应变化，并积累能力。积累解决问题的能力，优化思考方式，拓宽自己的认知。</p>\n<p><strong>原文地址：</strong> <a href=\"https://mp.weixin.qq.com/s/CTbEdi0F4-qFoJT05kNlXA\" target=\"_blank\" rel=\"noopener noreferrer\">https://mp.weixin.qq.com/s/CTbEdi0F4-qFoJT05kNlXA</a></p>\n</blockquote>\n<p>苦海无边，回头无岸。</p>\n<h2>01 前言</h2>\n<p>晃晃悠悠的，在互联网行业工作了五年，默然回首，你看哪里像灯火阑珊处？</p>\n<p>初入职场，大部分程序员会觉得苦学技术，以后会顺风顺水升职加薪，这样的想法没有错，但是不算全面，五年后你会不会继续做技术写代码这是核心问题。</p>\n<p>初入职场，会觉得努力加班可以不断提升能力，可以学到技术的公司就算薪水低点也可以接受，但是五年之后会认为加班都是在不断挤压自己的上升空间，薪水低是人生的天花板。</p>\n<p>这里想说的关键问题就是：初入职场的认知和想法大部分不会再适用于五年后的认知。</p>\n<p>工作五年之后面临的最大压力就是选择：职场天花板，技术能力天花板，薪水天花板，三十岁天花板。</p>\n<p>如何面对这些问题，是大部分程序员都在思考和纠结的。做选择的唯一参考点就是：利益最大化，这里可以理解为职场更好的升职加薪，顺风顺水。</p>\n<p>五年，变化最大不是工作经验，能力积累，而是心态，清楚的知道现实和理想之间是存在巨大的差距。</p>\n<h2>02 学会适应变化，并积累能力</h2>\n<p>回首自己的职场五年，最认可的一句话就是：学会适应变化，并积累能力。</p>\n<p>变化的就是，五年的时间技术框架更新迭代，开发工具的变迁，公司环境队友的更换，甚至是不同城市的流浪，想着能把肉体和灵魂安放在一处，有句很经典的话就是：唯一不变的就是变化本身。</p>\n<p>要积累的是：解决问题的能力，思考方式，拓宽认知。</p>\n<p>这种很难直白的描述，属于个人认知的范畴，不同的人有不一样的看法，所以只能站在大众化的角度去思考。</p>\n<p>首先聊聊技术，大部分小白级别的，都希望自己的技术能力不断提高，争取做到架构师级别，但是站在当前的互联网环境中，这种想法实现难度还是偏高，这里既不是打击也不是为了抬杠。</p>\n<p>可以观察一下现状，技术团队大的 20-30 人，小的 10-15 人，能有一个架构师去专门管理底层框架都是少有现象。</p>\n<p>这个问题的原因很多，首先架构师的成本过高，环境架构也不是需要经常升级，说的难听点可能框架比项目生命周期更高。</p>\n<p>所以大部分公司的大部分业务，基于现有大部分成熟的开源框架都可以解决，这也就导致架构师这个角色通常由项目主管代替或者级别较高的开发直接负责，这就是现实情况。</p>\n<p>这就导致技术框架的选择思路就是：只选对的。即这方面的人才多，开源解决方案多，以此降低技术方面对公司业务发展的影响。</p>\n<p>那为什么还要不断学习和积累技术能力？如果没有这个能力，程序员岗位可能根本走不了五年之久，需要用技术深度积累不断解决工作中的各种问题，用技术的广度提升自己实现业务需求的认知边界，这是安放肉体的根本保障。</p>\n<p>这就是导致很多五年以后的程序员压力陡然升高的原因，走向管理岗的另一个壁垒就是业务思维和认知。</p>\n<h2>03 提高业务能力的积累</h2>\n<p>程序员该不该用心研究业务，这个问题真的没有纠结的必要，只要不是纯技术型的公司，都需要面对业务。</p>\n<p>不管技术、运营、产品、管理层，都是在面向业务工作。</p>\n<p>从自己职场轨迹来看，五年变化最大就是解决业务问题的能力，职场之初面对很多业务场景都不知道如何下手，到几年之后设计业务的解决方案。</p>\n<p>这是大部分程序员在职场前五年跳槽就能涨薪的根本原因，面对业务场景，基于积累的经验和现有的开源工具，能快速给出合理的解决思路和实现过程。</p>\n<p>工作五年可能对技术底层的清晰程度都没有初入职场的小白清楚，但是写的程序却可以避开很多坑坑洼洼，对于业务的审视也是很细节全面。</p>\n<p>解决业务能力的积累，对于技术视野的宽度需求更甚，比如职场初期对于海量数据的处理束手无策，但是在工作几年之后见识数据行业的技术栈，真的就是技术选型的视野问题。</p>\n<p>什么是衡量技术能力的标准？站在一个共识的角度上看：系统的架构与代码设计能适应业务的不断变化和各种需求。</p>\n<p>相对比与技术，业务的变化更加快速频繁，高级工程师或者架构师之所以薪资高，这些角色一方面能适应业务的迭代，并且在工作中具有一定前瞻性，会考虑业务变化的情况下代码复用逻辑，这样的能力是需要一定的技术视野和业务思维的沉淀。</p>\n<p>所以职场中：业务能说的井井有条，代码能写的明明白白，得到机会的可能性更大。</p>\n<h2>04 不同的阶段技术和业务的平衡和选择</h2>\n<p>从理性的角度看技术和业务两个方面，能让大部分人职场走的平稳顺利，但是不同的阶段对两者的平衡和选择是不一样的。</p>\n<p>在思考如何选择的时候，可以参考二八原则的逻辑，即在任何一组东西中，最重要的只占其中一小部分，约 20%，其余 80%尽管是多数，却是次要的，因此又称二八定律。</p>\n<p>个人真的非常喜欢这个原则，大部分人都不是天才，所以很难三心二意同时做好几件事情，在同一时间段内应该集中精力做好一件事件。</p>\n<p>但是单纯的二八原则模式可能不适应大部分职场初期的人，因为初期要学习很多内容，如何在职场生存：专业能力，职场关系，为人处世，产品设计等等。</p>\n<p>当然这些东西不是都要用心刻意学习，但是合理安排二二六原则或其他组合是更明智的，首先是专业能力要重点练习，其次可以根据自己的兴趣合理选择一到两个方面去慢慢了解，例如产品，运营，运维，数据等，毕竟三五年以后会不会继续写代码很难说，多给自己留个机会总是有备无患。</p>\n<p>在职场初期，基本都是从技术角度去思考问题，如何快速提升自己的编码能力，在公司能稳定是首要目标，因此大部分时间都是在做基础编码和学习规范，这时可能 90%的心思都是放在基础编码上，另外 10%会学习环境架构。</p>\n<p>最多一到两年，就会开始独立负责模块需求开发，需要自己设计整个代码思路，这里业务就会进入视野，要懂得业务上下游关联关系，学会思考如何设计代码结构，才能在需求变动的情况下代码改动较少，这个时候可能就会放 20%的心思在业务方面，30%学习架构方式。</p>\n<p>三到五年这个时间段，是解决问题能力提升最快的时候，因为这个阶段的程序员基本都是在开发核心业务链路，例如交易、支付、结算、智能商业等模块，需要对业务整体有较清晰的把握能力，不然就是给自己挖坑，这个阶段要对业务流付出大量心血思考。</p>\n<p>越是核心的业务线，越是容易爆发各种问题，如果在日常工作中不花心思处理各种细节问题，半夜异常自动的消息和邮件总是容易让人憔悴。</p>\n<p>所以努力学习技术是提升自己，培养自己的业务认知也同样重要，个人认为这二者的分量平分秋色，只是需要在合适的阶段做出合理的权重划分。</p>\n<h2>05 学会在职场做选择和生存</h2>\n<p>基于技术能力和业务思维，学会在职场做选择和生存，这些是职场前五年一路走来的最大体会。</p>\n<p>不管是技术还是业务，这两个概念依旧是个很大的命题，不容易把握，所以学会理清这两个方面能力中的公共模块是关键。</p>\n<p>不管技术还是业务，都不可能从一家公司完全复制到另一家公司，但是可以把一家公司的技术框架，业务解决方案学会，并且带到另一家公司，例如技术领域内的架构、设计、流程、数据管理，业务领域内的思考方式、产品逻辑、分析等，这些是核心能力并且是大部分公司人才招聘的要求，所以这些才是工作中需要重点积累的。</p>\n<p>人的精力是有限的，而且面对三十这个天花板，各种事件也会接连而至，在职场中学会合理安排时间并不断提升核心能力，这样才能保证自己的竞争力。</p>\n<p>职场就像苦海无边，回首望去可能也没有岸边停泊，但是要具有换船的能力或者有个小木筏也就大差不差了。</p>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "date_published": "2023-06-04T16:54:06.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [
        {
          "name": "知了一笑"
        }
      ],
      "tags": [
        "技术文章精选集"
      ]
    },
    {
      "title": "使用建议",
      "url": "https://javaguide.cn/javaguide/use-suggestion.html",
      "id": "https://javaguide.cn/javaguide/use-suggestion.html",
      "summary": "JavaGuide使用建议，讲解如何高效利用本站内容进行Java学习与面试准备的方法指南。",
      "content_html": "<p><strong>对于不准备面试的同学来说</strong> ，本文档倾向于给你提供一个比较详细的学习路径，目录清晰，让你对于 Java 整体的知识体系有一个清晰认识。你可以跟着视频、书籍或者官方文档学习完某个知识点之后，然后来这里找对应的总结，帮助你更好地掌握对应的知识点。甚至说，你在有编程基础的情况下，想要学习某个知识点的话，可以直接看我的总结，这样学习效率会非常高。</p>\n<p><strong>对于准备面试的同学来说</strong> ，本文档涵盖 Java 程序员所需要掌握的核心知识的常见面试问题总结。</p>\n<p>大部分人看 JavaGuide 应该都是为了准备技术八股文。<strong>那如何才能更高效地准备技术八股文？</strong></p>\n<p>对于技术八股文来说，尽量不要死记硬背，这种方式非常枯燥且对自身能力提升有限！但是！想要一点不背是不太现实的，只是说要结合实际应用场景和实战来理解记忆。</p>\n<p>我一直觉得面试八股文最好是和实际应用场景和实战相结合。很多同学现在的方向都错了，上来就是直接背八股文，硬生生学成了文科，那当然无趣了。</p>\n<p>举个例子：你的项目中需要用到 Redis 来做缓存，你对照着官网简单了解并实践了简单使用 Redis 之后，你去看了 Redis 对应的八股文。你发现 Redis 可以用来做限流、分布式锁，于是你去在项目中实践了一下并掌握了对应的八股文。紧接着，你又发现 Redis 内存不够用的情况下，还能使用 Redis Cluster 来解决，于是你就又去实践了一下并掌握了对应的八股文。</p>\n<p>而且， <strong>面试中有水平的面试官都是根据你的项目经历来顺带着问一些技术八股文</strong> 。</p>\n<p>举个例子：你的项目用到了消息队列，那面试官可能就会问你：为什么使用消息队列？项目中什么模块用到了消息队列？如何保证消息不丢失？如何保证消息的顺序性?（结合你使用的具体的消息队列来准备）……。</p>\n<p><strong>一定要记住你的主要目标是理解和记关键词，而不是像背课文一样一字一句地记下来！</strong></p>\n<p>另外，记录博客或者用自己的理解把对应的知识点讲给别人听也是一个不错的选择。</p>\n<p>最后，准备技术面试的同学一定要定期复习（自测的方式非常好），不然确实会遗忘的。</p>\n",
      "date_published": "2023-05-22T02:13:07.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [],
      "tags": [
        "走近项目"
      ]
    },
    {
      "title": "十年大厂成长之路",
      "url": "https://javaguide.cn/high-quality-technical-articles/advanced-programmer/ten-years-of-dachang-growth-road.html",
      "id": "https://javaguide.cn/high-quality-technical-articles/advanced-programmer/ten-years-of-dachang-growth-road.html",
      "summary": "十年大厂成长之路：围绕技术知识与面试总结梳理关键概念、常见问题与实践要点，帮助你高效学习与备战面试。",
      "content_html": "<blockquote>\n<p><strong>推荐语</strong>：这篇文章的作者有着丰富的工作经验，曾在大厂工作了 12 年。结合自己走过的弯路和接触过的优秀技术人，他总结出了一些对于个人成长具有普遍指导意义的经验和特质。</p>\n<p><strong>原文地址：</strong> <a href=\"https://mp.weixin.qq.com/s/vIIRxznpRr5yd6IVyNUW2w\" target=\"_blank\" rel=\"noopener noreferrer\">https://mp.weixin.qq.com/s/vIIRxznpRr5yd6IVyNUW2w</a></p>\n</blockquote>\n<p>最近这段时间，有好几个年轻的同学和我聊到自己的迷茫。其中有关于技术成长的、有关于晋升的、有关于择业的。我很高兴他们愿意听我这个“过来人”分享自己的经验。</p>\n<p>我自己毕业后进入大厂，在大厂工作 12 年，我说的内容都来自于我自己或者身边人的真实情况。尤其，我会把 <strong>【我自己走过的弯路】</strong> 和 <strong>【我看到过的优秀技术人的特质】</strong> 相结合来给出建议。</p>\n<p>这些内容我觉得具有普遍的指导意义，所以决定做个整理分享出来。我相信，无论你在大厂还是小厂，如果你相信这些建议，或早或晚他们会帮助到你。</p>\n<p>我自己工作 12 年，走了些弯路，所以我就来讲讲，“在一个技术人 10 年的发展过程中，应该注意些什么”。我们把内容分为两块：</p>\n<ol>\n<li><strong>十年技术路怎么走</strong></li>\n<li><strong>一些重要选择</strong></li>\n</ol>\n<h2>01 十年技术路怎么走</h2>\n<h3>【1-2 年】=&gt; 从“菜鸟”到“职业”</h3>\n<p>应届生刚进入到工作时，会有各种不适应。比如写好的代码会被反复打回、和团队老司机讨论技术问题会有一堆问号、不敢提问和质疑、碰到问题一个人使劲死磕等等。</p>\n<p><strong>简单来说就是，即使日以继夜地埋头苦干，最后也无法顺利的开展工作。</strong></p>\n<p>这个阶段最重要的几个点：</p>\n<p><strong>【多看多模仿】</strong>：比如写代码的时候，不要就像在学校完成书本作业那样只关心功能是否正确，还要关心模块的设计、异常的处理、代码的可读性等等。在你还没有了解这些内容的精髓之前，也要照猫画虎地模仿起来，慢慢地你就会越来越明白真实世界的代码是怎么写的，以及为什么要这么写。</p>\n<p>做技术方案的时候也是同理，技术文档的要求你也许并不理解，但你可以先参考已有文档写起来。</p>\n<p><strong>【脸皮厚一点】</strong>：不懂就问，你是新人大家都是理解的。你做的各种方案也可以多找老司机们 review，不要怕被看笑话。</p>\n<p><strong>【关注工作方式】</strong>：比如发现需求在计划时间完不成就要尽快报风险、及时做好工作内容的汇报（例如周报）、开会后确定会议结论和 todo 项、承诺时间就要尽力完成、严格遵循公司的要求（例如发布规范、权限规范等）</p>\n<p>一般来说，工作 2 年后，你就应该成为一个职业人。老板可以相信任何工作交到你的手里，不会出现“意外”（例如一个重要需求明天要上线了，突然被告知上不了）。</p>\n<h3>【3-4 年】=&gt; 从“职业”到“尖兵”</h3>\n<p>工作两年后，对业务以及现有系统的了解已经到达了一定的程度，技术同学会开始承担更有难度的技术挑战。</p>\n<p>例如需要将性能提升到某一个水位、例如需要对某一个重要模块进行重构、例如有个重要的项目需要协同 N 个团队一起完成。</p>\n<p>可见，上述的这些技术问题，难度都已经远远超过一个普通的需求。解决这些问题需要有一定的技术能力，同时也需要具备更高的协同能力。</p>\n<p>这个阶段最重要的几个点：</p>\n<p><strong>【技术能力提升】</strong>：无论是公司内还是公司外的技术内容，都要多做主动的学习。基本上这个阶段的技术难题都集中在【性能】【稳定性】和【扩展性】上，而这些内容在业界都是有成型的方法论的。</p>\n<p><strong>【主人翁精神】</strong>：技术难题除了技术方案设计及落地外，背后还有一系列的其他工作。例如上线后对效果的观测、重点项目对于上下游改造和风险的了解程度、对于整个技改后续的计划（二期、三期的优化思路）等。</p>\n<p>在工作四年后，基本上你成为了团队的一、二号技术位。很多技术难题即使不是你来落地，也是由你来决定方案。你会做调研、会做方案对比、会考虑整个技改的生命周期。</p>\n<h3>【5-7 年】=&gt; 从“尖兵”到“专家”</h3>\n<p>技术尖兵重点在于解决某一个具体的技术难题或者重点项目。而下一步的发展方向，就是能够承担起来一整个“业务板块”，也就是“领域技术专家”。</p>\n<p>想要承担一整个“业务板块”需要 <strong>【对业务领域有深刻的理解，同时基于这些理解来规划技术的发展方向】</strong> 。</p>\n<p>拿支付做个例子。简单的支付功能其实很容易完成，只要处理好和双联（网联和银联）的接口调用（成功、失败、异常）即可。但在很多背景下，支付没有那么简单。</p>\n<p>例如，支付是一个用户敏感型操作，非常强调用户体验，如何能兼顾体验和接口的不稳定？支付接口还需要承担费用，同步和异步的接口费用不同，如何能够降本？支付接口往往还有限额等。这一系列问题的背后涉及到很多技术的设计，包括异步化、补偿设计、资金流设计、最终一致性设计等等。</p>\n<p>这个阶段最重要的几个点：</p>\n<p><strong>【深入理解行业及趋势】</strong>：密切关注行业的各种变化（新鲜的玩法、政策的变动、竞对的策略、科技等外在因素的影响等等），和业务同学加强沟通。</p>\n<p><strong>【深入了解行业解决方案】</strong>：充分对标已有的国内外技术方案，做深入学习和尝试，评估建设及运维成本，结合业务趋势制定计划。</p>\n<h3>【8-10 年】=&gt; 从“专家”到“TL”</h3>\n<p>其实很多时候，如果能做到专家，基本也是一个 TL 的角色了，但这并不代表正在执行 TL 的职责。</p>\n<p>专家虽然已经可以做到“为业务发展而规划好技术发展”，但问题是要怎么落地呢？显然，靠一个人的力量是不可能完成建设的。所以，这里的 TL 更多强调的不是“领导”这个职位，而是 <strong>【通过聚合一个团队的力量来实施技术规划】</strong> 。</p>\n<p>所以，这里的 TL 需要具备【团队技术培养】【合理分配资源】【确认工作优先级】【激励与奖惩】等各种能力。</p>\n<p>这个阶段最重要的几个点：</p>\n<p><strong>【学习管理学】</strong>：这里的管理学当然不是指 PUA，而是指如何在每个同学都有各自诉求的现实背景下，让个人目标和团队目标相结合，产生向前发展的动力。</p>\n<p><strong>【始终扎根技术】</strong>：很多时候，工作重心偏向管理以后，就会荒废技术。但事实是，一个优秀的领导永远是一个优秀的技术人。参与一起讨论技术方案并给予指导、不断扩展自己的技术宽度、保持对技术的好奇心，这些是让一个技术领导持续拥有向心力的关键。</p>\n<h2>02 一些重要选择</h2>\n<p>下面来聊聊在十年间我们可能会碰到的一些重要选择。这些都是真实的血与泪的教训。</p>\n<h3>我该不该转岗？</h3>\n<p>大厂都有转岗的机制。转岗可以帮助员工寻找自己感兴趣的方向，也可以帮助新型团队招募有即战力的同学。</p>\n<p>转岗看似只是在公司内部变动，但你需要谨慎决定。</p>\n<p>本人转岗过多次。虽然还在同一家公司，但转岗等同于换工作。无论是领域沉淀、工作内容、信任关系、协作关系都是从零开始。</p>\n<p>针对转岗我的建议是：<strong>如果你是想要拓宽自己的技术广度，也就是抱着提升技术能力的想法，我觉得可以转岗。但如果你想要晋升，不建议你转岗。</strong>晋升需要在一个领域的持续积淀和在一个团队信任感的持续建立。</p>\n<p>当然，转岗可能还有其他原因，例如家庭原因、身体原因等，这个不展开讨论了。</p>\n<h3>我该不该跳槽？</h3>\n<p>跳槽和转岗一样，往往有很多因素造成，不能一概而论，我仅以几个场景来说：</p>\n<p><strong>【晋升失败】</strong>：扪心自问，如果你觉得自己确实还不够格，那你就踏踏实实继续努力。如果你觉得评委有失偏颇，你可以尝试去外面面试一下，让市场来给你答案。</p>\n<p><strong>【成长局限】</strong>：觉得自己做的事情没有挑战，无法成长。你可以和老板聊一下，有可能是因为你没有看到其中的挑战，也有可能老板没有意识到你的“野心”。</p>\n<p><strong>【氛围不适】</strong>：一般来自于新入职或者领导更换，这种情况下不适是正常的。我的建议是，<strong>如果一个环境是“对事不对人”的，那就可以留下来</strong>，努力去适应，这种不适应只是做事方式不同导致的。但如果这个环境是“对人不对事”的话，走吧。</p>\n<h3>跳槽该找怎样的工作？</h3>\n<p>我们跳槽的时候往往会同时面试好几家公司。行情好的时候，往往可以收到多家 offer，那么我们要如何选择呢？</p>\n<p>考虑一个 offer 往往有这几点：【公司品牌】【薪资待遇】【职级职称】【技术背景】。每个同学其实都有自己的诉求，所以无论做什么选择都没有对错之分。</p>\n<p>我的一个建议是：<strong>你要关注新岗位的空间，这个空间是有希望满足你的期待的</strong>。</p>\n<p>比如，你想成为架构师，那新岗位是否有足够的技术挑战来帮助你提升技术能力，而不仅仅是疲于奔命地应付需求？</p>\n<p>比如，你想往技术管理发展，那新岗位是否有带人的机会？是否有足够的问题需要搭建团队来解决？</p>\n<p>比如，你想扎根在某个领域持续发展（例如电商、游戏），那新岗位是不是延续这个领域，并且可以碰到更多这个领域的问题？</p>\n<p>当然，如果薪资实在高到无法拒绝，以上参考可以忽略!</p>\n<h2>结语</h2>\n<p>以上就是我对互联网从业技术人员十年成长之路的心得，希望在你困惑和关键选择的时候可以帮助到你。如果我的只言片语能够在未来的某个时间帮助到你哪怕一点，那将是我莫大的荣幸。</p>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "date_published": "2023-05-15T09:49:43.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [
        {
          "name": "CodingBetterLife"
        }
      ],
      "tags": [
        "技术文章精选集"
      ]
    },
    {
      "title": "32条总结教你提升职场经验",
      "url": "https://javaguide.cn/high-quality-technical-articles/work/32-tips-improving-career.html",
      "id": "https://javaguide.cn/high-quality-technical-articles/work/32-tips-improving-career.html",
      "summary": "32条总结教你提升职场经验：围绕技术知识与面试总结梳理关键概念、常见问题与实践要点，帮助你高效学习与备战面试。",
      "content_html": "<blockquote>\n<p><strong>推荐语</strong>：阿里开发者的一篇职场经验的分享。</p>\n<p><strong>原文地址：</strong> <a href=\"https://mp.weixin.qq.com/s/6BkbGekSRTadm9j7XUL13g\" target=\"_blank\" rel=\"noopener noreferrer\">https://mp.weixin.qq.com/s/6BkbGekSRTadm9j7XUL13g</a></p>\n</blockquote>\n<h2>成长的捷径</h2>\n<ul>\n<li>入职伊始谦逊的态度是好的，但不要把“我是新人”作为心理安全线；</li>\n<li>写一篇技术博客大概需要两周左右，但可能是最快的成长方式；</li>\n<li>一定要读两本书：金字塔原理、高效能人士的七个习惯（这本书名字像成功学，实际讲的是如何塑造性格）；</li>\n<li>多问是什么、为什么，追本溯源把问题解决掉，试图绕过的问题永远会在下个路口等着你；</li>\n<li>不要沉迷于忙碌带来的虚假安全感中，目标的确定和追逐才是最真实的安全；</li>\n<li>不用过于计较一时的得失，在公平的环境中，吃亏是福不是鸡汤；</li>\n<li>思维和技能不要受限于前端、后端、测试等角色，把自己定位成业务域问题的终结者；</li>\n<li>好奇和热爱是成长最大的捷径，长期主义者会认同自己的工作价值，甚至要高于组织当下给的认同（KPI）。</li>\n</ul>\n<h2>功夫在日常</h2>\n<ul>\n<li>每行代码要代表自己当下的最高水平，你觉得无所谓的小细节，有可能就是在晋升场上伤害你的暗箭；</li>\n<li>双周报不是工作日志流水账，不要被时间推着走，最起码要知道下次双周报里会有什么（小目标驱动）；</li>\n<li>觉得日常都是琐碎工作、不技术、给师兄打杂等，可以尝试对手头事情做一下分类，想象成每个分类都是个小格子，这些格子连起来的终点就是自己的目标，这样每天不再是机械的做需求，而是有规划的填格子、为目标努力，甚至会给自己加需求，因为自己看清楚了要去哪里；</li>\n<li>日常的言行举止是能力的显微镜，大部分人可能意识不到，自己的强大和虚弱是那么的明显，不要无谓的试图掩盖，更不存在蒙混过关。</li>\n</ul>\n<blockquote>\n<p>最后一条大概意思就是有时候我们会在意自己在聚光灯下（述职、晋升、周报、汇报等）的表现，以为大家会根据这个评价自己。实际上日常是怎么完成业务需求、帮助身边同学、创造价值的，才是大家评价自己的依据，而且每个人是什么样的特质，合作过三次的伙伴就可以精准评价，在聚光灯下的表演只能骗自己。</p>\n</blockquote>\n<h2>学会被管理</h2>\n<blockquote>\n<p>上级、主管是泛指，开发对口的 PD 主管等也在范围内。</p>\n</blockquote>\n<ul>\n<li>\n<p>不要传播负面情绪，不要总是抱怨；</p>\n</li>\n<li>\n<p>对上级不卑不亢更容易获得尊重，但不要当众反驳对方观点，分歧私下沟通；</p>\n</li>\n<li>\n<p>好好做向上管理，尤其是对齐预期，沟通绩效出现 Surprise 双方其实都有责任，但倒霉的是自己；</p>\n</li>\n<li>\n<p>尽量站在主管角度想问题：</p>\n</li>\n<li>\n<ul>\n<li>这样能理解很多过去感觉匪夷所思的决策；</li>\n<li>不要在意谁执行、功劳是谁的等，为团队分忧赢得主管信任的重要性远远高于这些；</li>\n<li>不要把这个原则理解为唯上，这种最让人不齿。</li>\n</ul>\n</li>\n</ul>\n<h2>思维转换</h2>\n<ul>\n<li>定义问题是个高阶能力，尽早形成 发现问题-&gt;定义问题-&gt;解决问题-&gt;消灭问题 的思维闭环；</li>\n<li>定事情价值导向，做事情结果导向，讲事情问题导向；</li>\n<li>讲不清楚，大概率不是因为自己是实干型，而是没想清楚，在晋升场更加明显；</li>\n<li>当一个人擅长解决某一场景的问题的时候，时间越久也许越离不开这个场景（被人贴上一个标签很难，撕掉一个标签更难）。</li>\n</ul>\n<h2>要栓住情绪</h2>\n<ul>\n<li>学会控制情绪，没人会认真听一个愤怒的人在说什么；</li>\n<li>再委屈、再愤怒也要保持理智，不要让自己成为需要被哄着的那种人；</li>\n<li>足够自信的人才会坦率的承认自己的问题，很多时候我们被激怒了，只是因为对方指出了自己藏在深处的自卑；</li>\n<li>伤害我们最深的既不是别人的所作所为，也不是自己犯的错误，而是我们对错误的回应。</li>\n</ul>\n<h2>成为 Leader</h2>\n<blockquote>\n<p>Manager 有下属，Leader 有追随者，管理者不需要很多，但人人都可以是 Leader。</p>\n</blockquote>\n<ul>\n<li>让你信服、愿意追随的人不是职务上的 Manager，而是在帮助自己的那个人，自己想服众的话道理一样；</li>\n<li>不要轻易对人做负面评价，片面认知下的评价可能不准确，不经意的传播更是会给对方带来极大的困扰；</li>\n<li>Leader 如果不认同公司的使命、愿景、价值观，会过的特别痛苦；</li>\n<li>困难时候不要否定自己的队友，多给及时、正向的反馈；</li>\n<li>船长最重要的事情不是造船，而是激发水手对大海的向往；</li>\n<li>Leader 的天然职责是让团队活下去，唯一的途径是实现上级、老板、公司经营者的目标，越是艰难的时候越明显；</li>\n<li>Leader 的重要职责是识别团队需要被做的事情，并坚定信念，使众人行，越是艰难的时候越要坚定；</li>\n<li>Leader 应该让自己遇到的每个人都感觉自己很重要、被需要。</li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "date_published": "2023-05-15T09:49:43.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [],
      "tags": [
        "技术文章精选集"
      ]
    },
    {
      "title": "JVM线上问题排查和性能调优案例",
      "url": "https://javaguide.cn/java/jvm/jvm-in-action.html",
      "id": "https://javaguide.cn/java/jvm/jvm-in-action.html",
      "summary": "汇集 JVM 在生产中的问题排查与优化案例，涵盖内存与 GC、工具使用等。",
      "content_html": "<p>JVM 线上问题排查和性能调优也是面试常问的一个问题，尤其是社招中大厂的面试。</p>\n<p>这篇文章，我会分享一些我看到的相关的案例。</p>\n<p>下面是正文。</p>\n<p><a href=\"https://juejin.cn/post/7205141492264976445\" target=\"_blank\" rel=\"noopener noreferrer\">一次线上 OOM 问题分析 - 艾小仙 - 2023</a></p>\n<ul>\n<li><strong>现象</strong>：线上某个服务有接口非常慢，通过监控链路查看发现，中间的 GAP 时间非常大，实际接口并没有消耗很多时间，并且在那段时间里有很多这样的请求。</li>\n<li><strong>分析</strong>：使用 JDK 自带的<code>jvisualvm</code>分析 dump 文件(MAT 也能分析)。</li>\n<li><strong>建议</strong>：对于 SQL 语句，如果监测到没有<code>where</code>条件的全表查询应该默认增加一个合适的<code>limit</code>作为限制，防止这种问题拖垮整个系统</li>\n<li><strong>资料</strong>：<a href=\"https://heapdump.cn/article/3489050\" target=\"_blank\" rel=\"noopener noreferrer\">实战案例：记一次 dump 文件分析历程转载 - HeapDump - 2022</a>。</li>\n</ul>\n<p><a href=\"https://www.cnblogs.com/mylibs/p/production-accident-0002.html\" target=\"_blank\" rel=\"noopener noreferrer\">生产事故-记一次特殊的 OOM 排查 - 程语有云 - 2023</a></p>\n<ul>\n<li><strong>现象</strong>：网络没有问题的情况下，系统某开放接口从 2023 年 3 月 10 日 14 时许开始无法访问和使用。</li>\n<li><strong>临时解决办法</strong>：紧急回滚至上一稳定版本。</li>\n<li><strong>分析</strong>：使用 MAT (Memory Analyzer Tool)工具分析 dump 文件。</li>\n<li><strong>建议</strong>：正常情况下，<code>-Xmn</code>参数（控制 Young 区的大小）总是应当小于<code>-Xmx</code>参数（控制堆内存的最大大小），否则就会触发 OOM 错误。</li>\n<li><strong>资料</strong>：<a href=\"https://javaguide.cn/java/jvm/jvm-parameters-intro.html\" target=\"_blank\" rel=\"noopener noreferrer\">最重要的 JVM 参数总结 - JavaGuide - 2023</a></li>\n</ul>\n<p><a href=\"https://juejin.cn/post/7078624931826794503\" target=\"_blank\" rel=\"noopener noreferrer\">一次大量 JVM Native 内存泄露的排查分析（64M 问题） - 掘金 - 2022</a></p>\n<ul>\n<li><strong>现象</strong>：线上项目刚启动完使用 top 命令查看 RES 占用了超过 1.5G。</li>\n<li><strong>分析</strong>：整个分析流程用到了较多工作，可以跟着作者思路一步一步来，值得学习借鉴。</li>\n<li><strong>建议</strong>：远离 Hibernate。</li>\n<li><strong>资料</strong>：<a href=\"https://liam.page/2020/07/17/memory-stat-in-TOP/\" target=\"_blank\" rel=\"noopener noreferrer\">Linux top 命令里的内存相关字段（VIRT, RES, SHR, CODE, DATA）</a></li>\n</ul>\n<p><a href=\"https://www.heapdump.cn/article/1661497\" target=\"_blank\" rel=\"noopener noreferrer\">YGC 问题排查，又让我涨姿势了！ - IT 人的职场进阶 - 2021</a></p>\n<ul>\n<li><strong>现象</strong>：广告服务在新版本上线后，收到了大量的服务超时告警。</li>\n<li><strong>分析</strong>：使用 MAT (Memory Analyzer Tool) 工具分析 dump 文件。</li>\n<li><strong>建议</strong>：学会 YGC（Young GC） 问题的排查思路，掌握 YGC 的相关知识点。</li>\n</ul>\n<p><a href=\"https://shuyi.tech/archives/have-a-try-in-jvm-combat\" target=\"_blank\" rel=\"noopener noreferrer\">听说 JVM 性能优化很难？今天我小试了一把！ - 陈树义 - 2021</a></p>\n<p>通过观察 GC 频率和停顿时间，来进行 JVM 内存空间调整，使其达到最合理的状态。调整过程记得小步快跑，避免内存剧烈波动影响线上服务。 这其实是最为简单的一种 JVM 性能调优方式了，可以算是粗调吧。</p>\n<p><a href=\"https://mp.weixin.qq.com/s/df1uxHWUXzhErxW1sZ6OvQ\" target=\"_blank\" rel=\"noopener noreferrer\">你们要的线上 GC 问题案例来啦 - 编了个程 - 2021</a></p>\n<ul>\n<li><strong>案例 1</strong>：使用 guava cache 的时候，没有设置最大缓存数量和弱引用，导致频繁触发 Young GC</li>\n<li><strong>案例 2</strong>： 对于一个查询和排序分页的 SQL，同时这个 SQL 需要 join 多张表，在分库分表下，直接调用 SQL 性能很差。于是，查单表，再在内存排序分页，用了一个 List 来保存数据，而有些数据量大，造成了这个现象。</li>\n</ul>\n<p><a href=\"https://tech.meituan.com/2020/11/12/java-9-cms-gc.html\" target=\"_blank\" rel=\"noopener noreferrer\">Java 中 9 种常见的 CMS GC 问题分析与解决 - 美团技术团 - 2020</a></p>\n<p>这篇文章共 2w+ 字，详细介绍了 GC 基础，总结了 CMS GC 的一些常见问题分析与解决办法。</p>\n<p><a href=\"https://juejin.cn/post/7311623433817571365\" target=\"_blank\" rel=\"noopener noreferrer\">给祖传系统做了点 GC 调优，暂停时间降低了 90% - 京东云技术团队 - 2023</a></p>\n<p>这篇文章提到了一个在规则引擎系统中遇到的 GC（垃圾回收）问题，主要表现为系统在启动后发生了一次较长的 Young GC（年轻代垃圾回收）导致性能下降。经过分析，问题的核心在于动态对象年龄判定机制，它导致了过早的对象晋升，引起了长时间的垃圾回收。</p>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "date_published": "2023-05-10T08:36:34.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "分布式ID设计实战指南",
      "url": "https://javaguide.cn/distributed-system/distributed-id-design.html",
      "id": "https://javaguide.cn/distributed-system/distributed-id-design.html",
      "summary": "分布式ID设计实战指南，结合订单系统、一码付、优惠券等业务场景讲解分布式ID的设计要点、技术选型及不同场景下的ID生成策略。",
      "content_html": "<div class=\"hint-container tip\">\n<p class=\"hint-container-title\">提示</p>\n<p>看到百度 Geek 说的一篇结合具体场景聊分布式 ID 设计的文章，感觉挺不错的。于是，我将这篇文章的部分内容整理到了这里。原文传送门：<a href=\"https://mp.weixin.qq.com/s/bFDLb6U6EgI-DvCdLTq_QA\" target=\"_blank\" rel=\"noopener noreferrer\">分布式 ID 生成服务的技术原理和项目实战</a> 。</p>\n</div>\n<p>网上绝大多数的分布式 ID 生成服务，一般着重于技术原理剖析，很少见到根据具体的业务场景去选型 ID 生成服务的文章。</p>\n<p>本文结合一些使用场景，进一步探讨业务场景中对 ID 有哪些具体的要求。</p>\n<h2>场景一：订单系统</h2>\n<p>我们在商场买东西一码付二维码，下单生成的订单号，使用到的优惠券码，联合商品兑换券码，这些是在网上购物经常使用到的单号，那么为什么有些单号那么长，有些只有几位数？有些单号一看就知道年月日的信息，有些却看不出任何意义？下面展开分析下订单系统中不同场景的 id 服务的具体实现。</p>\n<h3>1、一码付</h3>\n<p>我们常见的一码付，指的是一个二维码可以使用支付宝或者微信进行扫码支付。</p>\n<p>二维码的本质是一个字符串。聚合码的本质就是一个链接地址。用户使用支付宝微信直接扫一个码付钱，不用担心拿支付宝扫了微信的收款码或者用微信扫了支付宝的收款码，这极大减少了用户扫码支付的时间。</p>\n<p>实现原理是当客户用 APP 扫码后，网站后台就会判断客户的扫码环境。（微信、支付宝、QQ 钱包、京东支付、云闪付等）。</p>\n<p>判断扫码环境的原理就是根据打开链接浏览器的 HTTP header。任何浏览器打开 http 链接时，请求的 header 都会有 User-Agent(UA、用户代理)信息。</p>\n<p>UA 是一个特殊字符串头，服务器依次可以识别出客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等很多信息。</p>\n<p>各渠道对应支付产品的名称不一样，一定要仔细看各支付产品的 API 介绍。</p>\n<ol>\n<li>微信支付：JSAPI 支付支付</li>\n<li>支付宝：手机网站支付</li>\n<li>QQ 钱包：公众号支付</li>\n</ol>\n<p>其本质均为在 APP 内置浏览器中实现 HTML5 支付。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/distributed-system/distributed-id-design-pay-one-card.png\" alt=\"文库会员支付示例\"></p>\n<p>文库的研发同学在这个思路上，做了优化迭代。动态生成一码付的二维码预先绑定用户所选的商品信息和价格，根据用户所选的商品动态更新。这样不仅支持一码多平台调起支付，而且不用用户选择商品输入金额，即可完成订单支付的功能，很丝滑。用户在真正扫码后，服务端才通过前端获取用户 UID，结合二维码绑定的商品信息，真正的生成订单，发送支付信息到第三方（qq、微信、支付宝），第三方生成支付订单推给用户设备，从而调起支付。</p>\n<p>区别于固定的一码付，在文库的应用中，使用到了动态二维码，二维码本质是一个短网址，ID 服务提供短网址的唯一标志参数。唯一的短网址映射的 ID 绑定了商品的订单信息，技术和业务的深度结合，缩短了支付流程，提升用户的支付体验。</p>\n<h3>2、订单号</h3>\n<p>订单号在实际的业务过程中作为一个订单的唯一标识码存在，一般实现以下业务场景：</p>\n<ol>\n<li>用户订单遇到问题，需要找客服进行协助；</li>\n<li>对订单进行操作，如线下收款，订单核销；</li>\n<li>下单，改单，成单，退单，售后等系统内部的订单流程处理和跟进。</li>\n</ol>\n<p>很多时候搜索订单相关信息的时候都是以订单 ID 作为唯一标识符，这是由于订单号的生成规则的唯一性决定的。从技术角度看，除了 ID 服务必要的特性之外，在订单号的设计上需要体现几个特性：</p>\n<p><strong>（1）信息安全</strong></p>\n<p>编号不能透露公司的运营情况，比如日销、公司流水号等信息，以及商业信息和用户手机号，身份证等隐私信息。并且不能有明显的整体规律（可以有局部规律），任意修改一个字符就能查询到另一个订单信息，这也是不允许的。</p>\n<p>类比于我们高考时候的考生编号的生成规则，一定不能是连号的，否则只需要根据顺序往下查询就能搜索到别的考生的成绩，这是绝对不可允许。</p>\n<p><strong>（2）部分可读</strong></p>\n<p>位数要便于操作，因此要求订单号的位数适中，且局部有规律。这样可以方便在订单异常，或者退货时客服查询。</p>\n<p>过长的订单号或易读性差的订单号会导致客服输入困难且易错率较高，影响用户体验的售后体验。因此在实际的业务场景中，订单号的设计通常都会适当携带一些允许公开的对使用场景有帮助的信息，如时间，星期，类型等等，这个主要根据所涉及的编号对应的使用场景来。</p>\n<p>而且像时间、星期这些自增长的属于作为订单号的设计的一部分元素，有助于解决业务累积而导致的订单号重复的问题。</p>\n<p><strong>（3）查询效率</strong></p>\n<p>常见的电商平台订单号大多是纯数字组成，兼具可读性的同时，int 类型相对 varchar 类型的查询效率更高，对在线业务更加友好。</p>\n<h3>3、优惠券和兑换券</h3>\n<p>优惠券、兑换券是运营推广最常用的促销工具之一，合理使用它们，可以让买家得到实惠，商家提升商品销量。常见场景有：</p>\n<ol>\n<li>在文库购买【文库 VIP+QQ 音乐年卡】联合商品，支付成功后会得到 QQ 音乐年卡的兑换码，可以去 QQ 音乐 App 兑换音乐会员年卡；</li>\n<li>疫情期间，部分地方政府发放的消费券；</li>\n<li>瓶装饮料经常会出现输入优惠编码兑换奖品。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/distributed-system/distributed-id-design-coupon.png\" alt=\"优惠编码兑换奖品\"></p>\n<p>从技术角度看，有些场景适合 ID 即时生成，比如电商平台购物领取的优惠券，只需要在用户领取时分配优惠券信息即可。有些线上线下结合的场景，比如疫情优惠券，瓶盖开奖，京东卡，超市卡这种，则需要预先生成，预先生成的券码具备以下特性：</p>\n<p>1.预先生成，在活动正式开始前提供出来进行活动预热；</p>\n<p>2.优惠券体量大，以万为单位，通常在 10 万级别以上；</p>\n<p>3.不可破解、仿制券码；</p>\n<p>4.支持用后核销；</p>\n<p>5.优惠券、兑换券属于广撒网的策略，所以利用率低，也就不适合使用数据库进行存储 <strong>（占空间，有效的数据又少）</strong>。</p>\n<p>设计思路上，需要设计一种有效的兑换码生成策略，支持预先生成，支持校验，内容简洁，生成的兑换码都具有唯一性，那么这种策略就是一种特殊的编解码策略，按照约定的编解码规则支撑上述需求。</p>\n<p>既然是一种编解码规则，那么需要约定编码空间(也就是用户看到的组成兑换码的字符)，编码空间由字符 a-z,A-Z,数字 0-9 组成，为了增强兑换码的可识别度，剔除大写字母 O 以及 I,可用字符如下所示，共 60 个字符：</p>\n<p>abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXZY0123456789</p>\n<p>之前说过，兑换码要求尽可能简洁，那么设计时就需要考虑兑换码的字符数，假设上限为 12 位，而字符空间有 60 位，那么可以表示的空间范围为 60^12=130606940160000000000000(也就是可以 12 位的兑换码可以生成天量,应该够运营同学挥霍了)，转换成 2 进制：</p>\n<p>1001000100000000101110011001101101110011000000000000000000000(61 位)</p>\n<p><strong>兑换码组成成分分析</strong></p>\n<p>兑换码可以预先生成，并且不需要额外的存储空间保存这些信息，每一个优惠方案都有独立的一组兑换码(指运营同学组织的每一场运营活动都有不同的兑换码,不能混合使用, 例如双 11 兑换码不能使用在双 12 活动上)，每个兑换码有自己的编号，防止重复，为了保证兑换码的有效性，对兑换码的数据需要进行校验，当前兑换码的数据组成如下所示：</p>\n<p>优惠方案 ID + 兑换码序列号 i + 校验码</p>\n<p><strong>编码方案</strong></p>\n<ol>\n<li>兑换码序列号 i，代表当前兑换码是当前活动中第 i 个兑换码，兑换码序列号的空间范围决定了优惠活动可以发行的兑换码数目，当前采用 30 位 bit 位表示，可表示范围：1073741824（10 亿个券码）。</li>\n<li>优惠方案 ID, 代表当前优惠方案的 ID 号，优惠方案的空间范围决定了可以组织的优惠活动次数，当前采用 15 位表示，可以表示范围：32768（考虑到运营活动的频率，以及 ID 的初始值 10000，15 位足够，365 天每天有运营活动，可以使用 54 年）。</li>\n<li>校验码，校验兑换码是否有效，主要为了快捷的校验兑换码信息的是否正确，其次可以起到填充数据的目的，增强数据的散列性，使用 13 位表示校验位，其中分为两部分，前 6 位和后 7 位。</li>\n</ol>\n<p>深耕业务还会有区分通用券和单独券的情况，分别具备以下特点，技术实现需要因地制宜地思考。</p>\n<ol>\n<li>通用券：多个玩家都可以输入兑换，然后有总量限制，期限限制。</li>\n<li>单独券：运营同学可以在后台设置兑换码的奖励物品、期限、个数，然后由后台生成兑换码的列表，兑换之后核销。</li>\n</ol>\n<h2>场景二：Tracing</h2>\n<h3>1、日志跟踪</h3>\n<p>在分布式服务架构下，一个 Web 请求从网关流入，有可能会调用多个服务对请求进行处理，拿到最终结果。这个过程中每个服务之间的通信又是单独的网络请求，无论请求经过的哪个服务出了故障或者处理过慢都会对前端造成影响。</p>\n<p>处理一个 Web 请求要调用的多个服务，为了能更方便的查询哪个环节的服务出现了问题，现在常用的解决方案是为整个系统引入分布式链路跟踪。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/distributed-system/distributed-id-design-tracing.png\" alt=\"在分布式链路跟踪\"></p>\n<p>在分布式链路跟踪中有两个重要的概念：跟踪（trace）和 跨度（ span)。trace 是请求在分布式系统中的整个链路视图，span 则代表整个链路中不同服务内部的视图，span 组合在一起就是整个 trace 的视图。</p>\n<p>在整个请求的调用链中，请求会一直携带 traceid 往下游服务传递，每个服务内部也会生成自己的 spanid 用于生成自己的内部调用视图，并和 traceid 一起传递给下游服务。</p>\n<h3>2、TraceId 生成规则</h3>\n<p>这种场景下，生成的 ID 除了要求唯一之外，还要求生成的效率高、吞吐量大。traceid 需要具备接入层的服务器实例自主生成的能力，如果每个 trace 中的 ID 都需要请求公共的 ID 服务生成，纯纯的浪费网络带宽资源。且会阻塞用户请求向下游传递，响应耗时上升，增加了没必要的风险。所以需要服务器实例最好可以自行计算 tracid，spanid，避免依赖外部服务。</p>\n<p>产生规则：服务器 IP + ID 产生的时间 + 自增序列 + 当前进程号 ，比如：</p>\n<p>0ad1348f1403169275002100356696</p>\n<p>前 8 位 0ad1348f 即产生 TraceId 的机器的 IP，这是一个十六进制的数字，每两位代表 IP 中的一段，我们把这个数字，按每两位转成 10 进制即可得到常见的 IP 地址表示方式 10.209.52.143，您也可以根据这个规律来查找到请求经过的第一个服务器。</p>\n<p>后面的 13 位 1403169275002 是产生 TraceId 的时间。之后的 4 位 1003 是一个自增的序列，从 1000 涨到 9000，到达 9000 后回到 1000 再开始往上涨。最后的 5 位 56696 是当前的进程 ID，为了防止单机多进程出现 TraceId 冲突的情况，所以在 TraceId 末尾添加了当前的进程 ID。</p>\n<h3>3、SpanId 生成规则</h3>\n<p>span 是层的意思，比如在第一个实例算是第一层， 请求代理或者分流到下一个实例处理，就是第二层，以此类推。通过层，SpanId 代表本次调用在整个调用链路树中的位置。</p>\n<p>假设一个 服务器实例 A 接收了一次用户请求，代表是整个调用的根节点，那么 A 层处理这次请求产生的非服务调用日志记录 spanid 的值都是 0，A 层需要通过 RPC 依次调用 B、C、D 三个服务器实例，那么在 A 的日志中，SpanId 分别是 0.1，0.2 和 0.3，在 B、C、D 中，SpanId 也分别是 0.1，0.2 和 0.3；如果 C 系统在处理请求的时候又调用了 E，F 两个服务器实例，那么 C 系统中对应的 spanid 是 0.2.1 和 0.2.2，E、F 两个系统对应的日志也是 0.2.1 和 0.2.2。</p>\n<p>根据上面的描述可以知道，如果把一次调用中所有的 SpanId 收集起来，可以组成一棵完整的链路树。</p>\n<p><strong>spanid 的生成本质：在跨层传递透传的同时，控制大小版本号的自增来实现的。</strong></p>\n<h2>场景三：短网址</h2>\n<p>短网址主要功能包括网址缩短与还原两大功能。相对于长网址，短网址可以更方便地在电子邮件，社交网络，微博和手机上传播，例如原来很长的网址通过短网址服务即可生成相应的短网址，避免折行或超出字符限制。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/distributed-system/distributed-id-design-short-url.png\" alt=\"短网址作用\"></p>\n<p>常用的 ID 生成服务比如：MySQL ID 自增、 Redis 键自增、号段模式，生成的 ID 都是一串数字。短网址服务把客户的长网址转换成短网址，</p>\n<p>实际是在 <a href=\"http://dwz.cn\" target=\"_blank\" rel=\"noopener noreferrer\">dwz.cn</a> 域名后面拼接新产生的数字类型 ID，直接用数字 ID，网址长度也有些长，服务可以通过数字 ID 转更高进制的方式压缩长度。这种算法在短网址的技术实现上越来越多了起来，它可以进一步压缩网址长度。转进制的压缩算法在生活中有广泛的应用场景，举例：</p>\n<ul>\n<li>客户的长网址：<a href=\"https://wenku.baidu.com/ndbusiness/browse/wenkuvipcashier?cashier_code=PCoperatebanner\" target=\"_blank\" rel=\"noopener noreferrer\">https://wenku.baidu.com/ndbusiness/browse/wenkuvipcashier?cashier_code=PCoperatebanner</a></li>\n<li>ID 映射的短网址：<a href=\"https://dwz.cn/2047601319t66\" target=\"_blank\" rel=\"noopener noreferrer\">https://dwz.cn/2047601319t66</a> (演示使用，可能无法正确打开)</li>\n<li>转进制后的短网址：<a href=\"https://dwz.cn/2ezwDJ0\" target=\"_blank\" rel=\"noopener noreferrer\">https://dwz.cn/2ezwDJ0</a> (演示使用，可能无法正确打开)</li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/system-design/distributed-system/distributed-id-design-pay-one-card.png",
      "date_published": "2023-05-05T02:23:30.000Z",
      "date_modified": "2026-03-12T04:06:59.000Z",
      "authors": [],
      "tags": [
        "分布式"
      ]
    },
    {
      "title": "Spring Cloud Gateway面试题总结",
      "url": "https://javaguide.cn/distributed-system/spring-cloud-gateway-questions.html",
      "id": "https://javaguide.cn/distributed-system/spring-cloud-gateway-questions.html",
      "summary": "Spring Cloud Gateway核心原理详解，包括路由配置、Predicate断言、Filter过滤器机制、限流熔断、工作流程等常见面试题与实践要点。",
      "content_html": "<blockquote>\n<p>本文重构完善自<a href=\"https://mp.weixin.qq.com/s/XjFYsP1IUqNzWqXZdJn-Aw\" target=\"_blank\" rel=\"noopener noreferrer\">6000 字 | 16 图 | 深入理解 Spring Cloud Gateway 的原理 - 悟空聊架构</a>这篇文章。</p>\n</blockquote>\n<h2>什么是 Spring Cloud Gateway？</h2>\n<p>Spring Cloud Gateway 属于 Spring Cloud 生态系统中的网关，其诞生的目标主要是为了替代 <strong>Zuul 1.x</strong>。Zuul 1.x 基于 Servlet 阻塞 I/O 架构，在高并发场景下性能有限。而 Zuul 2.x 虽然采用了 Netty 非阻塞架构，但 Spring Cloud 官方并未正式集成 Zuul 2.x。Spring Cloud Gateway 起步要比 Zuul 2.x 更早。</p>\n<p>为了提升网关的性能，Spring Cloud Gateway 基于 Spring WebFlux 。Spring WebFlux 使用 Reactor 库来实现响应式编程模型，底层基于 Netty 实现同步非阻塞的 I/O。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/distributed-system/api-gateway/springcloud-gateway- demo.png\" alt></p>\n<p>Spring Cloud Gateway 不仅提供统一的路由方式，并且基于 Filter 链的方式提供了网关基本的功能，例如：安全，监控/指标，限流。</p>\n<p>Spring Cloud Gateway 和 Zuul 2.x 的差别不大，也是通过过滤器来处理请求。不过，目前更加推荐使用 Spring Cloud Gateway 而非 Zuul，Spring Cloud 生态对其支持更加友好。</p>\n<ul>\n<li>GitHub 地址： <a href=\"https://github.com/spring-cloud/spring-cloud-gateway\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/spring-cloud/spring-cloud-gateway</a></li>\n<li>官网： <a href=\"https://spring.io/projects/spring-cloud-gateway\" target=\"_blank\" rel=\"noopener noreferrer\">https://spring.io/projects/spring-cloud-gateway</a></li>\n</ul>\n<h2>Spring Cloud Gateway 的工作流程？</h2>\n<p>Spring Cloud Gateway 的工作流程如下图所示：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/distributed-system/api-gateway/spring-cloud-gateway-workflow.png\" alt=\"Spring Cloud Gateway 的工作流程\"></p>\n<p>这是 Spring 官方博客中的一张图，原文地址：<a href=\"https://spring.io/blog/2022/08/26/creating-a-custom-spring-cloud-gateway-filter\" target=\"_blank\" rel=\"noopener noreferrer\">https://spring.io/blog/2022/08/26/creating-a-custom-spring-cloud-gateway-filter</a>。</p>\n<p>具体的流程分析：</p>\n<ol>\n<li><strong>路由判断</strong>：客户端的请求到达网关后，先经过 Gateway Handler Mapping 处理，这里面会做断言（Predicate）判断，看下符合哪个路由规则，这个路由映射后端的某个服务。</li>\n<li><strong>请求过滤</strong>：然后请求到达 Gateway Web Handler，这里面有很多过滤器，组成过滤器链（Filter Chain），这些过滤器可以对请求进行拦截和修改，比如添加请求头、参数校验等等，有点像净化污水。然后将请求转发到实际的后端服务。这些过滤器逻辑上可以称作 Pre-Filters，Pre 可以理解为“在...之前”。</li>\n<li><strong>服务处理</strong>：后端服务会对请求进行处理。</li>\n<li><strong>响应过滤</strong>：后端处理完结果后，返回给 Gateway 的过滤器再次做处理，逻辑上可以称作 Post-Filters，Post 可以理解为“在...之后”。</li>\n<li><strong>响应返回</strong>：响应经过过滤处理后，返回给客户端。</li>\n</ol>\n<p>总结：客户端的请求先通过匹配规则找到合适的路由，就能映射到具体的服务。然后请求经过过滤器处理后转发给具体的服务，服务处理后，再次经过过滤器处理，最后返回给客户端。</p>\n<h2>Spring Cloud Gateway 的断言是什么？</h2>\n<p>断言（Predicate）这个词听起来极其深奥，它是一种编程术语，我们生活中根本就不会用它。说白了它就是对一个表达式进行 if 判断，结果为真或假，如果为真则做这件事，否则做那件事。</p>\n<p>在 Gateway 中，如果客户端发送的请求满足了断言的条件，则映射到指定的路由器，就能转发到指定的服务上进行处理。</p>\n<p>断言配置的示例如下，配置了两个路由规则，有一个 predicates 断言配置，当请求 url 中包含 <code>api/thirdparty</code>，就匹配到了第一个路由 <code>route_thirdparty</code>。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/distributed-system/api-gateway/spring-cloud-gateway-predicate-example.png\" alt=\"断言配置示例\"></p>\n<p>常见的路由断言规则如下图所示：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/distributed-system/api-gateway/spring-cloud-gateway-predicate-rules.png\" alt=\"Spring Cloud GateWay 路由断言规则\"></p>\n<h2>Spring Cloud Gateway 的路由和断言是什么关系？</h2>\n<p>Route 路由和 Predicate 断言的对应关系如下：：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/distributed-system/api-gateway/spring-cloud-gateway-predicate-route.png\" alt=\"路由和断言的对应关系\"></p>\n<ul>\n<li><strong>一对多</strong>：一个路由规则可以包含多个断言。如上图中路由 Route1 配置了三个断言 Predicate。</li>\n<li><strong>同时满足</strong>：如果一个路由规则中有多个断言，则需要同时满足才能匹配。如上图中路由 Route2 配置了两个断言，客户端发送的请求必须同时满足这两个断言，才能匹配路由 Route2。</li>\n<li><strong>第一个匹配成功</strong>：如果一个请求可以匹配多个路由，则映射第一个匹配成功的路由。如上图所示，客户端发送的请求满足 Route3 和 Route4 的断言，但是 Route3 的配置在配置文件中靠前，所以只会匹配 Route3。</li>\n</ul>\n<h2>Spring Cloud Gateway 如何实现动态路由？</h2>\n<p>在使用 Spring Cloud Gateway 的时候，官方文档提供的方案总是基于配置文件或代码配置的方式。</p>\n<p>Spring Cloud Gateway 作为微服务的入口，需要尽量避免重启，而现在配置更改需要重启服务不能满足实际生产过程中的动态刷新、实时变更的业务需求，所以我们需要在 Spring Cloud Gateway 运行时动态配置网关。</p>\n<p>实现动态路由的方式有很多种，其中一种推荐的方式是基于 Nacos 注册中心来做。 Spring Cloud Gateway 可以从注册中心获取服务的元数据（例如服务名称、路径等），然后根据这些信息自动生成路由规则。这样，当你添加、移除或更新服务实例时，网关会自动感知并相应地调整路由规则，无需手动维护路由配置。</p>\n<p>其实这些复杂的步骤并不需要我们手动实现，通过 Nacos Server 和 Spring Cloud Alibaba Nacos Config 即可实现配置的动态变更，官方文档地址：<a href=\"https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config</a> 。</p>\n<h2>Spring Cloud Gateway 的过滤器有哪些？</h2>\n<p>过滤器 Filter 按照请求和响应可以分为两种：</p>\n<ul>\n<li><strong>Pre 类型</strong>：在请求被转发到微服务之前，对请求进行拦截和修改，例如参数校验、权限校验、流量监控、日志输出以及协议转换等操作。</li>\n<li><strong>Post 类型</strong>：微服务处理完请求后，返回响应给网关，网关可以再次进行处理，例如修改响应内容或响应头、日志输出、流量监控等。</li>\n</ul>\n<p>另外一种分类是按照过滤器 Filter 作用的范围进行划分：</p>\n<ul>\n<li><strong>GatewayFilter</strong>：局部过滤器，应用在单个路由或一组路由上的过滤器。标红色表示比较常用的过滤器。</li>\n<li><strong>GlobalFilter</strong>：全局过滤器，应用在所有路由上的过滤器。</li>\n</ul>\n<h3>局部过滤器</h3>\n<p>常见的局部过滤器如下图所示：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/distributed-system/api-gateway/spring-cloud-gateway-gatewayfilters.png\" alt></p>\n<p>具体怎么用呢？这里有个示例，如果 URL 匹配成功，则去掉 URL 中的 “api”。</p>\n<div class=\"language-yaml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"yaml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-yaml\"><span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">filters</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">#过滤器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  - </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">RewritePath=/api/(?&#x3C;segment>.*),/$\\{segment}</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> # 将跳转路径中包含的 “api” 替换成空</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>当然我们也可以自定义过滤器，本篇不做展开。</p>\n<h3>全局过滤器</h3>\n<p>常见的全局过滤器如下图所示：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/distributed-system/api-gateway/spring-cloud-gateway-globalfilters.png\" alt></p>\n<p>全局过滤器最常见的用法是进行负载均衡。配置如下所示：</p>\n<div class=\"language-yaml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"yaml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-yaml\"><span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">spring</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">  cloud</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">    gateway</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">      routes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        - </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">id</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">route_member</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> # 第三方微服务路由规则</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">          uri</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">lb://passjava-member</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> # 负载均衡，将请求转发到注册中心注册的 passjava-member 服务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">          predicates</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"># 断言</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            - </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">Path=/api/member/**</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> # 如果前端请求路径包含 api/member，则应用这条路由规则</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">          filters</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">#过滤器</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            - </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">RewritePath=/api/(?&#x3C;segment>.*),/$\\{segment}</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> # 将跳转路径中包含的api替换成空</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这里有个关键字 <code>lb</code>，用到了全局过滤器 <code>LoadBalancerClientFilter</code>，当匹配到这个路由后，会将请求转发到 passjava-member 服务，且支持负载均衡转发，也就是先将 passjava-member 解析成实际的微服务的 host 和 port，然后再转发给实际的微服务。</p>\n<h2>Spring Cloud Gateway 支持限流吗？</h2>\n<p>Spring Cloud Gateway 自带了限流过滤器，对应的接口是 <code>RateLimiter</code>，<code>RateLimiter</code> 接口只有一个实现类 <code>RedisRateLimiter</code> （基于 Redis + Lua 实现的限流），提供的限流功能比较简易且不易使用。</p>\n<p>从 Sentinel 1.6.0 版本开始，Sentinel 引入了 Spring Cloud Gateway 的适配模块，可以提供两种资源维度的限流：route 维度和自定义 API 维度。也就是说，Spring Cloud Gateway 可以结合 Sentinel 实现更强大的网关流量控制。</p>\n<h2>Spring Cloud Gateway 如何自定义全局异常处理？</h2>\n<p>在 SpringBoot 项目中，我们捕获全局异常只需要在项目中配置 <code>@RestControllerAdvice</code>和 <code>@ExceptionHandler</code>就可以了。不过，这种方式在 Spring Cloud Gateway 下不适用。</p>\n<p>Spring Cloud Gateway 提供了多种全局处理的方式，比较常用的一种是实现<code>ErrorWebExceptionHandler</code>并重写其中的<code>handle</code>方法。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Order</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Component</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">RequiredArgsConstructor</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> GlobalErrorWebExceptionHandler</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ErrorWebExceptionHandler</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ObjectMapper</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> objectMapper</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Mono</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Void</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> handle</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ServerWebExchange</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> exchange</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Throwable</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> ex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>参考</h2>\n<ul>\n<li>Spring Cloud Gateway 官方文档：<a href=\"https://cloud.spring.io/spring-cloud-gateway/reference/html/\" target=\"_blank\" rel=\"noopener noreferrer\">https://cloud.spring.io/spring-cloud-gateway/reference/html/</a></li>\n<li>Creating a custom Spring Cloud Gateway Filter：<a href=\"https://spring.io/blog/2022/08/26/creating-a-custom-spring-cloud-gateway-filter\" target=\"_blank\" rel=\"noopener noreferrer\">https://spring.io/blog/2022/08/26/creating-a-custom-spring-cloud-gateway-filter</a></li>\n<li>全局异常处理: <a href=\"https://zhuanlan.zhihu.com/p/347028665\" target=\"_blank\" rel=\"noopener noreferrer\">https://zhuanlan.zhihu.com/p/347028665</a></li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/system-design/distributed-system/api-gateway/springcloud-gateway-%20demo.png",
      "date_published": "2023-05-04T10:53:24.000Z",
      "date_modified": "2026-03-12T04:06:59.000Z",
      "authors": [],
      "tags": [
        "分布式"
      ]
    },
    {
      "title": "Disruptor常见问题总结",
      "url": "https://javaguide.cn/high-performance/message-queue/disruptor-questions.html",
      "id": "https://javaguide.cn/high-performance/message-queue/disruptor-questions.html",
      "summary": "本文总结 Disruptor 高性能内存队列的核心知识与面试要点，涵盖 Disruptor 架构（RingBuffer/Sequencer/WaitStrategy）、高性能原理（无锁设计/缓存行填充/预分配内存）、与 ArrayBlockingQueue 对比、生产者消费者模式等，助力 Disruptor 学习与面试。",
      "content_html": "<p>Disruptor 是一个相对冷门一些的知识点，不过，如果你的项目经历中用到了 Disruptor 的话，那面试中就很可能会被问到。</p>\n<p>一位球友之前投稿的面经（社招）中就涉及一些 Disruptor 的问题，文章传送门：<a href=\"https://mp.weixin.qq.com/s/C5QMjwEb6pzXACqZsyqC4A\" target=\"_blank\" rel=\"noopener noreferrer\">圆梦！顺利拿到字节、淘宝、拼多多等大厂 offer！</a> 。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/high-performance/message-queue/disruptor-interview-questions.png\" alt></p>\n<p>这篇文章可以看作是对 Disruptor 做的一个简单总结，每个问题都不会扯太深入，主要针对面试或者速览 Disruptor。</p>\n<h2>Disruptor 是什么？</h2>\n<p>Disruptor 是一个开源的高性能内存队列，诞生初衷是为了解决内存队列的性能和内存安全问题，由英国外汇交易公司 LMAX 开发。</p>\n<p>根据 Disruptor 官方介绍，基于 Disruptor 开发的系统 LMAX（新的零售金融交易平台），单线程就能支撑每秒 600 万订单。Martin Fowler 在 2011 年写的一篇文章 <a href=\"https://martinfowler.com/articles/lmax.html\" target=\"_blank\" rel=\"noopener noreferrer\">The LMAX Architecture</a> 中专门介绍过这个 LMAX 系统的架构，感兴趣的可以看看这篇文章。。</p>\n<p>LMAX 公司 2010 年在 QCon 演讲后，Disruptor 获得了业界关注，并获得了 2011 年的 Oracle 官方的 Duke's Choice Awards(Duke 选择大奖)。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/high-performance/message-queue/640.png\" alt></p>\n<blockquote>\n<p>“Duke 选择大奖”旨在表彰过去一年里全球个人或公司开发的、最具影响力的 Java 技术应用，由甲骨文公司主办。含金量非常高！</p>\n</blockquote>\n<p>我专门找到了 Oracle 官方当年颁布获得 Duke's Choice Awards 项目的那篇文章（文章地址：<a href=\"https://blogs.oracle.com/java/post/and-the-winners-arethe-dukes-choice-award%EF%BC%89\" target=\"_blank\" rel=\"noopener noreferrer\">https://blogs.oracle.com/java/post/and-the-winners-arethe-dukes-choice-award）</a> 。从文中可以看出，同年获得此大奖荣誉的还有大名鼎鼎的 Netty、JRebel 等项目。</p>\n<p><img src=\"https://oss.javaguide.cn/javaguide/image-20211015152323898.png\" alt=\"2011 年的 Oracle 官方的 Duke's Choice Awards\"></p>\n<p>Disruptor 提供的功能优点类似于 Kafka、RocketMQ 这类分布式队列，不过，其作为范围是 JVM(内存)。</p>\n<ul>\n<li>Github 地址：<a href=\"https://github.com/LMAX-Exchange/disruptor\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/LMAX-Exchange/disruptor</a></li>\n<li>官方教程： <a href=\"https://lmax-exchange.github.io/disruptor/user-guide/index.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://lmax-exchange.github.io/disruptor/user-guide/index.html</a></li>\n</ul>\n<p>关于如何在 Spring Boot 项目中使用 Disruptor，可以看这篇文章：<a href=\"https://mp.weixin.qq.com/s/0iG5brK3bYF0BgSjX4jRiA\" target=\"_blank\" rel=\"noopener noreferrer\">Spring Boot + Disruptor 实战入门</a> 。</p>\n<h2>为什么要用 Disruptor？</h2>\n<p>Disruptor 主要解决了 JDK 内置线程安全队列的性能和内存安全问题。</p>\n<p><strong>JDK 中常见的线程安全的队列如下</strong>：</p>\n<p>| 队列名字                | 锁                      | 是否有界 |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/high-performance/message-queue/disruptor-interview-questions.png",
      "date_published": "2023-05-04T10:53:24.000Z",
      "date_modified": "2026-01-21T03:04:06.000Z",
      "authors": [],
      "tags": [
        "高性能"
      ]
    },
    {
      "title": "NAT 协议详解（网络层）",
      "url": "https://javaguide.cn/cs-basics/network/nat.html",
      "id": "https://javaguide.cn/cs-basics/network/nat.html",
      "summary": "解析 NAT 的地址转换与端口映射机制，结合 LAN/WAN 通信与转换表，理解家庭与企业网络的实践细节。",
      "content_html": "<h2>应用场景</h2>\n<p><strong>NAT 协议（Network Address Translation）</strong> 的应用场景如同它的名称——网络地址转换，应用于内部网到外部网的地址转换过程中。具体地说，在一个小的子网（局域网，Local Area Network，LAN）内，各主机使用的是同一个 LAN 下的 IP 地址，但在该 LAN 以外，在广域网（Wide Area Network，WAN）中，需要一个统一的 IP 地址来标识该 LAN 在整个 Internet 上的位置。</p>\n<p>这个场景其实不难理解。随着一个个小型办公室、家庭办公室（Small Office, Home Office, SOHO）的出现，为了管理这些 SOHO，一个个子网被设计出来，从而在整个 Internet 中的主机数量将非常庞大。如果每个主机都有一个“绝对唯一”的 IP 地址，那么 IPv4 地址的表达能力可能很快达到上限（$2^{32}$）。因此，实际上，SOHO 子网中的 IP 地址是“相对的”，这在一定程度上也缓解了 IPv4 地址的分配压力。</p>\n<p>SOHO 子网的“代理人”，也就是和外界的窗口，通常由路由器扮演。路由器的 LAN 一侧管理着一个小子网，而它的 WAN 接口才是真正参与到 Internet 中的接口，也就有一个“绝对唯一的地址”。NAT 协议，正是在 LAN 中的主机在与 LAN 外界通信时，起到了地址转换的关键作用。</p>\n<h2>细节</h2>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/network/nat-demo.png\" alt=\"NAT 协议\"></p>\n<p>假设当前场景如上图。中间是一个路由器，它的右侧组织了一个 LAN，网络号为<code>10.0.0/24</code>。LAN 侧接口的 IP 地址为<code>10.0.0.4</code>，并且该子网内有至少三台主机，分别是<code>10.0.0.1</code>，<code>10.0.0.2</code>和<code>10.0.0.3</code>。路由器的左侧连接的是 WAN，WAN 侧接口的 IP 地址为<code>138.76.29.7</code>。</p>\n<p>首先，针对以上信息，我们有如下事实需要说明：</p>\n<ol>\n<li>路由器右侧子网的网络地址为 <code>10.0.0.0/24</code>（网络前缀 24 位，主机号占 8 位），三台主机地址以及路由器的 LAN 侧接口地址，均由 DHCP 协议规定。而且，该 DHCP 运行在路由器内部（路由器自维护一个小 DHCP 服务器），从而为子网内提供 DHCP 服务。</li>\n<li>路由器的 WAN 侧接口地址同样由 DHCP 协议规定，但该地址是路由器从 ISP（网络服务提供商）处获得，也就是该 DHCP 通常运行在路由器所在区域的 DHCP 服务器上。</li>\n</ol>\n<p>现在，路由器内部还运行着 NAT 协议，从而为 LAN-WAN 间通信提供地址转换服务。为此，一个很重要的结构是 <strong>NAT 转换表</strong>。为了说明 NAT 的运行细节，假设有以下请求发生：</p>\n<ol>\n<li>主机<code>10.0.0.1</code>向 IP 地址为<code>128.119.40.186</code>的 Web 服务器（端口 80）发送了 HTTP 请求（如请求页面）。此时，主机<code>10.0.0.1</code>将随机指派一个端口，如<code>3345</code>，作为本次请求的源端口号，将该请求发送到路由器中（目的地址将是<code>128.119.40.186</code>，但会先到达<code>10.0.0.4</code>）。</li>\n<li><code>10.0.0.4</code>即路由器的 LAN 接口收到<code>10.0.0.1</code>的请求。路由器将为该请求指派一个新的源端口号，如<code>5001</code>，并将请求报文发送给 WAN 接口<code>138.76.29.7</code>。同时，在 NAT 转换表中记录一条转换记录<strong>138.76.29.7:5001——10.0.0.1:3345</strong>。</li>\n<li>请求报文到达 WAN 接口，继续向目的主机<code>128.119.40.186</code>发送。</li>\n</ol>\n<p>之后，将会有如下响应发生：</p>\n<ol>\n<li>主机<code>128.119.40.186</code>收到请求，构造响应报文，并将其发送给目的地<code>138.76.29.7:5001</code>。</li>\n<li>响应报文到达路由器的 WAN 接口。路由器查询 NAT 转换表，发现<code>138.76.29.7:5001</code>在转换表中有记录，从而将其目的地址和目的端口转换成为<code>10.0.0.1:3345</code>，再发送到<code>10.0.0.4</code>上。</li>\n<li>被转换的响应报文到达路由器的 LAN 接口，继而被转发至目的地<code>10.0.0.1</code>。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/network/nat-demo2.png\" alt=\"LAN-WAN 间通信提供地址转换\"></p>\n<p>🐛 修正（参见：<a href=\"https://github.com/Snailclimb/JavaGuide/issues/2009\" target=\"_blank\" rel=\"noopener noreferrer\">issue#2009</a>）：上图第四步的 Dest 值应该为 <code>10.0.0.1:3345</code> 而不是<s><code>138.76.29.7:5001</code></s>，这里笔误了。</p>\n<h2>划重点</h2>\n<p>针对以上过程，有以下几个重点需要强调：</p>\n<ol>\n<li>当请求报文到达路由器，并被指定了新端口号时，由于端口号有 16 位，因此，通常来说，一个路由器管理的 LAN 中的最大主机数 $≈65500$（$2^{16}$ 的地址空间），但通常 SOHO 子网内不会有如此多的主机数量。</li>\n<li>对于目的服务器来说，从来不知道“到底是哪个主机给我发送的请求”，它只知道是来自<code>138.76.29.7:5001</code>的路由器转发的请求。因此，可以说，<strong>路由器在 WAN 和 LAN 之间起到了屏蔽作用</strong>，所有内部主机发送到外部的报文，都具有同一个 IP 地址（不同的端口号），所有外部发送到内部的报文，也都只有一个目的地（不同端口号），是经过了 NAT 转换后，外部报文才得以正确地送达内部主机。</li>\n<li>在报文穿过路由器，发生 NAT 转换时，如果 LAN 主机 IP 已经在 NAT 转换表中注册过了，则不需要路由器新指派端口，而是直接按照转换记录穿过路由器。同理，外部报文发送至内部时也如此。</li>\n</ol>\n<p>总结 NAT 协议的特点，有以下几点：</p>\n<ol>\n<li>NAT 协议通过对 WAN 屏蔽 LAN，有效地缓解了 IPv4 地址分配压力。</li>\n<li>LAN 主机 IP 地址的变更，无需通告 WAN。</li>\n<li>WAN 的 ISP 变更接口地址时，无需通告 LAN 内主机。</li>\n<li>LAN 主机对 WAN 不可见，不可直接寻址，可以保证一定程度的安全性。</li>\n</ol>\n<p>然而，NAT 协议由于其独特性，存在着一些争议。比如，可能你已经注意到了，<strong>NAT 协议在 LAN 以外，标识一个内部主机时，使用的是端口号，因为 IP 地址都是相同的</strong>。这种将端口号作为主机寻址的行为，可能会引发一些误会。此外，路由器作为网络层的设备，修改了传输层的分组内容（修改了源 IP 地址和端口号），同样是不规范的行为。但是，尽管如此，NAT 协议作为 IPv4 时代的产物，极大地方便了一些本来棘手的问题，一直被沿用至今。</p>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/cs-basics/network/nat-demo.png",
      "date_published": "2023-04-30T08:44:12.000Z",
      "date_modified": "2026-02-22T12:21:13.000Z",
      "authors": [],
      "tags": [
        "计算机基础"
      ]
    },
    {
      "title": "程序员简历编写指南",
      "url": "https://javaguide.cn/interview-preparation/resume-guide.html",
      "id": "https://javaguide.cn/interview-preparation/resume-guide.html",
      "summary": "程序员简历编写指南：从筛选逻辑出发讲清简历结构、项目经历与技能描述写法，提供简历模板与避坑建议，帮助你提高简历通过率并让面试官更好地深挖你的亮点。",
      "content_html": "<div class=\"hint-container tip\">\n<p class=\"hint-container-title\">友情提示</p>\n<p>本文节选自 <strong><a href=\"/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\">《Java 面试指北》</a></strong>。这是一份教你如何更高效地准备面试的小册，涵盖常见八股文（系统设计、常见框架、分布式、高并发 ……）、优质面经等内容。</p>\n</div>\n<h2>前言</h2>\n<p>一份好的简历可以在整个申请面试以及面试过程中起到非常重要的作用。</p>\n<p><strong>为什么说简历很重要呢？</strong> 我们可以从下面几点来说：</p>\n<p><strong>1、简历就像是我们的一个门面一样，它在很大程度上决定了是否能够获得面试机会。</strong></p>\n<ul>\n<li>假如你是网申，你的简历必然会经过 HR 的筛选，一张简历 HR 可能也就花费 10 秒钟左右看一下，然后决定你能否进入面试。</li>\n<li>假如你是内推，如果你的简历没有什么优势的话，就算是内推你的人再用心，也无能为力。</li>\n</ul>\n<p>另外，就算你通过了第一轮的筛选获得面试机会，后面的面试中，面试官也会根据你的简历来判断你究竟是否值得他花费很多时间去面试。</p>\n<p><strong>2、简历上的内容很大程度上决定了面试官提问的侧重点。</strong></p>\n<ul>\n<li>一般情况下你的简历上注明你会的东西才会被问到（Java 基础、集合、并发、MySQL、Redis 、Spring、Spring Boot 这些算是每个人必问的），比如写了你熟练使用 Redis,那面试官就很大概率会问你 Redis 的一些问题，再比如你写了你在项目中使用了消息队列，那面试官大概率问很多消息队列相关的问题。</li>\n<li>技能熟练度在很大程度上也决定了面试官提问的深度。</li>\n</ul>\n<p>在不夸大自己能力的情况下，写出一份好的简历也是一项很棒的能力。一般情况下，技术能力和学习能力比较厉害的，写出来的简历也比较棒！</p>\n<h2>简历模板</h2>\n<p>简历的样式真的非常非常重要！！！如果你的简历样式丑到没朋友的话，面试官真的没有看下去的欲望。一天处理上百份的简历的痛苦，你不懂！</p>\n<p>我这里的话，推荐大家使用 Markdown 语法写简历，然后再将 Markdown 格式转换为 PDF 格式后进行简历投递。如果你对 Markdown 语法不太了解的话，可以花半个小时简单看一下 Markdown 语法说明: <a href=\"http://www.markdown.cn/\" target=\"_blank\" rel=\"noopener noreferrer\">http://www.markdown.cn/</a>。</p>\n<p>下面是我收集的一些还不错的简历模板：</p>\n<ul>\n<li>适合中文的简历模板收集（推荐，开源免费）：<a href=\"https://github.com/dyweb/awesome-resume-for-chinese\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/dyweb/awesome-resume-for-chinese</a></li>\n<li>木及简历（推荐，部分免费） ： <a href=\"https://www.mujicv.com/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.mujicv.com/</a></li>\n<li>简单简历（推荐，部分免费）：<a href=\"https://easycv.cn/\" target=\"_blank\" rel=\"noopener noreferrer\">https://easycv.cn/</a></li>\n<li>极简简历（免费）： <a href=\"https://www.polebrief.com/index\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.polebrief.com/index</a></li>\n<li>Markdown 简历排版工具（开源免费）：<a href=\"https://resume.mdnice.com/\" target=\"_blank\" rel=\"noopener noreferrer\">https://resume.mdnice.com/</a></li>\n<li>站长简历（收费，支持 AI 生成）：<a href=\"https://jianli.chinaz.com/\" target=\"_blank\" rel=\"noopener noreferrer\">https://jianli.chinaz.com/</a></li>\n<li>typora+markdown+css 自定义简历模板 ：<a href=\"https://github.com/Snailclimb/typora-markdown-resume\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/Snailclimb/typora-markdown-resume</a></li>\n<li>超级简历（部分收费） ： <a href=\"https://www.wondercv.com/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.wondercv.com/</a></li>\n</ul>\n<p>上面这些简历模板大多是只有 1 页内容，很难展现足够的信息量。如果你不是顶级大牛（比如 ACM 大赛获奖）的话，我建议还是尽可能多写一点可以突出你自己能力的内容（校招生 2 页之内，社招生 3 页之内，记得精炼语言，不要过多废话）。</p>\n<p>再总结几点 <strong>简历排版的注意事项</strong>：</p>\n<ul>\n<li>尽量简洁，不要太花里胡哨。</li>\n<li>技术名词最好规范大小写比较好，比如 java-&gt;Java ，spring boot -&gt; Spring Boot 。这个虽然有些面试官不会介意，但是很多面试官都会在意这个细节的。</li>\n<li>中文和数字英文之间加上空格的话看起来会舒服一点。</li>\n</ul>\n<p>另外，知识星球里还有真实的简历模板可供参考，地址：<a href=\"https://t.zsxq.com/12ypxGNzU\" target=\"_blank\" rel=\"noopener noreferrer\">https://t.zsxq.com/12ypxGNzU</a> （需加入<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>获取）。</p>\n<p><img src=\"https://oss.javaguide.cn/javamianshizhibei/image-20230918073550606.png\" alt></p>\n<h2>简历内容</h2>\n<h3>个人信息</h3>\n<ul>\n<li>最基本的 ：姓名（身份证上的那个）、年龄、电话、籍贯、联系方式、邮箱地址</li>\n<li>潜在加分项 ： Github 地址、博客地址（如果技术博客和 Github 上没有什么内容的话，就不要写了）</li>\n</ul>\n<p>示例：</p>\n<p><img src=\"https://oss.javaguide.cn/zhishixingqiu/20210428212337599.png\" alt></p>\n<p><strong>简历要不要放照片呢？</strong> 很多人写简历的时候都有这个问题。</p>\n<p>其实放不放都行，影响不大，完全不用在意这个问题。除非，你投递的岗位明确要求要放照片。 不过，如果要放的话，不要放生活照，还是应该放正规一些的照片比如证件照。</p>\n<h3>求职意向</h3>\n<p>你想要应聘什么岗位，希望在什么城市。另外，你也可以将求职意向放到个人信息这块写。</p>\n<p>示例：</p>\n<p><img src=\"https://oss.javaguide.cn/zhishixingqiu/20210428212410288.png\" alt></p>\n<h3>教育经历</h3>\n<p>教育经历也不可或缺。通过教育经历的介绍，你要确保能让面试官就可以知道你的学历、专业、毕业学校以及毕业的日期。</p>\n<p>示例：</p>\n<blockquote>\n<p>北京理工大学 硕士，软件工程 2019.09 - 2022.01<br>\n湖南大学 学士，应用化学 2015.09 ~ 2019.06</p>\n</blockquote>\n<h3>专业技能</h3>\n<p>先问一下你自己会什么，然后看看你意向的公司需要什么。一般 HR 可能并不太懂技术，所以他在筛选简历的时候可能就盯着你专业技能的关键词来看。对于公司有要求而你不会的技能，你可以花几天时间学习一下，然后在简历上可以写上自己了解这个技能。</p>\n<p>下面是一份最新的 Java 后端开发技能清单，你可以根据自身情况以及岗位招聘要求做动态调整，核心思想就是尽可能满足岗位招聘的所有技能要求。</p>\n<p><img src=\"https://oss.javaguide.cn/zhishixingqiu/jinengmuban.png\" alt=\"Java 后端技能模板\"></p>\n<p>我这里再单独放一个我看过的某位同学的技能介绍，我们来找找问题。</p>\n<p><img src=\"https://oss.javaguide.cn/zhishixingqiu/up-a58d644340f8ce5cd32f9963f003abe4233.png\" alt></p>\n<p>上图中的技能介绍存在的问题：</p>\n<ul>\n<li>技术名词最好规范大小写比较好，比如 java-&gt;Java ，spring boot -&gt; Spring Boot 。这个虽然有些面试官不会介意，但是很多面试官都会在意这个细节的。</li>\n<li>技能介绍太杂，没有亮点。不需要全才，某个领域做得好就行了！</li>\n<li>对 Java 后台开发的部分技能比如 Spring Boot 的熟悉度仅仅为了解，无法满足企业的要求。</li>\n</ul>\n<h3>实习经历/工作经历（重要）</h3>\n<p>工作经历针对社招，实习经历针对校招。</p>\n<p>工作经历建议采用时间倒序的方式来介绍。实习经历和工作经历都需要简单突出介绍自己在职期间主要做了什么。</p>\n<p>示例：</p>\n<blockquote>\n<p><strong>XXX 公司 （201X 年 X 月 ~ 201X 年 X 月 ）</strong></p>\n<ul>\n<li><strong>职位</strong>：Java 后端开发工程师</li>\n<li><strong>工作内容</strong>：主要负责 XXX</li>\n</ul>\n</blockquote>\n<h3>项目经历（重要）</h3>\n<p>简历上有一两个项目经历很正常，但是真正能把项目经历很好的展示给面试官的非常少。</p>\n<p>很多求职者的项目经历介绍都会面临过于啰嗦、过于简单、没突出亮点等问题。</p>\n<p>项目经历介绍模板如下：</p>\n<blockquote>\n<p>项目名称（字号要大一些）</p>\n<p>2017-05~2018-06 淘宝 Java 后端开发工程师</p>\n<ul>\n<li><strong>项目描述</strong> : 简单描述项目是做什么的。</li>\n<li><strong>技术栈</strong> ：用了什么技术（如 Spring Boot + MySQL + Redis + Mybatis-plus + Spring Security + Oauth2）</li>\n<li><strong>工作内容/个人职责</strong> : 简单描述自己做了什么，解决了什么问题，带来了什么实质性的改善。突出自己的能力，不要过于平淡的叙述。</li>\n<li><strong>个人收获（可选）</strong> : 从这个项目中你学会了那些东西，使用到了那些技术，学会了那些新技术的使用。通常是可以不用写个人收获的，因为你在个人职责介绍中写的东西已经表明了自己的主要收获。</li>\n<li><strong>项目成果（可选）</strong> :简单描述这个项目取得了什么成绩。</li>\n</ul>\n</blockquote>\n<p><strong>1、项目经历应该突出自己做了什么，简单概括项目基本情况。</strong></p>\n<p>项目介绍尽量压缩在两行之内，不需要介绍太多，但也不要随便几个字就介绍完了。</p>\n<p>另外，个人收获和项目成果都是可选的，如果选择写的话，也不要花费太多篇幅，记住你的重点是介绍工作内容/个人职责。</p>\n<p><strong>2、技术架构直接写技术名词就行，不要再介绍技术是干嘛的了，没意义，属于无效介绍。</strong></p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/interview-preparation/46c92fbc5160e65dd85c451143177144.png\" alt></p>\n<p><strong>3、尽量减少纯业务的个人职责介绍，对于面试不太友好。尽量再多挖掘一些亮点（6~8 条个人职责介绍差不多了，做好筛选），最好可以体现自己的综合素质，比如你是如何协调项目组成员协同开发的或者在遇到某一个棘手的问题的时候你是如何解决的又或者说你在这个项目优化了某个模块的性能。</strong></p>\n<p>即使不是你做的功能模块或者解决的问题，你只要搞懂吃透了就能拿来自己用，适当润色即可！</p>\n<p>像性能优化方向上的亮点面试之前也比较容易准备，但也不要都是性能优化相关的，这种也算是一个极端。</p>\n<p>另外，技术优化取得的成果尽量要量化一下：</p>\n<ul>\n<li>使用 xxx 技术解决了 xxx 问题，系统 QPS 从 xxx 提高到了 xxx。</li>\n<li>使用 xxx 技术了优化了 xxx 接口，系统 QPS 从 xxx 提高到了 xxx。</li>\n<li>使用 xxx 技术解决了 xxx 问题，查询速度优化了 xxx，系统 QPS 达到 10w+。</li>\n<li>使用 xxx 技术优化了 xxx 模块，响应时间从 2s 降低到 0.2s。</li>\n<li>……</li>\n</ul>\n<p>个人职责介绍示例（这里只是举例，不要照搬，结合自己项目经历自己去写，不然面试的时候容易被问倒） ：</p>\n<ul>\n<li>基于 Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权，使用 RBAC 权限模型实现动态权限控制。</li>\n<li>参与项目订单模块的开发，负责订单创建、删除、查询等功能，基于 Spring 状态机实现订单状态流转。</li>\n<li>商品和订单搜索场景引入 Elasticsearch，并且实现了相关商品推荐以及搜索提示功能。</li>\n<li>整合 Canal + RabbitMQ 将 MySQL 增量数据（如商品、订单数据）同步到 Elasticsearch。</li>\n<li>利用 RabbitMQ 官方提供的延迟队列插件实现延时任务场景比如订单超时自动取消、优惠券过期提醒、退款处理。</li>\n<li>消息推送系统引入 RabbitMQ 实现异步处理、削峰填谷和服务解耦，最高推送速度 10w/s，单日最大消息量 2000 万。</li>\n<li>使用 MAT 工具分析 dump 文件解决了广告服务新版本上线后导致大量的服务超时告警的问题。</li>\n<li>排查并解决扣费模块由于扣费父任务和反作弊子任务使用同一个线程池导致的死锁问题。</li>\n<li>基于 EasyExcel 实现广告投放数据的导入导出，通过 MyBatis 批处理插入数据，基于任务表实现异步。</li>\n<li>负责用户统计模块的开发，使用 CompletableFuture 并行加载后台用户统计模块的数据信息，平均相应时间从 3.5s 降低到 1s。</li>\n<li>基于 Sentinel 对核心场景(如用户登入注册、收货地址查询等)进行限流、降级，保护系统，提升用户体验。</li>\n<li>热门数据（如首页、热门博客）使用 Redis+Caffeine 两级缓存，解决了缓存击穿和穿透问题，查询速度毫秒级，QPS 30w+。</li>\n<li>使用 CompletableFuture 优化购物车查询模块，对获取用户信息、商品详情、优惠券信息等异步 RPC 调用进行编排，响应时间从 2s 降低为 0.2s。</li>\n<li>搭建 EasyMock 服务，用于模拟第三方平台接口，方便了在网络隔离情况下的接口对接工作。</li>\n<li>基于 SkyWalking + Elasticsearch 搭建分布式链路追踪系统实现全链路监控。</li>\n</ul>\n<p><strong>4、如果你觉得你的项目技术比较落后的话，可以自己私下进行改进。重要的是让项目比较有亮点，通过什么方式就无所谓了。</strong></p>\n<p>项目经历这部分对于简历来说非常重要，<a href=\"https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\" rel=\"noopener noreferrer\">《Java 面试指北》</a>的面试准备篇有好几篇关于优化项目经历的文章，建议你仔细阅读一下，应该会对你有帮助。</p>\n<p><img src=\"https://oss.javaguide.cn/zhishixingqiu/4e11dbc842054e53ad6c5f0445023eb5~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p><strong>5、避免个人职责介绍都是围绕一个技术点来写，非常不可取。</strong></p>\n<p><img src=\"https://oss.javaguide.cn/zhishixingqiu/image-20230424222513028.png\" alt></p>\n<p><strong>6、避免模糊性描述，介绍要具体（技术+场景+效果），也要注意精简语言（避免堆砌技术词，省略不必要的描述）。</strong></p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/interview-preparation/project-experience-avoiding-ambiguity-descriptio.png\" alt></p>\n<h3>荣誉奖项（可选）</h3>\n<p>如果你有含金量比较高的竞赛（比如 ACM、阿里的天池大赛）的获奖经历的话，荣誉奖项这块内容一定要写一下！并且，你还可以将荣誉奖项这块内容适当往前放，放在一个更加显眼的位置。</p>\n<h3>校园经历（可选）</h3>\n<p>如果有比较亮眼的校园经历的话就简单写一下，没有就不写！</p>\n<h3>个人评价</h3>\n<p><strong>个人评价就是对自己的解读，一定要用简洁的语言突出自己的特点和优势，避免废话！</strong> 像勤奋、吃苦这些比较虚的东西就不要扯了，面试官看着这种个人评价就烦。</p>\n<p>我们可以从下面几个角度来写个人评价：</p>\n<ul>\n<li>文档编写能力、学习能力、沟通能力、团队协作能力</li>\n<li>对待工作的态度以及个人的责任心</li>\n<li>能承受的工作压力以及对待困难的态度</li>\n<li>对技术的追求、对代码质量的追求</li>\n<li>分布式、高并发系统开发或维护经验</li>\n</ul>\n<p>列举 3 个实际的例子：</p>\n<ul>\n<li>学习能力较强，大三参加国家软件设计大赛的时候快速上手 Python 写了一个可配置化的爬虫系统。</li>\n<li>具有团队协作精神，大三参加国家软件设计大赛的时候协调项目组内 5 名开发同学，并对编码遇到困难的同学提供帮助，最终顺利在 1 个月的时间完成项目的核心功能。</li>\n<li>项目经验丰富，在校期间主导过多个企业级项目的开发。</li>\n</ul>\n<h2>STAR 法则和 FAB 法则</h2>\n<h3>STAR 法则（Situation Task Action Result）</h3>\n<p>相信大家一定听说过 STAR 法则。对于面试，你可以将这个法则用在自己的简历以及和面试官沟通交流的过程中。</p>\n<p>STAR 法则由下面 4 个单词组成（STAR 法则的名字就是由它们的首字母组成）：</p>\n<ul>\n<li><strong>Situation：</strong> 情景。 事情是在什么情况下发生的？</li>\n<li><strong>Task：</strong> 任务。你的任务是什么？</li>\n<li><strong>Action：</strong> 行动。你做了什么？</li>\n<li><strong>Result：</strong> 结果。最终的结果怎样？</li>\n</ul>\n<h3>FAB 法则（Feature Advantage Benefit）</h3>\n<p>除了 STAR 法则，你还需要了解在销售行业经常用到的一个叫做 FAB 的法则。</p>\n<p>FAB 法则由下面 3 个单词组成（FAB 法则的名字就是由它们的首字母组成）：</p>\n<ul>\n<li><strong>Feature：</strong> 你的特征/优势是什么？</li>\n<li><strong>Advantage：</strong> 比别人好在哪些地方；</li>\n<li><strong>Benefit：</strong> 如果雇佣你，招聘方会得到什么好处。</li>\n</ul>\n<p>简单来说，<strong>FAB 法则主要是让你的面试官知道你的优势和你能为公司带来的价值。</strong></p>\n<h2>建议</h2>\n<h3>避免页数过多</h3>\n<p>精简表述，突出亮点。校招简历建议不要超过 2 页，社招简历建议不要超过 3 页。如果内容过多的话，不需要非把内容压缩到一页，保持排版干净整洁就可以了。</p>\n<p>看了几千份简历，有少部分同学的简历页数都接近 10 页了，让我头皮发麻。</p>\n<p><img src=\"https://oss.javaguide.cn/zhishixingqiu/image-20230508223646164.png\" alt=\"简历页数过多\"></p>\n<h3>避免语义模糊</h3>\n<p>尽量避免主观表述，少一点语义模糊的形容词。表述要简洁明了，简历结构要清晰。</p>\n<p>举例：</p>\n<ul>\n<li>不好的表述：我在团队中扮演了很重要的角色。</li>\n<li>好的表述：我作为后端技术负责人，领导团队完成后端项目的设计与开发。</li>\n</ul>\n<h3>注意简历样式</h3>\n<p>简历样式同样很重要，一定要注意！不必追求花里胡哨，但要尽量保证结构清晰且易于阅读。</p>\n<h3>其他</h3>\n<ul>\n<li>一定要使用 PDF 格式投递，不要使用 Word 或者其他格式投递。这是最基本的！</li>\n<li>不会的东西就不要写在简历上了。注意简历真实性，适当润色没有问题。</li>\n<li>工作经历建议采用时间倒序的方式来介绍，实习经历建议将最有价值的放在最前面。</li>\n<li>将自己的项目经历完美的展示出来非常重要，重点是突出自己做了什么（挖掘亮点），而不是介绍项目是做什么的。</li>\n<li>项目经历建议以时间倒序排序，另外项目经历不在于多（精选 2~3 即可），而在于有亮点。</li>\n<li>准备面试的过程中应该将你写在简历上的东西作为重点，尤其是项目经历上和技能介绍上的。</li>\n<li>面试和工作是两回事，聪明的人会把面试官往自己擅长的领域领，其他人则被面试官牵着鼻子走。虽说面试和工作是两回事，但是你要想要获得自己满意的 offer ，你自身的实力必须要强。</li>\n</ul>\n<h2>简历修改</h2>\n<p>到目前为止，我至少帮助 <strong>6000+</strong> 位球友提供了免费的简历修改服务。由于个人精力有限，修改简历仅限加入星球的读者，需要帮看简历的话，可以加入 <a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html#%E7%AE%80%E5%8E%86%E4%BF%AE%E6%94%B9\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>JavaGuide 官方知识星球</strong></a>（点击链接查看详细介绍）。</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/简历修改2.jpg\" alt=\"img\"></p>\n<p>虽然收费只有培训班/训练营的百分之一，但是知识星球里的内容质量更高，提供的服务也更全面，非常适合准备 Java 面试和学习 Java 的同学。</p>\n<p>下面是星球提供的部分服务（点击下方图片即可获取知识星球的详细介绍）：</p>\n<p><a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/xingqiufuwu.png\" alt=\"星球服务\"></a></p>\n<p>这里再提供一份限时专属优惠卷：</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuan-30.jpg\" alt=\"知识星球30元优惠卷\"></p>\n",
      "image": "https://oss.javaguide.cn/javamianshizhibei/image-20230918073550606.png",
      "date_published": "2023-04-28T13:38:12.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "面试准备"
      ]
    },
    {
      "title": "数据冷热分离详解",
      "url": "https://javaguide.cn/high-performance/data-cold-hot-separation.html",
      "id": "https://javaguide.cn/high-performance/data-cold-hot-separation.html",
      "summary": "本文详解数据冷热分离的核心原理与实践方案，涵盖冷热数据判定策略、多级分层设计、数据迁移一致性保障、冷数据查询优化、存储选型（HBase/TiDB/对象存储），以及订单/日志/内容系统的典型落地案例。",
      "content_html": "<p><a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/xingqiu.png\" alt=\"JavaGuide官方知识星球\"></a></p>\n<h2>什么是数据冷热分离？</h2>\n<p>数据冷热分离是指根据数据的<strong>访问频率</strong>和<strong>业务重要性</strong>，将数据划分为冷数据和热数据，并分别存储在不同性能和成本的存储介质中的架构策略。</p>\n<p>这种架构的核心目标有三个：</p>\n<ol>\n<li><strong>提升查询性能</strong>：热数据存储在高性能介质（如 SSD、内存）中，保障核心业务的响应速度。</li>\n<li><strong>降低存储成本</strong>：冷数据迁移至低成本介质（如 HDD、对象存储），大幅削减存储开支。</li>\n<li><strong>满足合规要求</strong>：部分行业（如金融、医疗）要求数据长期归档，冷热分离可兼顾合规与成本。</li>\n</ol>\n<h3>冷数据和热数据</h3>\n<p><strong>热数据</strong>是指被频繁访问和修改、且需要快速响应的数据；<strong>冷数据</strong>是指访问频率极低、对当前业务价值较小、但需要长期保留的数据。</p>\n<p>冷热数据的区分方法主要有两种：</p>\n<ol>\n<li><strong>时间维度区分</strong>：按照数据的创建时间、更新时间或过期时间划分。例如，订单系统将一段时间前（如 90 天或 1 年）的订单数据标记为冷数据。该方法适用于<strong>数据访问频率与时间强相关</strong>的场景，实现简单、成本低。</li>\n<li><strong>访问频率区分</strong>：将高频访问的数据视为热数据，低频访问的数据视为冷数据。例如，内容系统将<strong>浏览量低于阈值</strong>的文章标记为冷数据。该方法需要额外记录访问频率，适用于<strong>访问频率与数据本身特性强相关</strong>的场景。</li>\n</ol>\n<p><strong>如何选择区分策略？</strong></p>\n<ul>\n<li>若业务数据天然具有时效性（如订单、日志、账单），优先选择<strong>时间维度</strong>，实现成本最低。</li>\n<li>若数据价值与时间无关（如文章、商品、用户画像），需结合<strong>访问频率</strong>进行判定。</li>\n<li>实际项目中，可将两者结合使用：以时间维度为主、访问频率为辅，覆盖更多业务场景。</li>\n</ul>\n<h3>冷热分离的多级分层策略</h3>\n<p>实际落地时，&quot;冷&quot;与&quot;热&quot;往往不是非此即彼的二分法，而是<strong>渐进式多级分层</strong>：</p>\n<p>| 层级         | 数据特性             | 判定规则示例                | 存储策略               |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/high-performance/data-cold-hot-separation.png",
      "date_published": "2023-04-28T11:27:56.000Z",
      "date_modified": "2026-03-13T03:31:15.000Z",
      "authors": [],
      "tags": [
        "高性能"
      ]
    },
    {
      "title": "深度分页介绍及优化建议",
      "url": "https://javaguide.cn/high-performance/deep-pagination-optimization.html",
      "id": "https://javaguide.cn/high-performance/deep-pagination-optimization.html",
      "summary": "深度分页是指查询偏移量过大导致性能下降的场景，本文详解深度分页产生的原因及四种优化方案：范围查询、子查询优化、INNER JOIN 延迟关联、覆盖索引，并分析各方案的适用场景与优缺点。",
      "content_html": "<p><a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/xingqiu.png\" alt=\"JavaGuide官方知识星球\"></a></p>\n<h2>什么是深度分页？怎么导致的？</h2>\n<p>查询偏移量过大的场景我们称为深度分页，这会导致查询性能较低，例如：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"># MySQL 在无法利用索引的情况下跳过1000000条记录后，再获取10条记录</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> t_order </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> id </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">LIMIT</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1000000</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>当查询偏移量过大时，MySQL 的查询优化器可能会选择全表扫描而不是利用索引来优化查询。</p>\n<p><strong>深度分页变慢的根本原因</strong>在于 MySQL 的执行机制：对于 <code>LIMIT offset, N</code>，MySQL 并非直接跳到 <code>offset</code> 处，而是必须从头扫描 <code>offset + N</code> 条记录。如果查询依赖二级索引且不满足覆盖索引，这意味着 MySQL 需要对前 <code>offset</code> 条记录执行毫无意义的<strong>回表查询（产生海量的随机 I/O）</strong>，最后再将这些辛苦查出的数据丢弃。即便优化器最终因代价过高退化为全表扫描，顺序扫描百万行的成本依然巨大。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/mysql/deep-pagination-phenomenon.png\" alt=\"深度分页问题\"></p>\n<p>不同机器上这个查询偏移量过大的临界点可能不同，取决于多个因素，包括硬件配置（如 CPU 性能、磁盘速度）、表的大小、索引的类型和统计信息等。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/mysql/deep-pagination-phenomenon-critical-point.png\" alt=\"转全表扫描的临界点\"></p>\n<p>MySQL 的查询优化器采用基于成本的策略来选择最优的查询执行计划。它会根据 CPU 和 I/O 的成本来决定是否使用索引扫描或全表扫描。如果优化器认为全表扫描的成本更低，它就会放弃使用索引。不过，即使偏移量很大，如果查询中使用了覆盖索引（covering index），MySQL 仍然可能会使用索引，避免回表操作。</p>\n<h2>深度分页优化建议</h2>\n<blockquote>\n<p><strong>本文基于 MySQL 8.0 + InnoDB 存储引擎</strong>，不同版本优化器行为可能存在差异。</p>\n</blockquote>\n<h3>范围查询（游标分页）</h3>\n<p>通过记录上一页最后一条记录的 ID，使用 <code>WHERE id &gt; last_id LIMIT n</code> 获取下一页数据：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"># 通过记录上次查询结果的最后一条记录的 ID 进行下一页的查询</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> t_order </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> id </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 100000</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> id </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">LIMIT</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 10</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>游标分页的核心优势</strong>：<strong>不依赖 ID 的连续性</strong>。MySQL 只需要在 B+ 树上定位到 <code>last_id</code> 的位置，然后顺序向后读取 <code>n</code> 条记录即可，中间是否有断层（如 ID 被删除）完全不影响结果的准确性和性能。</p>\n<p>这种方式的限制：</p>\n<ol>\n<li><strong>不支持跳页</strong>：无法直接跳转到第 N 页，只能逐页向后（或向前）翻页。</li>\n<li><strong>排序字段受限</strong>：如果查询需要按照其他字段（如创建时间）排序而非 ID 排序，需使用联合游标 <code>(sort_field, id)</code> 保证唯一性和顺序。</li>\n<li><strong>并发场景</strong>：当分页查询期间有新数据插入或删除时，可能出现：\n<ul>\n<li><strong>数据遗漏</strong>：查询第二页时，有新数据插入到第一页范围内，导致该数据被&quot;挤&quot;到第二页，但第二页查询已基于旧的最后 ID 跳过它。</li>\n<li><strong>数据重复</strong>：查询第二页时，第一页末尾有数据被删除，原第二页的第一条数据&quot;升&quot;到第一页末尾，导致第二页查询再次返回它。</li>\n</ul>\n</li>\n</ol>\n<h3>子查询</h3>\n<p>我们先查询出 limit 第一个参数对应的主键值，再根据这个主键值再去过滤并 limit，这样效率会更快一些。</p>\n<p>阿里巴巴《Java 开发手册》中也有对应的描述：</p>\n<blockquote>\n<p>利用延迟关联或者子查询优化超多分页场景。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/mysql/alibaba-java-development-handbook-paging.png\" alt></p>\n</blockquote>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 先通过子查询在主键索引上进行偏移，快速找到起始ID</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> t_order</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> id </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">>=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> id </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> t_order </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> id </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">LIMIT</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1000000</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> id </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">LIMIT</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>工作原理</strong>:</p>\n<ol>\n<li>子查询 <code>(SELECT id FROM t_order ORDER BY id LIMIT 1000000, 1)</code> 利用主键索引扫描并跳过前 1000000 条记录，返回第 1000001 条记录的主键值。</li>\n<li>主查询 <code>SELECT * FROM t_order WHERE id &gt;= ... ORDER BY id LIMIT 10</code> 以该主键为起点，获取后续 10 条完整记录。</li>\n</ol>\n<p>不过，某些情况下子查询可能会产生临时表，影响性能，因此在复杂查询中建议优先考虑延迟关联。</p>\n<blockquote>\n<p><strong>复杂过滤场景</strong>：在包含复杂过滤条件的分页场景中（如 <code>WHERE status = 1 ORDER BY id LIMIT 1000000, 10</code>），符合条件的 ID 往往是离散的。此时子查询的优势更加明显：通过在子查询中利用联合索引（如 <code>(status, id)</code>）实现覆盖索引扫描，可以高效地跳过前 100 万条符合条件的记录，定位到目标 ID 后，主查询只需回表 10 次。</p>\n</blockquote>\n<p>当然，我们也可以利用子查询先去获取目标分页的 ID 集合，然后再根据 ID 集合获取内容，但这种写法非常繁琐，不如使用 INNER JOIN 延迟关联。</p>\n<h3>延迟关联</h3>\n<p>延迟关联与子查询的优化思路类似，都是通过将 <code>LIMIT</code> 操作转移到主键索引树上，减少回表次数。相比直接使用子查询，延迟关联通过 <code>INNER JOIN</code> 将子查询结果集成到主查询中，避免了子查询可能产生的临时表。在执行 <code>INNER JOIN</code> 时，MySQL 优化器能够利用索引进行高效的连接操作（如索引扫描或其他优化策略），因此在深度分页场景下，性能通常优于直接使用子查询。</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 使用 INNER JOIN 进行延迟关联</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> t1.*</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> t_order t1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">INNER JOIN</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    -- 这里的子查询可以利用覆盖索引，性能极高</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> id </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> t_order </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> id </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">LIMIT</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1000000</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) t2 </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ON</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> t1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">id</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> t2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">id</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> t1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">id</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>工作原理</strong>:</p>\n<ol>\n<li>子查询 <code>(SELECT id FROM t_order ORDER BY id LIMIT 1000000, 10)</code> 利用主键索引扫描并跳过前 1000000 条记录，返回目标分页的 10 条记录的 ID。</li>\n<li>通过 <code>INNER JOIN</code> 将子查询结果与主表 <code>t_order</code> 关联，获取完整的记录数据。</li>\n</ol>\n<p>除了使用 INNER JOIN 之外，还可以使用逗号连接子查询。</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 使用逗号进行延迟关联</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> t1.* </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> t_order t1,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> id </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> t_order </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> id </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">LIMIT</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1000000</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) t2</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> t1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">id</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> t2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">id</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> t1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">id</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>注意</strong>: 虽然逗号连接子查询也能实现类似的效果，但为了代码可读性和可维护性，建议使用更规范的 <code>INNER JOIN</code> 语法。</p>\n<h3>覆盖索引</h3>\n<p>索引中已经包含了所有需要获取的字段的查询方式称为覆盖索引。</p>\n<p><strong>覆盖索引的好处：</strong></p>\n<ul>\n<li><strong>避免 InnoDB 表进行索引的二次查询，也就是回表操作</strong>：InnoDB 是以聚集索引的顺序来存储的，对于 InnoDB 来说，二级索引在叶子节点中所保存的是行的主键信息，如果是用二级索引查询数据的话，在查找到相应的键值后，还要通过主键进行二次查询才能获取我们真实所需要的数据。而在覆盖索引中，二级索引的键值中可以获取所有的数据，避免了对主键的二次查询（回表），减少了 IO 操作，提升了查询效率。</li>\n<li><strong>减少回表带来的随机 IO</strong>：通过覆盖索引直接返回数据，避免了根据二级索引的主键值回表查询聚簇索引的随机 IO 操作。回表时每次按主键值查找聚簇索引，本质上是随机 IO。</li>\n</ul>\n<p>假设建立了 <code>(code, type)</code> 联合索引，下面的查询即可使用覆盖索引：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"># 在 InnoDB 中，辅助索引天然包含主键 id</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"># 如果只需要查询 id, code, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">type</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> 这三列，只需建立 (code, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">type</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) 的联合索引即可实现覆盖</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> id, code, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">type</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> t_order</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">LIMIT</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1000000</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>⚠️注意</strong>:</p>\n<ul>\n<li>当查询的结果集占表的总行数的很大一部分时，MySQL 查询优化器可能选择放弃使用索引，自动转换为全表扫描。</li>\n<li>虽然可以使用 <code>FORCE INDEX</code> 强制查询优化器走索引，但这种方式可能会导致查询优化器无法选择更优的执行计划，效果并不总是理想。</li>\n</ul>\n<h2>生产落地建议</h2>\n<h3>监控与告警</h3>\n<ul>\n<li><strong>慢查询监控</strong>：监控慢查询日志中 <code>LIMIT</code> 偏移量过大的 SQL，及时发现问题。</li>\n<li><strong>阈值告警</strong>：设置 <code>long_query_time</code> 阈值捕获深度分页查询。</li>\n<li><strong>执行计划检查</strong>：使用 <code>EXPLAIN</code> 定期检查关键分页 SQL 的执行计划，确保优化器按预期使用索引。</li>\n</ul>\n<h3>常见误区</h3>\n<p>| 误区                              | 事实                                                 |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/mysql/deep-pagination-phenomenon.png",
      "date_published": "2023-04-28T11:27:56.000Z",
      "date_modified": "2026-03-13T10:05:05.000Z",
      "authors": [],
      "tags": [
        "高性能"
      ]
    },
    {
      "title": "面试太紧张怎么办？",
      "url": "https://javaguide.cn/interview-preparation/how-to-handle-interview-nerves.html",
      "id": "https://javaguide.cn/interview-preparation/how-to-handle-interview-nerves.html",
      "summary": "面试太紧张影响发挥怎么办？从心态调整、提前准备到模拟面试与表达训练，提供一套可落地的方法，帮助你降低焦虑、提升临场表现，更稳定地通过技术面试。",
      "content_html": "<p><a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/xingqiu.png\" alt=\"JavaGuide官方知识星球\"></a></p>\n<p>很多小伙伴在第一次技术面试时都会感到紧张甚至害怕，遇到稍微刁钻的问题大脑就一片空白，面试结束后还会有种“懵懵的”感觉。我也经历过类似的状况，对这种手心出汗、语无伦次的窘境深有体会。</p>\n<p>其实，<strong>紧张是非常正常的生理和心理反应</strong>——它代表你对这次机会的重视，也源于人类对未知结果的天然担忧。但如果任由过度紧张蔓延，绝对会大幅折损你的临场发挥水平。</p>\n<p>下面，我将结合自己的实战经验，从<strong>心态重塑、战术准备、临场应对、面后复盘</strong>四个维度，分享一套可落地的“抗紧张”指南。</p>\n<h2>试着接受紧张情绪，调整心态</h2>\n<p>首先要明白，紧张是正常情绪，特别是初次或前几次面试时，多少都会有点忐忑。不要过分排斥这种情绪，可以适当地“拥抱”它：</p>\n<ul>\n<li><strong>搞清楚面试的本质</strong>：面试本质上是一场与面试官的深入交流，是一个双向选择的过程。面试失败并不意味着你的价值和努力被否定，而可能只是因为你与目标岗位暂时不匹配，或者仅仅是一次 KPI 面试，这家公司可能压根就没有真正的招聘需求。失败的原因也可能是某些知识点、项目经验或表达方式未能充分展现出你的能力。即便这次面试未通过，也不妨碍你继续尝试其他公司，完全不慌！</li>\n<li><strong>不要害怕面试官</strong>：很多求职者平时和同学朋友交流沟通的蛮好，一到面试就害怕了。面试官和求职者双方是平等的，以后说不定就是同事关系。也不要觉得面试官就很厉害，实际上，面试官的水平也参差不齐。他们提出的问题，可能自己也没有完全理解。</li>\n<li><strong>给自己积极的心理暗示</strong>：告诉自己“有点紧张没关系，这只能让我更专注，心跳加快是我在给自己打气，我一定可以回答的很好！”。</li>\n</ul>\n<h2>提前准备，减少不确定性</h2>\n<p><strong>不确定性越多，越容易紧张。</strong> 如果你能够在面试前做充分的准备，很多“未知”就会消失，紧张情绪自然会减轻很多。</p>\n<h3>认真准备技术面试</h3>\n<ul>\n<li><strong>优先梳理核心知识点</strong>：比如计算基础、数据库、Java 基础、Java 集合、并发编程、SpringBoot（这里以 Java 后端方向为例）等。如果时间不够，可以分轻重缓急，有重点地复习。如果你想要系统准备 Java 后端面试但又不知道如何开始的，可以参考 <a href=\"https://javaguide.cn/interview-preparation/backend-interview-plan.html\" target=\"_blank\" rel=\"noopener noreferrer\">Java 后端面试通关计划（后端通用）</a>。</li>\n<li><strong>精心准备项目经历</strong>：认真思考你简历上最重要的项目（面试以前两个项目为主，尤其是第一个），它们的技术难点、业务逻辑、架构设计，以及可能被面试官深挖的点。把你的思考总结成可能出现的面试问题，并尝试回答。</li>\n</ul>\n<h3>模拟面试和自测</h3>\n<ul>\n<li><strong>约朋友或同学互相提问</strong>：以真实的面试场景来进行演练，并及时对回答进行诊断和反馈。</li>\n<li><strong>线上练习</strong>：直接利用 AI 来进行模拟面试即可，免费且高效。把自己的简历投喂给它，让它根据你的简历，尤其是项目经历生成面试问题。</li>\n<li><strong>面经</strong>：平时可以多看一些前辈整理的面经，尤其是目标岗位或目标公司的面经，总结高频考点和常见问题。</li>\n<li><strong>技术面试题自测</strong>：在 <a href=\"https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\" rel=\"noopener noreferrer\">《Java 面试指北》</a> 的 「技术面试题自测篇」 ，我总结了 Java 面试中最重要的知识点的最常见的面试题并按照面试提问的方式展现出来。其中，每一个问题都有提示和重要程度说明，非常适合用来自测。</li>\n</ul>\n<p><a href=\"https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\" rel=\"noopener noreferrer\">《Java 面试指北》</a> 的 「技术面试题自测篇」概览：</p>\n<p><img src=\"https://oss.javaguide.cn/javamianshizhibei/technical-interview-questions-self-test.png\" alt=\"技术面试题自测篇\"></p>\n<h3>多表达</h3>\n<p>平时要多说，多表达出来，不要只是在心里面想，不然真正面试的时候会发现想的和说的不太一样。</p>\n<p>我前面推荐的模拟面试和自测，有一部分原因就是为了能够多多表达。</p>\n<h3>多面试</h3>\n<ul>\n<li><strong>先小厂后大厂</strong>：可以先去一些规模较小或者对你来说压力没那么大的公司试试手，积累一些实战经验，增加一些信心；等熟悉了面试流程、能够更从容地回答问题后，再去挑战自己心仪的大厂或热门公司。</li>\n<li><strong>积累“失败经验”</strong>：不要怕被拒，有些时候被拒绝却能从中学到更多。多复盘，多思考到底是哪个环节出了问题，再用更好的状态迎接下一次面试。</li>\n</ul>\n<h3>保证休息</h3>\n<ul>\n<li><strong>留出充裕时间</strong>：面试前尽量不要排太多事情，保证自己能有个好状态去参加面试。</li>\n<li><strong>保证休息</strong>：充足睡眠有助于情绪稳定，也能让你在面试时更清晰地思考问题。</li>\n</ul>\n<h2>遇到不会的问题不要慌</h2>\n<p>一场面试，不太可能面试官提的每一个问题你都能轻松应对，除非这场面试非常简单。</p>\n<p>在面试过程中，遇到不会的问题，首先要做的是快速回顾自己过往的知识，看是否能找到突破口。如果实在没有思路的话，可以真诚地向面试要一些提示比如谈谈你对这个问题的理解以及困惑点。一定不要觉得向面试官要提示很可耻，只要沟通没问题，这其实是很正常的。最怕的就是自己不会，还乱回答一通，这样会让面试官觉得你技术态度有问题。</p>\n<h2>面试结束后的复盘</h2>\n<p>很多人关注面试前的准备，却忽略了面试后的复盘，这一步真的非常非常非常重要：</p>\n<ol>\n<li><strong>记录面试中的问题</strong>：无论回答得好坏，都把它们写下来。如果问到了一些没想过的问题，可以认真思考并在面试后补上答案。</li>\n<li><strong>反思自己的表现</strong>：有没有遇到卡壳的地方？是知识没准备到还是过于紧张导致表达混乱？下次如何改进？</li>\n<li><strong>持续完善自己的“面试题库”</strong>：把新的问题补充进去，不断拓展自己的知识面，也逐步降低对未知问题的恐惧感。</li>\n</ol>\n",
      "image": "https://oss.javaguide.cn/javamianshizhibei/technical-interview-questions-self-test.png",
      "date_published": "2023-04-28T11:27:56.000Z",
      "date_modified": "2026-03-04T08:41:37.000Z",
      "authors": [],
      "tags": [
        "面试准备"
      ]
    },
    {
      "title": "校招没有实习经历怎么办？实习经历怎么写？",
      "url": "https://javaguide.cn/interview-preparation/internship-experience.html",
      "id": "https://javaguide.cn/interview-preparation/internship-experience.html",
      "summary": "校招没有实习经历也能上岸：从补强项目经验、持续优化简历到系统准备技术面试，给出可执行的提升路径与注意事项，帮助你在没有大厂实习的情况下提高面试通过率。",
      "content_html": "<p><a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/xingqiu.png\" alt=\"JavaGuide官方知识星球\"></a></p>\n<p>由于目前的面试太卷，对于犹豫是否要找实习的同学来说，个人建议不论是本科生还是研究生都应该在参加校招面试之前，争取一下不错的实习机会，尤其是大厂的实习机会，日常实习或者暑期实习都可以。当然，如果大厂实习面不上，中小厂实习也是可以接受的。</p>\n<p>不过，现在的实习是真难找，这两年有非常多的同学没有找到实习，有一部分甚至是 211/985 名校的同学。实习难找是一方面原因，国内很多学校的导师压根不放实习，这也是很棘手的问题。</p>\n<h2>没有实习经历怎么办？</h2>\n<p>如果实在是找不到合适的实习的话，那也没办法，我们应该多花时间去把下面这三件事情给做好：</p>\n<ol>\n<li>补强项目经历</li>\n<li>持续完善简历</li>\n<li>准备技术面试</li>\n</ol>\n<h3>补强项目经历</h3>\n<p>校招没有实习经历的话，找工作比较吃亏（没办法，太卷了），需要在项目经历部分多发力弥补一下。</p>\n<p>建议你尽全力地去补强自己的项目经历，完善现有的项目或者去做更有亮点的项目，尽可能地通过项目经历去弥补一些。</p>\n<p>你面试中的重点就是你的项目经历涉及到的知识点，如果你的项目经历比较简单的话，面试官直接不知道问啥了。另外，你的项目经历中不涉及的知识点，但在技能介绍中提到的知识点也很大概率会被问到。像 Redis 这种基本是面试 Java 后端岗位必备的技能，我觉得大部分面试官应该都会问。</p>\n<p>推荐阅读一下网站的这篇文章：<a href=\"https://javaguide.cn/interview-preparation/project-experience-guide.html\" target=\"_blank\" rel=\"noopener noreferrer\">项目经验指南</a>。</p>\n<h3>完善简历</h3>\n<p>一定一定一定要重视简历啊！建议至少花 2~3 天时间来专门完善自己的简历。并且，后续还要持续完善。</p>\n<p>对于面试官来说，筛选简历的时候会比较看重下面这些维度：</p>\n<ol>\n<li><strong>实习/工作经历</strong>：看你是否有不错的实习经历，大厂且与面试岗位相关的实习/工作经历最佳。</li>\n<li><strong>获奖经历</strong>：如果有含金量比较高（知名度较高的赛事比如 ACM、阿里云天池）的获奖经历的话，也是加分点，尤其是对于校招来说，这类求职者属于是很多大厂争抢的对象（但不是说获奖了就能进大厂，还是要面试表现还可以）。对于社招来说，获奖经历作用相对较小，通常会更看重过往的工作经历和项目经验。</li>\n<li><strong>项目经验</strong>：项目经验对于面试来说非常重要，面试官会重点关注，同时也是有水平的面试提问的重点。</li>\n<li><strong>技能匹配度</strong>：看你的技能是否满足岗位的需求。在投递简历之前，一定要确认一下自己的技能介绍中是否缺少一些你要投递的对应岗位的技能要求。</li>\n<li><strong>学历</strong>：相对其他行业来说，程序员求职面试对于学历的包容度还是比较高的，只要你在其他方面有过人之出的话，也是可以弥补一下学历的缺陷的。你要知道，很多行业比如律师、金融，学历就是敲门砖，学历没达到要求，直接面试机会都没有。不过，由于现在面试越来越卷，一些大厂、国企和研究所也开始卡学历了，很多岗位都要求 211/985，甚至必须需要硕士学历。总之，学历很难改变，学校较差的话，就投递那些对学历没有明确要求的公司即可，努力提升自己的其他方面的硬实力。</li>\n</ol>\n<p>对于大部分求职者来说，实习/工作经历、项目经验、技能匹配度更重要一些。不过，不排除一些公司会因为学历卡人。</p>\n<p>详细的程序员简历编写指南可以参考这篇文章：<a href=\"https://javaguide.cn/interview-preparation/resume-guide.html\" target=\"_blank\" rel=\"noopener noreferrer\">程序员简历编写指南(重要)</a>。</p>\n<h3>准备技术面试</h3>\n<p>面试之前一定要提前准备一下常见的面试题也就是八股文：</p>\n<ul>\n<li>自己面试中可能涉及哪些知识点、那些知识点是重点。</li>\n<li>面试中哪些问题会被经常问到、面试中自己该如何回答。(强烈不推荐死记硬背，第一：通过背这种方式你能记住多少？能记住多久？第二：背题的方式的学习很难坚持下去！)</li>\n</ul>\n<p>不同类型的公司对于技能的要求侧重点是不同的比如腾讯、字节可能更重视计算机基础比如网络、操作系统这方面的内容。阿里、美团这种可能更重视你的项目经历、实战能力。</p>\n<p>一定不要抱着一种思想，觉得八股文或者基础问题的考查意义不大。如果你抱着这种思想复习的话，那效果可能不会太好。实际上，个人认为还是很有意义的，八股文或者基础性的知识在日常开发中也会需要经常用到。例如，线程池这块的拒绝策略、核心参数配置什么的，如果你不了解，实际项目中使用线程池可能就用的不是很明白，容易出现问题。而且，其实这种基础性的问题是最容易准备的，像各种底层原理、系统设计、场景题以及深挖你的项目这类才是最难的！</p>\n<p>八股文资料首推我的 <a href=\"https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\" rel=\"noopener noreferrer\">《Java 面试指北》</a> 和 <a href=\"https://javaguide.cn/home.html\" target=\"_blank\" rel=\"noopener noreferrer\">JavaGuide</a> 。里面不仅仅是原创八股文，还有很多对实际开发有帮助的干货。除了我的资料之外，你还可以去网上找一些其他的优质的文章、视频来看。</p>\n<p>如果你想要系统准备 Java 后端面试但又不知道如何开始的，可以参考 <a href=\"https://javaguide.cn/interview-preparation/backend-interview-plan.html\" target=\"_blank\" rel=\"noopener noreferrer\">Java 后端面试通关计划（后端通用）</a>。</p>\n<h2>实习经历在简历上一般怎么写比较出彩？</h2>\n<p>实习经历的描述一定要避免空谈，尽量列举出你在实习期间取得的成就和具体贡献，使用具体的数据和指标来量化你的工作成果。</p>\n<p>示例（这里假设项目细节放在实习经历这里介绍，你也可以选择将实习经历参与的项目放到项目经历中）：</p>\n<ol>\n<li>负责订单模块核心流程开发，实现订单状态的精确流转，并保障与库存、支付等模块的数据一致性。</li>\n<li>负责行为风控黑名单看板的开发，支持查看拉黑用户、批量拉黑以及取消拉黑。</li>\n<li>基于 Redisson + AOP 封装限流组件，实现对核心接口（如付费、课程搜索）的限流，有效防止恶意请求冲击。</li>\n<li>优化用户统计模块性能，利用 CompletableFuture 并行加载多维度数据（如用户增长、课程活跃度），，平均相应时间从 3.5s 降低到 1s。</li>\n<li>封装通用数据脱敏组件，通过自定义 Jackson 注解实现对手机号、邮箱等敏感信息的自动、无侵入式脱敏。</li>\n<li>优化文件上传模块，基于 MinIO 实现了文件的分片上传、断点续传以及极速秒传功能。</li>\n<li>排查并解决扣费模块由于扣费父任务和反作弊子任务使用同一个线程池导致的死锁问题，通过线程池隔离策略根除该隐患。</li>\n<li>实习期间独立负责 7 个功能需求与 3 个线上问题修复，代码均一次性通过评审与测试。</li>\n</ol>\n<p>下面是<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">星球</a>一位球友分享的实习经历介绍，整体写的还是非常不错的：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/interview-preparation/qiuyou-shixijingli-demo.png\" alt=\"实习经历模板\"></p>\n<p>📌关于实习经历这块再多提一点：很多同学实习期间可能接触不到什么实际的开发任务，大部分时间可能都是在熟悉和维护项目。</p>\n<p>对于这种情况，应对思路是一套组合拳：首先，你肯定是要和 mentor 沟通继续争取做一些有价值的工作，这样你的实习经历才更有价值，简历上自然就能够有东西可写。记得找一个 mentor 不那么忙的时候沟通，放低姿态，真诚一些，表明自己现有的工作已经认真完成，想要承担更多责任的意愿。其次，不管是否能够争取到这种机会，你都要自己有意识地寻找项目中适合自己研究的功能点（比如同组其他实习生干的活），进行深度挖掘。重点关注以下几个方面：</p>\n<ol>\n<li><strong>这个功能是干嘛的？</strong> 它解决了什么业务痛点？给哪个业务方用的？整个流程是怎样的？</li>\n<li><strong>它是怎么实现的？</strong> 用了哪些关键技术、框架或者设计模式？核心代码的逻辑是怎样的？</li>\n<li><strong>为什么要这么设计？</strong> 当初设计的时候有没有别的方案？现在这个方案好在哪，又有什么潜在的坑？如果让你来做，你会怎么设计？</li>\n</ol>\n<p>只要你把具体的功能点彻底搞懂，那就可以在简历上合理包装成自己的成果。除了功能点开发之外，也可以包装一些合适的问题排查解决经历，这样能够体现你解决问题的能力。 面试时也不用太担心自己“露馅”，只要你选择的内容不属于那些显然不会交给实习生完成的高难度任务，并且能清晰地讲明白，就不会有问题。</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/interview-preparation/qiuyou-shixijingli-demo.png",
      "date_published": "2023-04-28T11:27:56.000Z",
      "date_modified": "2026-03-02T15:06:39.000Z",
      "authors": [],
      "tags": [
        "面试准备"
      ]
    },
    {
      "title": "Java 学习路线(最新版，4w+字)",
      "url": "https://javaguide.cn/interview-preparation/java-roadmap.html",
      "id": "https://javaguide.cn/interview-preparation/java-roadmap.html",
      "summary": "Java学习路线最新版：结合当下 Java 后端招聘要求，提供从基础到进阶的系统学习路径与资料建议，覆盖Java核心、数据库、缓存、中间件、框架与面试重点，帮助高效规划与提速上岸。",
      "content_html": "<p><a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/xingqiu.png\" alt=\"JavaGuide官方知识星球\"></a></p>\n<div class=\"hint-container tip\">\n<p class=\"hint-container-title\">重要说明</p>\n<p>本学习路线保持<strong>年度系统性修订</strong>，严格同步 Java 技术生态与招聘市场的最新动态，<strong>确保内容时效性与前瞻性</strong>。</p>\n</div>\n<p>历时一个月精心打磨，笔者基于当下 Java 后端开发岗位招聘的最新要求，对既有学习路线进行了全面升级。本次升级涵盖技术栈增删、学习路径优化、配套学习资源更新等维度，力争构建出更符合 Java 开发者成长曲线的知识体系。</p>\n<p>亮色板概览：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/interview-preparation/java-road-map-pdf.png\" alt=\"Java 学习路线 PDF 概览 - 亮色板\"></p>\n<p>暗色板概览：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/interview-preparation/java-road-map-pdf-dark.png\" alt=\"Java 学习路线 PDF 概览 - 暗色版\"></p>\n<p>这可能是你见过的最用心、最全面的 Java 后端学习路线。这份学习路线共包含 <strong>4w+</strong> 字，但你完全不用担心内容过多而学不完。我会根据学习难度，划分出适合找小厂工作必学的内容，以及适合逐步提升 Java 后端开发能力的学习路径。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/interview-preparation/java-road-map.png\" alt=\"Java 学习路线图\"></p>\n<p>对于初学者，你可以按照这篇文章推荐的学习路线和资料进行系统性的学习；对于有经验的开发者，你可以根据这篇文章更一步地深入学习 Java 后端开发，提升个人竞争力。</p>\n<p>在看这份学习路线的过程中，建议搭配 <a href=\"https://javaguide.cn/interview-preparation/key-points-of-interview.html\" target=\"_blank\" rel=\"noopener noreferrer\">Java 面试重点总结(重要)</a>，可以让你在学习过程中更有目的性。</p>\n<p>由于这份学习路线内容太多，因此我将其整理成了 PDF 版本（共 <strong>55</strong> 页），方便大家阅读。这份 PDF 有黑夜和白天两种阅读版本，满足大家的不同需求。</p>\n<p>这份学习路线的获取方法很简单：直接在公众号「<strong>JavaGuide</strong>」后台回复“<strong>路线</strong>”即可获取。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png\" alt=\"JavaGuide 官方公众号\"></p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/interview-preparation/java-road-map-pdf.png",
      "date_published": "2023-04-28T11:27:56.000Z",
      "date_modified": "2026-01-17T03:43:57.000Z",
      "authors": [],
      "tags": [
        "面试准备"
      ]
    },
    {
      "title": "《后端面试高频系统设计&场景题》",
      "url": "https://javaguide.cn/zhuanlan/back-end-interview-high-frequency-system-design-and-scenario-questions.html",
      "id": "https://javaguide.cn/zhuanlan/back-end-interview-high-frequency-system-design-and-scenario-questions.html",
      "summary": "后端面试高频系统设计与场景题专栏，涵盖秒杀系统、短链系统、海量数据处理等30+道经典面试题解析。",
      "content_html": "<h2>介绍</h2>\n<p><strong>《后端面试高频系统设计&amp;场景题》</strong> 是我的<a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\">知识星球</a>的一个内部小册，系统性地总结了后端面试中高频出现的系统设计案例和场景题。</p>\n<h3>为什么你需要这份小册？</h3>\n<p>近年来，国内技术面试&quot;越来越卷&quot;。越来越多的公司（阿里、美团、字节、腾讯等）开始在面试中考察 <strong>系统设计</strong> 和 <strong>场景问题</strong>，以此来更全面地考察求职者的综合能力——不论是校招还是社招。</p>\n<blockquote>\n<p>很多同学八股文背得滚瓜烂熟，但一遇到&quot;如何设计一个秒杀系统？&quot;这类开放性问题就懵了。</p>\n</blockquote>\n<p><strong>系统设计和场景题的考察特点</strong>：</p>\n<ul>\n<li>✅ 没有标准答案，重点考察思维过程和架构能力</li>\n<li>✅ 考察对高并发、高可用、分布式等技术的综合运用</li>\n<li>✅ 考察解决实际问题的能力和工程经验</li>\n<li>⚠️ 正常面试不会全是场景题，一般会穿插 1-2 道来考察你</li>\n</ul>\n<p>于是，<strong>《后端面试高频系统设计&amp;场景题》</strong> 小册就诞生了！</p>\n<h3>这份小册能带给你什么？</h3>\n<p><strong>1. 面试加分项</strong></p>\n<p>系统设计和场景题回答得好，面试官会对你印象非常好！这类问题稍微准备就能脱颖而出。</p>\n<p><strong>2. 提升系统设计思维</strong></p>\n<p>即使不是准备面试，这份小册也能帮助你建立系统设计的思维框架，提升解决实际问题的能力。</p>\n<p><strong>3. 实战落地参考</strong></p>\n<p>涉及到的很多案例都可以直接用到自己的项目上，比如：</p>\n<ul>\n<li>第三方授权登录（微信/QQ 登录）</li>\n<li>Redis 实现延时任务的正确方式</li>\n<li>动态线程池的设计与实现</li>\n<li>分布式锁的多种实现方案</li>\n</ul>\n<h2>内容概览</h2>\n<h3>📐 系统设计案例</h3>\n<p>| 主题                                   | 核心知识点                                         |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/xingqiu/back-end-interview-high-frequency-system-design-and-scenario-questions-fengmian.png",
      "date_published": "2023-04-28T09:48:34.000Z",
      "date_modified": "2026-03-12T08:34:17.000Z",
      "authors": [],
      "tags": [
        "知识星球"
      ]
    },
    {
      "title": "PriorityQueue 源码分析（付费）",
      "url": "https://javaguide.cn/java/collection/priorityqueue-source-code.html",
      "id": "https://javaguide.cn/java/collection/priorityqueue-source-code.html",
      "summary": "PriorityQueue源码深度解析：详解基于二叉堆的优先队列实现、堆化siftUp/siftDown操作、Comparator自定义排序、动态扩容机制。",
      "content_html": "<p><strong>PriorityQueue 源码分析</strong> 为我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>（点击链接即可查看详细介绍以及加入方法）专属内容，已经整理到了<a href=\"https://javaguide.cn/zhuanlan/source-code-reading.html\" target=\"_blank\" rel=\"noopener noreferrer\">《Java 必读源码系列》</a>中。</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/image-20230727084055593.png\" alt=\"PriorityQueue 源码分析\"></p>\n<p><a href=\"/zhuanlan/source-code-reading.html\" target=\"_blank\">《Java 必读源码系列》</a>（点击链接即可查看详细介绍）的部分内容展示如下。</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/image-20220621091832348.png\" alt=\"《Java 必读源码系列》\"></p>\n<p>为了帮助更多同学准备 Java 面试以及学习 Java ，我创建了一个纯粹的<a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\">Java 面试知识星球</a>。虽然收费只有培训班/训练营的百分之一，但是知识星球里的内容质量更高，提供的服务也更全面，非常适合准备 Java 面试和学习 Java 的同学。</p>\n<p><strong>欢迎准备 Java 面试以及学习 Java 的同学加入我的 <a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\">知识星球</a>，干货非常多，学习氛围也很不错！收费虽然是白菜价，但星球里的内容或许比你参加上万的培训班质量还要高。</strong></p>\n<p>下面是星球提供的部分服务（点击下方图片即可获取知识星球的详细介绍）：</p>\n<p><a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/xingqiufuwu.png\" alt=\"星球服务\"></a></p>\n<p><strong>我有自己的原则，不割韭菜，用心做内容，真心希望帮助到你！</strong></p>\n<p>如果你感兴趣的话，不妨花 3 分钟左右看看星球的详细介绍：<a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\">JavaGuide 知识星球详细介绍</a> 。</p>\n<p>这里再送一个 <strong>30</strong> 元的星球专属优惠券，数量有限（价格即将上调。老用户续费半价 ，微信扫码即可续费）！</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuan-30.jpg\" alt=\"知识星球30元优惠卷\"></p>\n<p>进入星球之后，记得查看 <strong><a href=\"https://t.zsxq.com/0d18KSarv\" target=\"_blank\" rel=\"noopener noreferrer\">星球使用指南</a></strong> （一定要看！！！） 和 <strong><a href=\"https://t.zsxq.com/12uSKgTIm\" target=\"_blank\" rel=\"noopener noreferrer\">星球优质主题汇总</a></strong> 。</p>\n<p><strong>无任何套路，无任何潜在收费项。用心做内容，不割韭菜！</strong></p>\n<p>不过， <strong>一定要确定需要再进</strong> 。并且， <strong>三天之内觉得内容不满意可以全额退款</strong> 。</p>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/xingqiu/image-20230727084055593.png",
      "date_published": "2023-04-28T09:48:34.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "Spring Boot核心源码解读（付费）",
      "url": "https://javaguide.cn/system-design/framework/spring/springboot-source-code.html",
      "id": "https://javaguide.cn/system-design/framework/spring/springboot-source-code.html",
      "summary": "Spring Boot核心源码深度解读，涵盖启动流程、自动配置机制、条件注解及SpringApplication源码分析。",
      "content_html": "<p><strong>Spring Boot 核心源码解读</strong> 为我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>（点击链接即可查看详细介绍以及加入方法）专属内容，已经整理到了<a href=\"https://javaguide.cn/zhuanlan/source-code-reading.html\" target=\"_blank\" rel=\"noopener noreferrer\">《Java 必读源码系列》</a>中。</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/springboot-source-code.png\" alt=\"Spring Boot核心源码解读\"></p>\n<p><a href=\"/zhuanlan/source-code-reading.html\" target=\"_blank\">《Java 必读源码系列》</a>（点击链接即可查看详细介绍）的部分内容展示如下。</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/image-20220621091832348.png\" alt=\"《Java 必读源码系列》\"></p>\n<p>为了帮助更多同学准备 Java 面试以及学习 Java ，我创建了一个纯粹的<a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\">Java 面试知识星球</a>。虽然收费只有培训班/训练营的百分之一，但是知识星球里的内容质量更高，提供的服务也更全面，非常适合准备 Java 面试和学习 Java 的同学。</p>\n<p><strong>欢迎准备 Java 面试以及学习 Java 的同学加入我的 <a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\">知识星球</a>，干货非常多，学习氛围也很不错！收费虽然是白菜价，但星球里的内容或许比你参加上万的培训班质量还要高。</strong></p>\n<p>下面是星球提供的部分服务（点击下方图片即可获取知识星球的详细介绍）：</p>\n<p><a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/xingqiufuwu.png\" alt=\"星球服务\"></a></p>\n<p><strong>我有自己的原则，不割韭菜，用心做内容，真心希望帮助到你！</strong></p>\n<p>如果你感兴趣的话，不妨花 3 分钟左右看看星球的详细介绍：<a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\">JavaGuide 知识星球详细介绍</a> 。</p>\n<p>这里再送一个 <strong>30</strong> 元的星球专属优惠券，数量有限（价格即将上调。老用户续费半价 ，微信扫码即可续费）！</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuan-30.jpg\" alt=\"知识星球30元优惠卷\"></p>\n<p>进入星球之后，记得查看 <strong><a href=\"https://t.zsxq.com/0d18KSarv\" target=\"_blank\" rel=\"noopener noreferrer\">星球使用指南</a></strong> （一定要看！！！） 和 <strong><a href=\"https://t.zsxq.com/12uSKgTIm\" target=\"_blank\" rel=\"noopener noreferrer\">星球优质主题汇总</a></strong> 。</p>\n<p><strong>无任何套路，无任何潜在收费项。用心做内容，不割韭菜！</strong></p>\n<p>不过， <strong>一定要确定需要再进</strong> 。并且， <strong>三天之内觉得内容不满意可以全额退款</strong> 。</p>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/xingqiu/springboot-source-code.png",
      "date_published": "2023-04-28T09:48:34.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [],
      "tags": [
        "框架"
      ]
    },
    {
      "title": "Java 后端面试通关计划（涵盖后端通用体系）",
      "url": "https://javaguide.cn/interview-preparation/backend-interview-plan.html",
      "id": "https://javaguide.cn/interview-preparation/backend-interview-plan.html",
      "summary": "Java 后端面试通关计划：严格按照面试考察真实优先级编排，涵盖项目经历、Java核心、MySQL/Redis、框架、系统设计、计算机基础、分布式与JVM，适合校招/社招准备。",
      "content_html": "<p>本计划严格按照面试考察的<strong>真实优先级</strong>进行编排，顺序为：<br>\n<strong>「 项目经历与简历深挖 → Java核心/MySQL/Redis → 框架应用 → 系统设计与场景题 → 计算机基础 → 分布式/高并发 → JVM」</strong></p>\n<p>每一阶段都对应了本站具体的精选文章，方便你按图索骥，逐个击破。</p>\n<ul>\n<li><strong>建议总周期</strong>：4～8 周（请根据目标公司是中小厂还是大厂，以及自身的脱产时间灵活压缩或拉长）。</li>\n<li><strong>适用人群</strong>：准备秋招/春招的计算机专业学生，以及 0-5 年经验准备跳槽的 Java 开发者。</li>\n<li><strong>面试突击</strong>：下文中推荐的技术文章以 <a href=\"https://javaguide.cn/\" target=\"_blank\" rel=\"noopener noreferrer\">JavaGuide</a> 为主，非常全面且详细，如果突击面试，可以选择阅读 <a href=\"https://interview.javaguide.cn/\" target=\"_blank\" rel=\"noopener noreferrer\">JavaGuide 面试突击版</a> 中对应的文章。</li>\n</ul>\n<h3>计划总览</h3>\n<p>| 阶段                               | 建议时长              | 核心产出                                       | 自测标准                                                                      |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/xingqiu/back-end-interview-high-frequency-system-design-and-scenario-questions-fengmian.png",
      "date_published": "2023-04-22T02:34:42.000Z",
      "date_modified": "2026-03-11T02:51:59.000Z",
      "authors": [],
      "tags": [
        "面试准备"
      ]
    },
    {
      "title": "Java后端面试重点总结",
      "url": "https://javaguide.cn/interview-preparation/key-points-of-interview.html",
      "id": "https://javaguide.cn/interview-preparation/key-points-of-interview.html",
      "summary": "Java后端面试重点总结：梳理校招/社招高频考点与复习优先级，覆盖Java基础、集合、并发、MySQL、Redis、Spring/Spring Boot、JVM与项目经验准备，帮你抓重点高效备战。",
      "content_html": "<p><a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/xingqiu.png\" alt=\"JavaGuide官方知识星球\"></a></p>\n<div class=\"hint-container tip\">\n<p class=\"hint-container-title\">友情提示</p>\n<p>本文节选自 <strong><a href=\"/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\">《Java 面试指北》</a></strong>。这是一份教你如何更高效地准备面试的专栏，内容和 JavaGuide 互补，涵盖常见八股文（系统设计、常见框架、分布式、高并发 ……）、优质面经等内容。</p>\n</div>\n<h2>Java 后端面试哪些知识点是重点？</h2>\n<p><strong>准备面试的时候，具体哪些知识点是重点呢？如何把握重点？</strong></p>\n<p>先看下面这张全局图（后续会详细解读）：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/interview-preparation/back-end-interview-focus.png\" alt=\"Java 后端面试重点\"></p>\n<p>给你几点靠谱的建议：</p>\n<ol>\n<li>Java 基础、集合、并发、MySQL、Redis 、Spring、Spring Boot 这些 Java 后端开发必备的知识点（MySQL + Redis &gt;= Java &gt; Spring + Spring Boot）。大厂以及中小厂的面试问的比较多的就是这些知识点。Spring 和 Spring Boot 这俩框架类的知识点相对前面的知识点来说重要性要稍低一些，但一般面试也会问一些，尤其是中小厂。并发知识一般中大厂提问更多也更难，尤其是大厂喜欢深挖底层，很容易把人问倒。计算机基础相关的内容会在下面提到。</li>\n<li>你的项目经历涉及到的知识点是重中之重，有水平的面试官都是会根据你的项目经历来问的。举个例子，你的项目经历使用了 Redis 来做限流，那 Redis 相关的八股文（比如 Redis 常见数据结构）以及限流相关的八股文（比如常见的限流算法）你就应该多花更多心思来搞懂吃透！你把项目经历上的知识点吃透之后，再把你简历上哪些写熟练掌握的技术给吃透，最后再去花时间准备其他知识点。</li>\n<li>针对自身找工作的需求，你又可以适当地调整复习的重点。像中小厂一般问计算机基础比较少一些，有些大厂比如字节比较重视计算机基础尤其是算法。这样的话，如果你的目标是中小厂的话，计算机基础就准备面试来说不是那么重要了。如果复习时间不够的话，可以暂时先放放，腾出时间给其他重要的知识点。</li>\n<li>一般校招的面试不会强制要求你会分布式/微服务、高并发的知识（不排除个别岗位有这方面的硬性要求），所以到底要不要掌握还是要看你个人当前的实际情况。如果你会这方面的知识的话，对面试相对来说还是会更有利一些（想要让项目经历有亮点，还是得会一些性能优化的知识。性能优化的知识这也算是高并发知识的一个小分支了）。如果你的技能介绍或者项目经历涉及到分布式/微服务、高并发的知识，那建议你尽量也要抽时间去认真准备一下，面试中很可能会被问到，尤其是项目经历用到的时候。不过，也还是主要准备写在简历上的那些知识点就好。</li>\n<li>JVM 相关的知识点，一般是大厂（例如美团、阿里）和一些不错的中厂（例如携程、顺丰、招银网络）才会问到，面试国企、差一点的中厂和小厂就没必要准备了。JVM 面试中比较常问的是 <a href=\"https://javaguide.cn/java/jvm/memory-area.html\" target=\"_blank\" rel=\"noopener noreferrer\">Java 内存区域</a>、<a href=\"https://javaguide.cn/java/jvm/jvm-garbage-collection.html\" target=\"_blank\" rel=\"noopener noreferrer\">JVM 垃圾回收</a>、<a href=\"https://javaguide.cn/java/jvm/classloader.html\" target=\"_blank\" rel=\"noopener noreferrer\">类加载器和双亲委派模型</a> 以及 JVM 调优和问题排查（我之前分享过一些<a href=\"https://t.zsxq.com/0bsAac47U\" target=\"_blank\" rel=\"noopener noreferrer\">常见的线上问题案例</a>，里面就有 JVM 相关的）。</li>\n<li>不同的大厂面试侧重点也会不同。比如说你要去阿里这种公司的话，项目和八股文就是重点，阿里笔试一般会有代码题，进入面试后就很少问代码题了，但是对原理性的问题问的比较深，经常会问一些你对技术的思考。再比如说你要面试字节这种公司，那计算机基础，尤其是算法是重点，字节的面试十分注重代码功底，有时候开始面试就会直接甩给你一道代码题，写出来再谈别的。也会问面试八股文，以及项目，不过，相对来说要少很多。</li>\n<li>多去找一些面经看看，尤其你目标公司或者类似公司对应岗位的面经。这样可以实现针对性的复习，还能顺便自测一波，检查一下自己的掌握情况。</li>\n</ol>\n<p>看似 Java 后端八股文很多，实际把复习范围一缩小，重要的东西就是那些。考虑到时间问题，你不可能连一些比较冷门的知识点也给准备了。这没必要，主要精力先放在那些重要的知识点即可。</p>\n<h2>如何更高效地准备八股文？</h2>\n<img src=\"https://oss.javaguide.cn/github/javaguide/interview-preparation/preparation-for eight-part essay.png\" style=\"zoom:50%;\">\n<p>对于技术八股文来说，尽量不要死记硬背，这种方式非常枯燥且对自身能力提升有限！但是！想要一点不背是不太现实的，只是说要结合实际应用场景和实战来理解记忆。</p>\n<p>我一直觉得面试八股文最好是和实际应用场景和实战相结合。很多同学现在的方向都错了，上来就是直接背八股文，硬生生学成了文科，那当然无趣了。</p>\n<p>举个例子：你的项目中需要用到 Redis 来做缓存，你对照着官网简单了解并实践了简单使用 Redis 之后，你去看了 Redis 对应的八股文。你发现 Redis 可以用来做限流、分布式锁，于是你去在项目中实践了一下并掌握了对应的八股文。紧接着，你又发现 Redis 内存不够用的情况下，还能使用 Redis Cluster 来解决，于是你就又去实践了一下并掌握了对应的八股文。</p>\n<p><strong>一定要记住你的主要目标是理解和记关键词，而不是像背课文一样一字一句地记下来，这样毫无意义！效率最低，对自身帮助也最小！</strong></p>\n<p>还要注意适当“投机取巧”，不要单纯死记八股，有些技术方案的实现有很多种，例如分布式 ID、分布式锁、幂等设计，想要完全记住所有方案不太现实，你就重点记忆你项目的实现方案以及选择该种实现方案的原因就好了。当然，其他方案还是建议你简单了解一下，不然也没办法和你选择的方案进行对比。</p>\n<p>想要检测自己是否搞懂或者加深印象，记录博客或者用自己的理解把对应的知识点讲给别人听也是一个不错的选择。</p>\n<p>另外，准备八股文的过程中，强烈建议你花个几个小时去根据你的简历（主要是项目经历部分）思考一下哪些地方可能被深挖，然后把你自己的思考以面试问题的形式体现出来。面试之后，你还要根据当下的面试情况复盘一波，对之前自己整理的面试问题进行完善补充。这个过程对于个人进一步熟悉自己的简历（尤其是项目经历）部分，非常非常有用。这些问题你也一定要多花一些时间搞懂吃透，能够流畅地表达出来。面试问题可以参考 <a href=\"https://t.zsxq.com/0eRq7EJPy\" target=\"_blank\" rel=\"noopener noreferrer\">Java 面试常见问题总结（2024 最新版）</a>，记得根据自己项目经历去深入拓展即可！</p>\n<p>最后，准备技术面试的同学一定要定期复习（自测的方式非常好），不然确实会遗忘的。</p>\n<h2>详细面试准备计划（后端通用）</h2>\n<p><a href=\"https://javaguide.cn/interview-preparation/backend-interview-plan.html\" target=\"_blank\" rel=\"noopener noreferrer\">Java 后端面试重点和详细准备计划</a></p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/interview-preparation/back-end-interview-focus.png",
      "date_published": "2023-04-22T02:34:42.000Z",
      "date_modified": "2026-03-08T05:13:15.000Z",
      "authors": [],
      "tags": [
        "面试准备"
      ]
    },
    {
      "title": "计算机网络常见面试题总结(下)",
      "url": "https://javaguide.cn/cs-basics/network/other-network-questions2.html",
      "id": "https://javaguide.cn/cs-basics/network/other-network-questions2.html",
      "summary": "最新计算机网络高频面试题总结（下）：TCP/UDP深度对比、三次握手四次挥手、HTTP/3 QUIC优化、IPv6优势、NAT/ARP详解，附表格+⭐️重点标注，一文掌握传输层&网络层核心考点，快速通关后端技术面试！",
      "content_html": "<p><a href=\"/zhuanlan/interview-guide.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/interview-guide-banner.png\" alt=\"《SpringAI 智能面试平台+RAG 知识库》\"></a></p>\n<p>下篇主要是传输层和网络层相关的内容。</p>\n<h2>TCP 与 UDP</h2>\n<h3>⭐️TCP 与 UDP 的区别（重要）</h3>\n<ol>\n<li><strong>是否面向连接</strong>：\n<ul>\n<li>TCP 是面向连接的。在传输数据之前，必须先通过“三次握手”建立连接；数据传输完成后，还需要通过“四次挥手”来释放连接。这保证了双方都准备好通信。</li>\n<li>UDP 是无连接的。发送数据前不需要建立任何连接，直接把数据包（数据报）扔出去。</li>\n</ul>\n</li>\n<li><strong>是否是可靠传输</strong>：\n<ul>\n<li>TCP 提供可靠的数据传输服务。它通过序列号、确认应答 (ACK)、超时重传、流量控制、拥塞控制等一系列机制，来确保数据能够无差错、不丢失、不重复且按顺序地到达目的地。</li>\n<li>UDP 提供不可靠的传输。它尽最大努力交付 (best-effort delivery)，但不保证数据一定能到达，也不保证到达的顺序，更不会自动重传。收到报文后，接收方也不会主动发确认。</li>\n</ul>\n</li>\n<li><strong>是否有状态</strong>：\n<ul>\n<li>TCP 是有状态的。因为要保证可靠性，TCP 需要在连接的两端维护连接状态信息，比如序列号、窗口大小、哪些数据发出去了、哪些收到了确认等。</li>\n<li>UDP 是无状态的。它不维护连接状态，发送方发出数据后就不再关心它是否到达以及如何到达，因此开销更小（<strong>这很“渣男”！</strong>）。</li>\n</ul>\n</li>\n<li><strong>传输效率</strong>：\n<ul>\n<li>TCP 因为需要建立连接、发送确认、处理重传等，其开销较大，传输效率相对较低。</li>\n<li>UDP 结构简单，没有复杂的控制机制，开销小，传输效率更高，速度更快。</li>\n</ul>\n</li>\n<li><strong>传输形式</strong>：\n<ul>\n<li>TCP 是面向字节流 (Byte Stream) 的。它将应用程序交付的数据视为一连串无结构的字节流，可能会对数据进行拆分或合并。</li>\n<li>UDP 是面向报文 (Message Oriented) 的。应用程序交给 UDP 多大的数据块，UDP 就照样发送，既不拆分也不合并，保留了应用程序消息的边界。</li>\n</ul>\n</li>\n<li><strong>首部开销</strong>：\n<ul>\n<li>TCP 的头部至少需要 20 字节，如果包含选项字段，最多可达 60 字节。</li>\n<li>UDP 的头部非常简单，固定只有 8 字节。</li>\n</ul>\n</li>\n<li><strong>是否提供广播或多播服务</strong>：\n<ul>\n<li>TCP 只支持点对点 (Point-to-Point) 的单播通信。</li>\n<li>UDP 支持一对一 (单播)、一对多 (多播/Multicast) 和一对所有 (广播/Broadcast) 的通信方式。</li>\n</ul>\n</li>\n<li>……</li>\n</ol>\n<p>为了更直观地对比，可以看下面这个表格：</p>\n<p>| 特性         | TCP                        | UDP                                 |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/cs-basics/network/http-3-implementation.png",
      "date_published": "2023-04-13T11:00:22.000Z",
      "date_modified": "2026-03-10T15:16:51.000Z",
      "authors": [],
      "tags": [
        "计算机基础"
      ]
    },
    {
      "title": "DNS 域名系统详解（应用层）",
      "url": "https://javaguide.cn/cs-basics/network/dns.html",
      "id": "https://javaguide.cn/cs-basics/network/dns.html",
      "summary": "详解 DNS 的层次结构与解析流程，覆盖递归/迭代、缓存与权威服务器，明确应用层端口与性能优化要点。",
      "content_html": "<p>DNS（Domain Name System）域名管理系统，是当用户使用浏览器访问网址之后，使用的第一个重要协议。DNS 要解决的是<strong>域名和 IP 地址的映射问题</strong>。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/network/dns-overview.png\" alt=\"DNS:域名系统\"></p>\n<p>在实际使用中，有一种情况下，浏览器是可以不必动用 DNS 就可以获知域名和 IP 地址的映射的。浏览器在本地会维护一个<code>hosts</code>列表，一般来说浏览器要先查看要访问的域名是否在<code>hosts</code>列表中，如果有的话，直接提取对应的 IP 地址记录，就好了。如果本地<code>hosts</code>列表内没有域名-IP 对应记录的话，那么 DNS 就闪亮登场了。</p>\n<p>目前 DNS 的设计采用的是分布式、层次数据库结构，<strong>DNS 是应用层协议，通常基于 UDP 协议，端口为 53</strong>。当响应数据超过 UDP 报文长度限制（512 字节，EDNS0 可扩展至更大）或进行区域传送（Zone Transfer）时，会改用 TCP 协议以保证数据完整性。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/network/network-protocol-overview.png\" alt=\"TCP/IP 各层协议概览\"></p>\n<h2>DNS 服务器</h2>\n<p>DNS 服务器自底向上可以依次分为以下几个层级(所有 DNS 服务器都属于以下四个类别之一):</p>\n<ul>\n<li>根 DNS 服务器。根 DNS 服务器提供 TLD 服务器的 IP 地址。目前世界上只有 13 组根服务器，我国境内目前仍没有根服务器。</li>\n<li>顶级域 DNS 服务器（TLD 服务器）。顶级域是指域名的后缀，如<code>com</code>、<code>org</code>、<code>net</code>和<code>edu</code>等。国家也有自己的顶级域，如<code>uk</code>、<code>fr</code>和<code>ca</code>。TLD 服务器提供了权威 DNS 服务器的 IP 地址。</li>\n<li>权威 DNS 服务器。在因特网上具有公共可访问主机的每个组织机构必须提供公共可访问的 DNS 记录，这些记录将这些主机的名字映射为 IP 地址。</li>\n<li>本地 DNS 服务器。每个 ISP（互联网服务提供商）都有一个自己的本地 DNS 服务器。当主机发出 DNS 请求时，该请求被发往本地 DNS 服务器，它起着代理的作用，并将该请求转发到 DNS 层次结构中。严格说来，不属于 DNS 层级结构。</li>\n</ul>\n<p><strong>世界上真的只有 13 台根服务器吗？</strong> 这是一个流传已久的技术误解。如果你在网上搜索，仍能看到许多陈旧文章宣称“全球仅有 13 台根服务器，且全部由美国控制”。</p>\n<p><strong>事实并非如此。</strong></p>\n<p>最初在设计 DNS（域名系统）架构时，受限于早期 IPv4 数据包的大小限制（UDP 报文需控制在 512 字节以内），预留给根服务器地址的空间确实只够容纳 13 个 IP 地址，每个 IP 地址对应一个不同的根 DNS 服务器。这 13 个地址分别被命名为 <code>a.root-servers.net</code> 到 <code>m.root-servers.net</code>。</p>\n<p>虽然<strong>逻辑上</strong>只有 13 个 IP 地址，但随着互联网规模的爆发，物理上的“单一服务器”早已无法承载全球的查询压力。为了提升 DNS 的可靠性、安全性和响应速度，技术人员引入了 <strong>IP 任播（Anycast）</strong> 技术。</p>\n<p>通过任播技术，每一个逻辑 IP 地址背后都可以对应成百上千台分布在全球各地的物理服务器。当你发起查询请求时，互联网路由协议（BGP）会自动将请求引导至地理位置或网络路径上离你<strong>最近</strong>的那台物理实例。</p>\n<p>截止到 2023 年底，全球根服务器物理实例总数已超过 1700 台。根据 <strong><a href=\"https://root-servers.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Root-Servers.org</a></strong> 的最新实时监测数据，到 <strong>2026 年，全球根服务器物理实例已突破 1900+ 台</strong>，并正向 2000 台大关迈进。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/network/root-servers-org.png\" alt=\"Root-Servers.org\"></p>\n<h2>DNS 工作流程</h2>\n<p>以下图为例，介绍 DNS 的查询解析过程。DNS 的查询解析过程分为两种模式：</p>\n<ul>\n<li><strong>迭代</strong></li>\n<li><strong>递归</strong></li>\n</ul>\n<p>下图是实践中常采用的方式，从请求主机到本地 DNS 服务器的查询是递归的，其余的查询时迭代的。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/network/DNS-process.png\" alt></p>\n<p>现在，主机<code>cis.poly.edu</code>想知道<code>gaia.cs.umass.edu</code>的 IP 地址。假设主机<code>cis.poly.edu</code>的本地 DNS 服务器为<code>dns.poly.edu</code>，并且<code>gaia.cs.umass.edu</code>的权威 DNS 服务器为<code>dns.cs.umass.edu</code>。</p>\n<ol>\n<li>首先，主机<code>cis.poly.edu</code>向本地 DNS 服务器<code>dns.poly.edu</code>发送一个 DNS 请求，该查询报文包含被转换的域名<code>gaia.cs.umass.edu</code>。</li>\n<li>本地 DNS 服务器<code>dns.poly.edu</code>检查本机缓存，发现并无记录，也不知道<code>gaia.cs.umass.edu</code>的 IP 地址该在何处，不得不向根服务器发送请求。</li>\n<li>根服务器注意到请求报文中含有<code>edu</code>顶级域，因此告诉本地 DNS，你可以向<code>edu</code>的 TLD DNS 发送请求，因为目标域名的 IP 地址很可能在那里。</li>\n<li>本地 DNS 获取到了<code>edu</code>的 TLD DNS 服务器地址，向其发送请求，询问<code>gaia.cs.umass.edu</code>的 IP 地址。</li>\n<li><code>edu</code>的 TLD DNS 服务器仍不清楚请求域名的 IP 地址，但是它注意到该域名有<code>umass.edu</code>前缀，因此返回告知本地 DNS，<code>umass.edu</code>的权威服务器可能记录了目标域名的 IP 地址。</li>\n<li>这一次，本地 DNS 将请求发送给权威 DNS 服务器<code>dns.cs.umass.edu</code>。</li>\n<li>终于，由于<code>gaia.cs.umass.edu</code>向权威 DNS 服务器备案过，在这里有它的 IP 地址记录，权威 DNS 成功地将 IP 地址返回给本地 DNS。</li>\n<li>最后，本地 DNS 获取到了目标域名的 IP 地址，将其返回给请求主机。</li>\n</ol>\n<p>除了迭代式查询，还有一种递归式查询如下图，具体过程和上述类似，只是顺序有所不同。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/network/DNS-process2.png\" alt></p>\n<p>另外，DNS 的缓存位于本地 DNS 服务器。由于全世界的根服务器甚少，只有 600 多台，分为 13 组，且顶级域的数量也在一个可数的范围内，因此本地 DNS 通常已经缓存了很多 TLD DNS 服务器，所以在实际查找过程中，无需访问根服务器。根服务器通常是被跳过的，不请求的。这样可以提高 DNS 查询的效率和速度，减少对根服务器和 TLD 服务器的负担。</p>\n<h2>DNS 报文格式</h2>\n<p>DNS 的报文格式如下图所示：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/network/DNS-packet.png\" alt></p>\n<p>DNS 报文分为查询和回答报文，两种形式的报文结构相同。</p>\n<ul>\n<li>标识符。16 比特，用于标识该查询。这个标识符会被复制到对查询的回答报文中，以便让客户用它来匹配发送的请求和接收到的回答。</li>\n<li>标志。1 比特的”查询/回答“标识位，<code>0</code>表示查询报文，<code>1</code>表示回答报文；1 比特的”权威的“标志位（当某 DNS 服务器是所请求名字的权威 DNS 服务器时，且是回答报文，使用”权威的“标志）；1 比特的”希望递归“标志位，显式地要求执行递归查询；1 比特的”递归可用“标志位，用于回答报文中，表示 DNS 服务器支持递归查询。</li>\n<li>问题数、回答 RR 数、权威 RR 数、附加 RR 数。分别指示了后面 4 类数据区域出现的数量。</li>\n<li>问题区域。包含正在被查询的主机名字，以及正被询问的问题类型。</li>\n<li>回答区域。包含了对最初请求的名字的资源记录。<strong>在回答报文的回答区域中可以包含多条 RR，因此一个主机名能够有多个 IP 地址。</strong></li>\n<li>权威区域。包含了其他权威服务器的记录。</li>\n<li>附加区域。包含了其他有帮助的记录。</li>\n</ul>\n<h2>DNS 记录</h2>\n<p>DNS 服务器在响应查询时，需要查询自己的数据库，数据库中的条目被称为 <strong>资源记录(Resource Record，RR)</strong> 。RR 提供了主机名到 IP 地址的映射。RR 是一个包含了<code>Name</code>, <code>Value</code>, <code>Type</code>, <code>TTL</code>四个字段的四元组。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/network/20210506174303797.png\" alt></p>\n<p><code>TTL</code>是该记录的生存时间，它决定了资源记录应当从缓存中删除的时间。</p>\n<p><code>Name</code>和<code>Value</code>字段的取值取决于<code>Type</code>：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/network/20210506170307897.png\" alt></p>\n<ul>\n<li>如果<code>Type=A</code>，则<code>Name</code>是主机名信息，<code>Value</code> 是该主机名对应的 IP 地址。这样的 RR 记录了一条主机名到 IP 地址的映射。</li>\n<li>如果 <code>Type=AAAA</code> （与 <code>A</code> 记录非常相似），唯一的区别是 A 记录使用的是 IPv4，而 <code>AAAA</code> 记录使用的是 IPv6。</li>\n<li>如果<code>Type=CNAME</code> (Canonical Name Record,真实名称记录) ，则<code>Value</code>是别名为<code>Name</code>的主机对应的规范主机名。<code>Value</code>值才是规范主机名。<code>CNAME</code> 记录将一个主机名映射到另一个主机名。<code>CNAME</code> 记录用于为现有的 <code>A</code> 记录创建别名。下文有示例。</li>\n<li>如果<code>Type=NS</code>，则<code>Name</code>是个域，而<code>Value</code>是个知道如何获得该域中主机 IP 地址的权威 DNS 服务器的主机名。通常这样的 RR 是由 TLD 服务器发布的。</li>\n<li>如果<code>Type=MX</code> ，则<code>Value</code>是个别名为<code>Name</code>的邮件服务器的规范主机名。既然有了 <code>MX</code> 记录，那么邮件服务器可以和其他服务器使用相同的别名。为了获得邮件服务器的规范主机名，需要请求 <code>MX</code> 记录；为了获得其他服务器的规范主机名，需要请求 <code>CNAME</code> 记录。</li>\n</ul>\n<p><code>CNAME</code>记录总是指向另一则域名，而非 IP 地址。假设有下述 DNS zone：</p>\n<div class=\"language-plain line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"plain\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-plain\"><span class=\"line\"><span>NAME                    TYPE   VALUE</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div>",
      "image": "https://oss.javaguide.cn/github/javaguide/cs-basics/network/dns-overview.png",
      "date_published": "2023-04-11T08:54:54.000Z",
      "date_modified": "2026-02-22T12:21:13.000Z",
      "authors": [],
      "tags": [
        "计算机基础"
      ]
    },
    {
      "title": "坚持写技术博客六年了!",
      "url": "https://javaguide.cn/about-the-author/writing-technology-blog-six-years.html",
      "id": "https://javaguide.cn/about-the-author/writing-technology-blog-six-years.html",
      "summary": "坚持写技术博客六年的心得分享，写博客的好处、如何坚持下去、写哪些方向的博客、实用写作技巧等经验总结。",
      "content_html": "<p>坚持写技术博客已经有六年了，也算是一个小小的里程碑了。</p>\n<p>一开始，我写技术博客就是简单地总结自己课堂上学习的课程比如网络、操作系统。渐渐地，我开始撰写一些更为系统化的知识点详解和面试常见问题总结。</p>\n<p><img src=\"https://oss.javaguide.cn/about-the-author/college-life/image-20230408131717766.png\" alt=\"JavaGuide 首页\"></p>\n<p>许多人都想写技术博客，但却不清楚这对他们有何好处。有些人开始写技术博客，却不知道如何坚持下去，也不知道该写些什么。这篇文章我会认真聊聊我对记录技术博客的一些看法和心得，或许可以帮助你解决这些问题。</p>\n<h2>写技术博客有哪些好处？</h2>\n<h3>学习效果更好，加深知识点的认识</h3>\n<p><strong>费曼学习法</strong> 大家应该已经比较清楚了，这是一个经过实践证明非常有效的学习方式。费曼学习法的命名源自 Richard Feynman，这位物理学家曾获得过诺贝尔物理学奖，也曾参与过曼哈顿计划。</p>\n<p>所谓费曼学习法，就是当你学习了一个新知识之后，想象自己是一个老师：用最简单、最浅显直白的话复述、表达复杂深奥的知识，最好不要使用行业术语，让非行业内的人也能听懂。为了达到这种效果，最好想象你是在给一个 80 多岁或 8 岁的小孩子上课，甚至他们都能听懂。</p>\n<p><img src=\"https://oss.javaguide.cn/about-the-author/college-life/v2-19373c2e61873c5083ee4b1d1523f8f5_720w.png\" alt=\"教授别人学习效果最好\"></p>\n<p>看书、看视频这类都属于是被动学习，学习效果比较差。费曼学习方法属于主动学习，学习效果非常好。</p>\n<p><strong>写技术博客实际就是教别人的一种方式。</strong> 不过，记录技术博客的时候是可以有专业术语（除非你的文章群体是非技术人员），只是你需要用自己的话表述出来，尽量让别人一看就懂。<strong>切忌照搬书籍或者直接复制粘贴其他人的总结！</strong></p>\n<p>如果我们被动的学习某个知识点，可能大部分时候都是仅仅满足自己能够会用的层面，你并不会深究其原理，甚至很多关键概念都没搞懂。</p>\n<p>如果你是要将你所学到的知识总结成一篇博客的话，一定会加深你对这个知识点的思考。很多时候，你为了将一个知识点讲清楚，你回去查阅很多资料，甚至需要查看很多源码，这些细小的积累在潜移默化中加深了你对这个知识点的认识。</p>\n<p>甚至，我还经常会遇到这种情况：<strong>写博客的过程中，自己突然意识到自己对于某个知识点的理解存在错误。</strong></p>\n<p><strong>写博客本身就是一个对自己学习到的知识进行总结、回顾、思考的过程。记录博客也是对于自己学习历程的一种记录。随着时间的流逝、年龄的增长，这又何尝不是一笔宝贵的精神财富呢？</strong></p>\n<p>知识星球的一位球友还提到写技术博客有助于完善自己的知识体系：</p>\n<p><img src=\"https://oss.javaguide.cn/about-the-author/college-life/image-20230408121336432.png\" alt=\"写技术博客有助于完善自己的知识体系\"></p>\n<h3>帮助别人的同时获得成就感</h3>\n<p>就像我们程序员希望自己的产品能够得到大家的认可和喜欢一样。我们写技术博客在某一方面当然也是为了能够得到别人的认可。</p>\n<p><strong>当你写的东西对别人产生帮助的时候，你会产生成就感和幸福感。</strong></p>\n<p><img src=\"https://oss.javaguide.cn/about-the-author/college-life/image-20230404181906257.png\" alt=\"读者的认可\"></p>\n<p>这种成就感和幸福感会作为 <strong>正向反馈</strong> ，继续激励你写博客。</p>\n<p>但是，即使受到很多读者的赞赏，也要保持谦虚学习的太多。人外有人，比你技术更厉害的读者多了去，一定要虚心学习！</p>\n<p>当然，你可以可能会受到很多非议。可能会有很多人说你写的文章没有深度，还可能会有很多人说你闲的蛋疼，你写的东西网上/书上都有。</p>\n<p><strong>坦然对待这些非议，做好自己，走好自己的路就好！用行动自证！</strong></p>\n<h3>可能会有额外的收入</h3>\n<p>写博客可能还会为你带来经济收入。输出价值的同时，还能够有合理的经济收入，这是最好的状态！</p>\n<p>为什么说是可能呢？ <strong>因为就目前来看，大部分人还是很难短期通过写博客有收入。我也不建议大家一开始写博客就奔着赚钱的目的，这样功利性太强了，效果可能反而不好。就比如说你坚持了写了半年发现赚不到钱，那你可能就会坚持不下去了。</strong></p>\n<p>我自己从大二开始写博客，大三下学期开始将自己的文章发布到公众号上，一直到大四下学期，才通过写博客赚到属于自己的第一笔钱。</p>\n<p>第一笔钱是通过微信公众号接某培训机构的推广获得的。没记错的话，当时通过这个推广为自己带来了大约 <strong>500</strong> 元的收入。虽然这不是很多，但对于还在上大学的我来说，这笔钱非常宝贵。那时我才知道，原来写作真的可以赚钱，这也让我更有动力去分享自己的写作。可惜的是，在接了两次这家培训机构的广告之后，它就倒闭了。</p>\n<p>之后，很长一段时间我都没有接到过广告。直到网易的课程合作找上门，一篇文章 1000 元，每个月接近一篇，发了接近两年，这也算是我在大学期间比较稳定的一份收入来源了。</p>\n<p><img src=\"https://oss.javaguide.cn/about-the-author/college-life/image-20230408115720135.png\" alt=\"网易的课程合作\"></p>\n<p>老粉应该大部分都是通过 JavaGuide 这个项目认识我的，这是我在大三开始准备秋招面试时创建的一个项目。没想到这个项目竟然火了一把，一度霸占了 GitHub 榜单。可能当时国内这类开源文档教程类项目太少了，所以这个项目受欢迎程度非常高。</p>\n<p><img src=\"https://oss.javaguide.cn/about-the-author/college-life/image-20230408131849198.png\" alt=\"JavaGuide Star 趋势\"></p>\n<p>项目火了之后，有一个国内比较大的云服务公司找到我，说是要赞助 JavaGuide 这个项目。我既惊又喜，担心别人是骗子，反复确认合同之后，最终确定以每月 1000 元的费用在我的项目首页加上对方公司的 banner。</p>\n<p>随着时间的推移，以及自己后来写了一些比较受欢迎、比较受众的文章，我的博客知名度也有所提升，通过写博客的收入也增加了不少。</p>\n<h3>增加个人影响力</h3>\n<p>写技术博客是一种展示自己技术水平和经验的方式，能够让更多的人了解你的专业领域知识和技能。持续分享优质的技术文章，一定能够在技术领域增加个人影响力，这一点是毋庸置疑的。</p>\n<p>有了个人影响力之后，不论是对你后面找工作，还是搞付费知识分享或者出书，都非常有帮助。</p>\n<p>拿我自己来说，已经很多知名出版社的编辑找过我，协商出一本的书的事情。这种机会应该也是很多人梦寐以求的。不过，我都一一拒绝了，因为觉得自己远远没有达到能够写书的水平。</p>\n<p><img src=\"https://oss.javaguide.cn/about-the-author/college-life/image-20230408121132211.png\" alt=\"电子工业出版社编辑邀约出书\"></p>\n<p>其实不出书最主要的原因还是自己嫌麻烦，整个流程的事情太多了。我自己又是比较佛系随性的人，平时也不想把时间都留给工作。</p>\n<h2>怎样才能坚持写技术博客？</h2>\n<p><strong>不可否认，人都是有懒性的，这是人的本性。我们需要一个目标/动力来 Push 一下自己。</strong></p>\n<p>就技术写作而言，你的目标可以以技术文章的数量为标准，比如：</p>\n<ul>\n<li>一年写多少篇技术文章。我个人觉得一年的范围还是太长了，不太容易定一个比较合适的目标。</li>\n<li>每月输出一篇高质量的技术文章。这个相对容易实现一些，每月一篇，一年也有十二篇了，也很不错了。</li>\n</ul>\n<p>不过，以技术文章的数量为目标有点功利化，文章的质量同样很重要。一篇高质量的技术文可能需要花费一周甚至半个月的业余时间才能写完。一定要避免自己刻意追求数量，而忽略质量，迷失技术写作的本心。</p>\n<p>我个人给自己定的目标是：<strong>每个月至少写一篇原创技术文章或者认真修改完善过去写的三篇技术文章</strong> （像开源项目推荐、开源项目学习、个人经验分享、面经分享等等类型的文章不会被记入）。</p>\n<p>我的目标对我来说比较容易完成，因此不会出现为了完成目标而应付任务的情况。在我状态比较好，工作也不是很忙的时候，还会经常超额完成任务。下图是我今年 3 月份完成的任务（任务管理工具：Microsoft To-Do）。除了 gossip 协议是去年写的之外，其他都是 3 月份完成的。</p>\n<p><img src=\"https://oss.javaguide.cn/about-the-author/college-life/image-20230404181033089.png\" alt></p>\n<p>如果觉得以文章数量为标准过于功利的话，也可以比较随性地按照自己的节奏来写作。不过，一般这种情况下，你很可能过段时间就忘了还有这件事，开始慢慢抵触写博客。</p>\n<p>写完一篇技术文章之后，我们不光要同步到自己的博客，还要分发到国内一些常见的技术社区比如博客园、掘金。<strong>分发到其他平台的原因是获得关注进而收获正向反馈（动力来源之一）与建议，这是技术写作能坚持下去的非常重要的一步，一定要重视！！！</strong></p>\n<p>说实话，当你写完一篇自认为还不错的文章的幸福感和成就感还是有的。<strong>但是，让自己去做这件事情还是比较痛苦的。</strong> 就好比你让自己出去玩很简单，为了达到这个目的，你可以有各种借口。但是，想要自己老老实实学习，还是需要某个外力来督促自己的。</p>\n<h2>写哪些方向的博客比较好？</h2>\n<p>通常来说，写下面这些方向的博客会比较好：</p>\n<ol>\n<li><strong>详细讲解某个知识点</strong>：一定要有自己的思考而不是东拼西凑。不仅要介绍知识点的基本概念和原理，还需要适当结合实际案例和应用场景进行举例说明。</li>\n<li><strong>问题排查/性能优化经历</strong>：需要详细描述清楚具体的场景以及解决办法。一定要有足够的细节描述，包括出现问题的具体场景、问题的根本原因、解决问题的思路和具体步骤等等。同时，要注重实践性和可操作性，帮助读者更好地学习理解。</li>\n<li><strong>源码阅读记录</strong>：从一个功能点出发描述其底层源码实现，谈谈你从源码中学到了什么。</li>\n</ol>\n<p>最重要的是一定要重视 Markdown 规范，不然内容再好也会显得不专业。</p>\n<p>详见 <a href=\"/javaguide/contribution-guideline.html\" target=\"_blank\">Markdown 规范</a> （很重要，尽量按照规范来，对你工作中写文档会非常有帮助）</p>\n<h2>有没有什么写作技巧分享？</h2>\n<h3>句子不要过长</h3>\n<p>句子不要过长，尽量使用短句（但也不要太短），这样读者更容易阅读和理解。</p>\n<h3>尽量让文章更加生动有趣</h3>\n<p>尽量让文章更加生动有趣，比如你可以适当举一些形象的例子、用一些有趣的段子、歇后语或者网络热词。</p>\n<p>不过，这个也主要看你的文章风格。</p>\n<h3>使用简单明了的语言</h3>\n<p>避免使用阅读者可能无法理解的行话或复杂语言。</p>\n<p>注重清晰度和说服力，保持简单。简单的写作是有说服力的，一个五句话的好论点会比一百句话的精彩论点更能打动人。为什么格言、箴言这类文字容易让人接受，与简洁、直白也有些关系。</p>\n<h3>使用视觉效果</h3>\n<p>图表、图像等视觉效果可以让朴素的文本内容更容易理解。记得在适当的地方使用视觉效果来增强你的文章的表现力。</p>\n<p><img src=\"https://oss.javaguide.cn/about-the-author/college-life/image-20230404192458759.png\" alt></p>\n<h3>技术文章配图色彩要鲜明</h3>\n<p>下面是同样内容的两张图，都是通过 drawio 画的，小伙伴们更喜欢哪一张呢？</p>\n<p>我相信大部分小伙伴都会选择后面一个色彩更鲜明的！</p>\n<p>色彩的调整不过花费了我不到 30s 的时间，带来的阅读体验的上升却是非常之大！</p>\n<p><img src=\"https://oss.javaguide.cn/2021-1/image-20210104182517226.png\" alt></p>\n<h3>确定你的读者</h3>\n<p>写作之前，思考一下你的文章的主要受众全体是谁。受众群体确定之后，你可以根据受众的需求和理解水平调整你的写作风格和内容难易程度。</p>\n<h3>审查和修改</h3>\n<p>在发表之前一定要审查和修改你的文章。这将帮助你发现错误、澄清任何令人困惑的信息并提高文档的整体质量。</p>\n<p><strong>好文是改出来的，切记！！！</strong></p>\n<h2>总结</h2>\n<p>总的来说，写技术博客是一件利己利彼的事情。你可能会从中收获到很多东西，你写的东西也可能对别人也有很大的帮助。但是，写技术博客还是比较耗费自己时间的，你需要和工作以及生活做好权衡。</p>\n",
      "image": "https://oss.javaguide.cn/about-the-author/college-life/image-20230408131717766.png",
      "date_published": "2023-04-09T12:42:14.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [],
      "tags": [
        "走近作者"
      ]
    },
    {
      "title": "操作系统常见面试题总结(下)",
      "url": "https://javaguide.cn/cs-basics/operating-system/operating-system-basic-questions-02.html",
      "id": "https://javaguide.cn/cs-basics/operating-system/operating-system-basic-questions-02.html",
      "summary": "最新操作系统高频面试题总结（下）：虚拟内存映射、内存碎片/伙伴系统、TLB+页缺失处理、分页分段对比、页面置换算法详解、文件系统&磁盘调度，附图表+⭐️重点标注，一文掌握OS内存/文件考点，快速通关后端面试！",
      "content_html": "<p><a href=\"/zhuanlan/interview-guide.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/interview-guide-banner.png\" alt=\"《SpringAI 智能面试平台+RAG 知识库》\"></a></p>\n<h2>内存管理</h2>\n<h3>内存管理主要做了什么？</h3>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/memory-management-roles.png\" alt=\"内存管理主要做的事情\"></p>\n<p>操作系统的内存管理非常重要，主要负责下面这些事情：</p>\n<ul>\n<li><strong>内存的分配与回收</strong>：对进程所需的内存进行分配和释放，malloc 函数：申请内存，free 函数：释放内存。</li>\n<li><strong>地址转换</strong>：将程序中的虚拟地址转换成内存中的物理地址。</li>\n<li><strong>内存扩充</strong>：当系统没有足够的内存时，利用虚拟内存技术或自动覆盖技术，从逻辑上扩充内存。</li>\n<li><strong>内存映射</strong>：将一个文件直接映射到进程的进程空间中，这样可以通过内存指针用读写内存的办法直接存取文件内容，速度更快。</li>\n<li><strong>内存优化</strong>：通过调整内存分配策略和回收算法来优化内存使用效率。</li>\n<li><strong>内存安全</strong>：保证进程之间使用内存互不干扰，避免一些恶意程序通过修改内存来破坏系统的安全性。</li>\n<li>……</li>\n</ul>\n<h3>什么是内存碎片？</h3>\n<p>内存碎片是由内存的申请和释放产生的，通常分为下面两种：</p>\n<ul>\n<li><strong>内部内存碎片(Internal Memory Fragmentation，简称为内存碎片)</strong>：已经分配给进程使用但未被使用的内存。导致内部内存碎片的主要原因是，当采用固定比例比如 2 的幂次方进行内存分配时，进程所分配的内存可能会比其实际所需要的大。举个例子，一个进程只需要 65 字节的内存，但为其分配了 128（2^7） 大小的内存，那 63 字节的内存就成为了内部内存碎片。</li>\n<li><strong>外部内存碎片(External Memory Fragmentation，简称为外部碎片)</strong>：由于未分配的连续内存区域太小，以至于不能满足任意进程所需要的内存分配请求，这些小片段且不连续的内存空间被称为外部碎片。也就是说，外部内存碎片指的是那些并未分配给进程但又不能使用的内存。我们后面介绍的分段机制就会导致外部内存碎片。</li>\n</ul>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/internal-and-external-fragmentation.png\" alt=\"内存碎片\"></p>\n<p>内存碎片会导致内存利用率下降，如何减少内存碎片是内存管理要非常重视的一件事情。</p>\n<h3>常见的内存管理方式有哪些？</h3>\n<p>内存管理方式可以简单分为下面两种：</p>\n<ul>\n<li><strong>连续内存管理</strong>：为一个用户程序分配一个连续的内存空间，内存利用率一般不高。</li>\n<li><strong>非连续内存管理</strong>：允许一个程序使用的内存分布在离散或者说不相邻的内存中，相对更加灵活一些。</li>\n</ul>\n<h4>连续内存管理</h4>\n<p><strong>块式管理</strong> 是早期计算机操作系统的一种连续内存管理方式，存在严重的内存碎片问题。块式管理会将内存分为几个固定大小的块，每个块中只包含一个进程。如果程序运行需要内存的话，操作系统就分配给它一块，如果程序运行只需要很小的空间的话，分配的这块内存很大一部分几乎被浪费了。这些在每个块中未被利用的空间，我们称之为内部内存碎片。除了内部内存碎片之外，由于两个内存块之间可能还会有外部内存碎片，这些不连续的外部内存碎片由于太小了无法再进行分配。</p>\n<p>在 Linux 系统中，连续内存管理采用了 <strong>伙伴系统（Buddy System）算法</strong> 来实现，这是一种经典的连续内存分配算法，可以有效解决外部内存碎片的问题。伙伴系统的主要思想是将内存按 2 的幂次划分（每一块内存大小都是 2 的幂次比如 2^6=64 KB），并将相邻的内存块组合成一对伙伴（注意：<strong>必须是相邻的才是伙伴</strong>）。</p>\n<p>当进行内存分配时，伙伴系统会尝试找到大小最合适的内存块。如果找到的内存块过大，就将其一分为二，分成两个大小相等的伙伴块。如果还是大的话，就继续切分，直到到达合适的大小为止。</p>\n<p>假设两块相邻的内存块都被释放，系统会将这两个内存块合并，进而形成一个更大的内存块，以便后续的内存分配。这样就可以减少内存碎片的问题，提高内存利用率。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/linux-buddy-system.png\" alt=\"伙伴系统（Buddy System）内存管理\"></p>\n<p>虽然解决了外部内存碎片的问题，但伙伴系统仍然存在内存利用率不高的问题（内部内存碎片）。这主要是因为伙伴系统只能分配大小为 2^n 的内存块，因此当需要分配的内存大小不是 2^n 的整数倍时，会浪费一定的内存空间。举个例子：如果要分配 65 大小的内存快，依然需要分配 2^7=128 大小的内存块。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/buddy-system-memory-waste.png\" alt=\"伙伴系统内存浪费问题\"></p>\n<p>对于内部内存碎片的问题，Linux 采用 <strong>SLAB</strong> 进行解决。由于这部分内容不是本篇文章的重点，这里就不详细介绍了。</p>\n<h4>非连续内存管理</h4>\n<p>非连续内存管理存在下面 3 种方式：</p>\n<ul>\n<li><strong>段式管理</strong>：以段(一段连续的物理内存)的形式管理/分配物理内存。应用程序的虚拟地址空间被分为大小不等的段，段是有实际意义的，每个段定义了一组逻辑信息，例如有主程序段 MAIN、子程序段 X、数据段 D 及栈段 S 等。</li>\n<li><strong>页式管理</strong>：把物理内存分为连续等长的物理页，应用程序的虚拟地址空间也被划分为连续等长的虚拟页，是现代操作系统广泛使用的一种内存管理方式。</li>\n<li><strong>段页式管理机制</strong>：结合了段式管理和页式管理的一种内存管理机制，把物理内存先分成若干段，每个段又继续分成若干大小相等的页。</li>\n</ul>\n<h3>虚拟内存</h3>\n<h4>什么是虚拟内存?有什么用？</h4>\n<p><strong>虚拟内存(Virtual Memory)</strong> 是计算机系统内存管理非常重要的一个技术，本质上来说它只是逻辑存在的，是一个假想出来的内存空间，主要作用是作为进程访问主存（物理内存）的桥梁并简化内存管理。</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/virtual-memory.png\" alt=\"虚拟内存作为进程访问主存的桥梁\"></p>\n<p>总结来说，虚拟内存主要提供了下面这些能力：</p>\n<ul>\n<li><strong>隔离进程</strong>：物理内存通过虚拟地址空间访问，虚拟地址空间与进程一一对应。每个进程都认为自己拥有了整个物理内存，进程之间彼此隔离，一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。</li>\n<li><strong>提升物理内存利用率</strong>：有了虚拟地址空间后，操作系统只需要将进程当前正在使用的部分数据或指令加载入物理内存。</li>\n<li><strong>简化内存管理</strong>：进程都有一个一致且私有的虚拟地址空间，程序员不用和真正的物理内存打交道，而是借助虚拟地址空间访问物理内存，从而简化了内存管理。</li>\n<li><strong>多个进程共享物理内存</strong>：进程在运行过程中，会加载许多操作系统的动态库。这些库对于每个进程而言都是公用的，它们在内存中实际只会加载一份，这部分称为共享内存。</li>\n<li><strong>提高内存使用安全性</strong>：控制进程对物理内存的访问，隔离不同进程的访问权限，提高系统的安全性。</li>\n<li><strong>提供更大的可使用内存空间</strong>：可以让程序拥有超过系统物理内存大小的可用内存空间。这是因为当物理内存不够用时，可以利用磁盘充当，将物理内存页（通常大小为 4 KB）保存到磁盘文件（会影响读写速度），数据或代码页会根据需要在物理内存与磁盘之间移动。</li>\n</ul>\n<h4>没有虚拟内存有什么问题？</h4>\n<p>如果没有虚拟内存的话，程序直接访问和操作的都是物理内存，看似少了一层中介，但多了很多问题。</p>\n<p><strong>具体有什么问题呢？</strong> 这里举几个例子说明(参考虚拟内存提供的能力回答这个问题)：</p>\n<ol>\n<li>用户程序可以访问任意物理内存，可能会不小心操作到系统运行必需的内存，进而造成操作系统崩溃，严重影响系统的安全。</li>\n<li>同时运行多个程序容易崩溃。比如你想同时运行一个微信和一个 QQ 音乐，微信在运行的时候给内存地址 1xxx 赋值后，QQ 音乐也同样给内存地址 1xxx 赋值，那么 QQ 音乐对内存的赋值就会覆盖微信之前所赋的值，这就可能会造成微信这个程序会崩溃。</li>\n<li>程序运行过程中使用的所有数据或指令都要载入物理内存，根据局部性原理，其中很大一部分可能都不会用到，白白占用了宝贵的物理内存资源。</li>\n<li>……</li>\n</ol>\n<h4>什么是虚拟地址和物理地址？</h4>\n<p><strong>物理地址（Physical Address）</strong> 是真正的物理内存中地址，更具体点来说是内存地址寄存器中的地址。程序中访问的内存地址不是物理地址，而是 <strong>虚拟地址（Virtual Address）</strong> 。</p>\n<p>也就是说，我们编程开发的时候实际就是在和虚拟地址打交道。比如在 C 语言中，指针里面存储的数值就可以理解成为内存里的一个地址，这个地址也就是我们说的虚拟地址。</p>\n<p>操作系统一般通过 CPU 芯片中的一个重要组件 <strong>MMU(Memory Management Unit，内存管理单元)</strong> 将虚拟地址转换为物理地址，这个过程被称为 <strong>地址翻译/地址转换（Address Translation）</strong> 。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/physical-virtual-address-translation.png\" alt=\"地址翻译过程\"></p>\n<p>通过 MMU 将虚拟地址转换为物理地址后，再通过总线传到物理内存设备，进而完成相应的物理内存读写请求。</p>\n<p>MMU 将虚拟地址翻译为物理地址的主要机制有两种: <strong>分段机制</strong> 和 <strong>分页机制</strong> 。</p>\n<h4>什么是虚拟地址空间和物理地址空间？</h4>\n<ul>\n<li>虚拟地址空间是虚拟地址的集合，是虚拟内存的范围。每一个进程都有一个一致且私有的虚拟地址空间。</li>\n<li>物理地址空间是物理地址的集合，是物理内存的范围。</li>\n</ul>\n<h4>虚拟地址与物理内存地址是如何映射的？</h4>\n<p>MMU 将虚拟地址翻译为物理地址的主要机制有 3 种:</p>\n<ol>\n<li>分段机制</li>\n<li>分页机制</li>\n<li>段页机制</li>\n</ol>\n<p>其中，现代操作系统广泛采用分页机制，需要重点关注！</p>\n<h3>分段机制</h3>\n<p><strong>分段机制（Segmentation）</strong> 以段(一段 <strong>连续</strong> 的物理内存)的形式管理/分配物理内存。应用程序的虚拟地址空间被分为大小不等的段，段是有实际意义的，每个段定义了一组逻辑信息，例如有主程序段 MAIN、子程序段 X、数据段 D 及栈段 S 等。</p>\n<h4>段表有什么用？地址翻译过程是怎样的？</h4>\n<p>分段管理通过 <strong>段表（Segment Table）</strong> 映射虚拟地址和物理地址。</p>\n<p>分段机制下的虚拟地址由两部分组成：</p>\n<ul>\n<li><strong>段号</strong>：标识着该虚拟地址属于整个虚拟地址空间中的哪一个段。</li>\n<li><strong>段内偏移量</strong>：相对于该段起始地址的偏移量。</li>\n</ul>\n<p>具体的地址翻译过程如下：</p>\n<ol>\n<li>MMU 首先解析得到虚拟地址中的段号；</li>\n<li>通过段号去该应用程序的段表中取出对应的段信息（找到对应的段表项）；</li>\n<li>从段信息中取出该段的起始地址（物理地址）加上虚拟地址中的段内偏移量得到最终的物理地址。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/segment-virtual-address-composition.png\" alt=\"分段机制下的地址翻译过程\"></p>\n<p>段表中还存有诸如段长(可用于检查虚拟地址是否超出合法范围)、段类型（该段的类型，例如代码段、数据段等）等信息。</p>\n<p><strong>通过段号一定要找到对应的段表项吗？得到最终的物理地址后对应的物理内存一定存在吗？</strong></p>\n<p>不一定。段表项可能并不存在：</p>\n<ul>\n<li><strong>段表项被删除</strong>：软件错误、软件恶意行为等情况可能会导致段表项被删除。</li>\n<li><strong>段表项还未创建</strong>：如果系统内存不足或者无法分配到连续的物理内存块就会导致段表项无法被创建。</li>\n</ul>\n<h4>分段机制为什么会导致内存外部碎片？</h4>\n<p>分段机制容易出现外部内存碎片，即在段与段之间留下碎片空间(不足以映射给虚拟地址空间中的段)。从而造成物理内存资源利用率的降低。</p>\n<p>举个例子：假设可用物理内存为 5G 的系统使用分段机制分配内存。现在有 4 个进程，每个进程的内存占用情况如下：</p>\n<ul>\n<li>进程 1：0~1G（第 1 段）</li>\n<li>进程 2：1~3G（第 2 段）</li>\n<li>进程 3：3~4.5G（第 3 段）</li>\n<li>进程 4：4.5~5G（第 4 段）</li>\n</ul>\n<p>此时，我们关闭了进程 1 和进程 4，则第 1 段和第 4 段的内存会被释放，空闲物理内存还有 1.5G。由于这 1.5G 物理内存并不是连续的，导致没办法将空闲的物理内存分配给一个需要 1.5G 物理内存的进程。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/segment-external-memory-fragmentation.png\" alt=\"分段机制导致外部内存碎片\"></p>\n<h3>分页机制</h3>\n<p><strong>分页机制（Paging）</strong> 把主存（物理内存）分为连续等长的物理页，应用程序的虚拟地址空间划也被分为连续等长的虚拟页。现代操作系统广泛采用分页机制。</p>\n<p><strong>注意：这里的页是连续等长的，不同于分段机制下不同长度的段。</strong></p>\n<p>在分页机制下，应用程序虚拟地址空间中的任意虚拟页可以被映射到物理内存中的任意物理页上，因此可以实现物理内存资源的离散分配。分页机制按照固定页大小分配物理内存，使得物理内存资源易于管理，可有效避免分段机制中外部内存碎片的问题。</p>\n<h4>页表有什么用？地址翻译过程是怎样的？</h4>\n<p>分页管理通过 <strong>页表（Page Table）</strong> 映射虚拟地址和物理地址。我这里画了一张基于单级页表进行地址翻译的示意图。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/page-table.png\" alt=\"单级页表\"></p>\n<p>在分页机制下，每个进程都会有一个对应的页表。</p>\n<p>分页机制下的虚拟地址由两部分组成：</p>\n<ul>\n<li><strong>页号</strong>：通过虚拟页号可以从页表中取出对应的物理页号；</li>\n<li><strong>页内偏移量</strong>：物理页起始地址+页内偏移量=物理内存地址。</li>\n</ul>\n<p>具体的地址翻译过程如下：</p>\n<ol>\n<li>MMU 首先解析得到虚拟地址中的虚拟页号；</li>\n<li>通过虚拟页号去该应用程序的页表中取出对应的物理页号（找到对应的页表项）；</li>\n<li>用该物理页号对应的物理页起始地址（物理地址）加上虚拟地址中的页内偏移量得到最终的物理地址。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/paging-virtual-address-composition.png\" alt=\"分页机制下的地址翻译过程\"></p>\n<p>页表中还存有诸如访问标志（标识该页面有没有被访问过）、脏数据标识位等信息。</p>\n<p><strong>通过虚拟页号一定要找到对应的物理页号吗？找到了物理页号得到最终的物理地址后对应的物理页一定存在吗？</strong></p>\n<p>不一定！可能会存在 <strong>页缺失</strong> 。也就是说，物理内存中没有对应的物理页或者物理内存中有对应的物理页但虚拟页还未和物理页建立映射（对应的页表项不存在）。关于页缺失的内容，后面会详细介绍到。</p>\n<h4>单级页表有什么问题？为什么需要多级页表？</h4>\n<p>以 32 位的环境为例，虚拟地址空间范围共有 2^32（4G）。假设 一个页的大小是 2^12（4KB），那页表项共有 4G / 4K = 2^20 个。每个页表项为一个地址，占用 4 字节，<code>(2^20 * 2^2) / (1024 * 1024)= 4MB</code>。也就是说一个程序啥都不干，页表大小就得占用 4M。</p>\n<p>系统运行的应用程序多起来的话，页表的开销还是非常大的。而且，绝大部分应用程序可能只能用到页表中的几项，其他的白白浪费了。</p>\n<p>为了解决这个问题，操作系统引入了 <strong>多级页表</strong> ，多级页表对应多个页表，每个页表与前一个页表相关联。32 位系统一般为二级页表，64 位系统一般为四级页表。</p>\n<p>这里以二级页表为例进行介绍：二级列表分为一级页表和二级页表。一级页表共有 1024 个页表项，一级页表又关联二级页表，二级页表同样共有 1024 个页表项。二级页表中的一级页表项是一对多的关系，二级页表按需加载（只会用到很少一部分二级页表），进而节省空间占用。</p>\n<p>假设只需要 2 个二级页表，那两级页表的内存占用情况为: 4KB（一级页表占用） + 4KB * 2（二级页表占用） = 12 KB。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/multilevel-page-table.png\" alt=\"多级页表\"></p>\n<p>多级页表属于时间换空间的典型场景，利用增加页表查询的次数减少页表占用的空间。</p>\n<h4>TLB 有什么用？使用 TLB 之后的地址翻译流程是怎样的？</h4>\n<p>为了提高虚拟地址到物理地址的转换速度，操作系统在 <strong>页表方案</strong> 基础之上引入了 <strong>转址旁路缓存(Translation Lookaside Buffer，TLB，也被称为快表)</strong> 。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/physical-virtual-address-translation-mmu.png\" alt=\"加入 TLB 之后的地址翻译\"></p>\n<p>在主流的 AArch64 和 x86-64 体系结构下，TLB 属于 (Memory Management Unit，内存管理单元) 内部的单元，本质上就是一块高速缓存（Cache），缓存了虚拟页号到物理页号的映射关系，你可以将其简单看作是存储着键（虚拟页号）值（物理页号）对的哈希表。</p>\n<p>使用 TLB 之后的地址翻译流程是这样的：</p>\n<ol>\n<li>用虚拟地址中的虚拟页号作为 key 去 TLB 中查询；</li>\n<li>如果能查到对应的物理页的话，就不用再查询页表了，这种情况称为 TLB 命中（TLB hit)。</li>\n<li>如果不能查到对应的物理页的话，还是需要去查询主存中的页表，同时将页表中的该映射表项添加到 TLB 中，这种情况称为 TLB 未命中（TLB miss)。</li>\n<li>当 TLB 填满后，又要登记新页时，就按照一定的淘汰策略淘汰掉快表中的一个页。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/page-table-tlb.png\" alt=\"使用 TLB 之后的地址翻译流程\"></p>\n<p>由于页表也在主存中，因此在没有 TLB 之前，每次读写内存数据时 CPU 要访问两次主存。有了 TLB 之后，对于存在于 TLB 中的页表数据只需要访问一次主存即可。</p>\n<p>TLB 的设计思想非常简单，但命中率往往非常高，效果很好。这就是因为被频繁访问的页就是其中的很小一部分。</p>\n<p>看完了之后你会发现快表和我们平时经常在开发系统中使用的缓存（比如 Redis）很像，的确是这样的，操作系统中的很多思想、很多经典的算法，你都可以在我们日常开发使用的各种工具或者框架中找到它们的影子。</p>\n<h4>换页机制有什么用？</h4>\n<p>换页机制的思想是当物理内存不够用的时候，操作系统选择将一些物理页的内容放到磁盘上去，等要用到的时候再将它们读取到物理内存中。也就是说，换页机制利用磁盘这种较低廉的存储设备扩展的物理内存。</p>\n<p>这也就解释了一个日常使用电脑常见的问题：为什么操作系统中所有进程运行所需的物理内存即使比真实的物理内存要大一些，这些进程也是可以正常运行的，只是运行速度会变慢。</p>\n<p>这同样是一种时间换空间的策略，你用 CPU 的计算时间，页的调入调出花费的时间，换来了一个虚拟的更大的物理内存空间来支持程序的运行。</p>\n<h4>什么是页缺失？</h4>\n<p>根据维基百科:</p>\n<blockquote>\n<p>页缺失（Page Fault，又名硬错误、硬中断、分页错误、寻页缺失、缺页中断、页故障等）指的是当软件试图访问已映射在虚拟地址空间中，但是目前并未被加载在物理内存中的一个分页时，由 MMU 所发出的中断。</p>\n</blockquote>\n<p>常见的页缺失有下面这两种：</p>\n<ul>\n<li><strong>硬性页缺失（Hard Page Fault）</strong>：物理内存中没有对应的物理页。于是，Page Fault Handler 会指示 CPU 从已经打开的磁盘文件中读取相应的内容到物理内存，而后交由 MMU 建立相应的虚拟页和物理页的映射关系。</li>\n<li><strong>软性页缺失（Soft Page Fault）</strong>：物理内存中有对应的物理页，但虚拟页还未和物理页建立映射。于是，Page Fault Handler 会指示 MMU 建立相应的虚拟页和物理页的映射关系。</li>\n</ul>\n<p>发生上面这两种缺页错误的时候，应用程序访问的是有效的物理内存，只是出现了物理页缺失或者虚拟页和物理页的映射关系未建立的问题。如果应用程序访问的是无效的物理内存的话，还会出现 <strong>无效缺页错误（Invalid Page Fault）</strong> 。</p>\n<h4>常见的页面置换算法有哪些?</h4>\n<p>当发生硬性页缺失时，如果物理内存中没有空闲的物理页面可用的话。操作系统就必须将物理内存中的一个物理页淘汰出去，这样就可以腾出空间来加载新的页面了。</p>\n<p>用来选择淘汰哪一个物理页的规则叫做 <strong>页面置换算法</strong> ，我们可以把页面置换算法看成是淘汰物物理页的规则。</p>\n<p>页缺失太频繁的发生会非常影响性能，一个好的页面置换算法应该是可以减少页缺失出现的次数。</p>\n<p>常见的页面置换算法有下面这 5 种（其他还有很多页面置换算法都是基于这些算法改进得来的）：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/image-20230409113009139.png\" alt=\"常见的页面置换算法\"></p>\n<ol>\n<li><strong>最佳页面置换算法（OPT，Optimal）</strong>：优先选择淘汰的页面是以后永不使用的，或者是在最长时间内不再被访问的页面，这样可以保证获得最低的缺页率。但由于人们目前无法预知进程在内存下的若干页面中哪个是未来最长时间内不再被访问的，因而该算法无法实现，只是理论最优的页面置换算法，可以作为衡量其他置换算法优劣的标准。</li>\n<li><strong>先进先出页面置换算法（FIFO，First In First Out）</strong> : 最简单的一种页面置换算法，总是淘汰最先进入内存的页面，即选择在内存中驻留时间最久的页面进行淘汰。该算法易于实现和理解，一般只需要通过一个 FIFO 队列即可满足需求。不过，它的性能并不是很好。</li>\n<li><strong>最近最久未使用页面置换算法（LRU ，Least Recently Used）</strong>：LRU 算法赋予每个页面一个访问字段，用来记录一个页面自上次被访问以来所经历的时间 T，当须淘汰一个页面时，选择现有页面中其 T 值最大的，即最近最久未使用的页面予以淘汰。LRU 算法是根据各页之前的访问情况来实现，因此是易于实现的。OPT 算法是根据各页未来的访问情况来实现，因此是不可实现的。</li>\n<li><strong>最少使用页面置换算法（LFU，Least Frequently Used）</strong> : 和 LRU 算法比较像，不过该置换算法选择的是之前一段时间内使用最少的页面作为淘汰页。</li>\n<li><strong>时钟页面置换算法（Clock）</strong>：可以认为是一种最近未使用算法，即逐出的页面都是最近没有使用的那个。</li>\n</ol>\n<p><strong>FIFO 页面置换算法性能为何不好？</strong></p>\n<p>主要原因主要有二：</p>\n<ol>\n<li><strong>经常访问或者需要长期存在的页面会被频繁调入调出</strong>：较早调入的页往往是经常被访问或者需要长期存在的页，这些页会被反复调入和调出。</li>\n<li><strong>存在 Belady 现象</strong>：被置换的页面并不是进程不会访问的，有时就会出现分配的页面数增多但缺页率反而提高的异常现象。出现该异常的原因是因为 FIFO 算法只考虑了页面进入内存的顺序，而没有考虑页面访问的频率和紧迫性。</li>\n</ol>\n<p><strong>哪一种页面置换算法实际用的比较多？</strong></p>\n<p>LRU 算法是实际使用中应用的比较多，也被认为是最接近 OPT 的页面置换算法。</p>\n<p>不过，需要注意的是，实际应用中这些算法会被做一些改进，就比如 InnoDB Buffer Pool（ InnoDB 缓冲池，MySQL 数据库中用于管理缓存页面的机制）就改进了传统的 LRU 算法，使用了一种称为&quot;Adaptive LRU&quot;的算法（同时结合了 LRU 和 LFU 算法的思想）。</p>\n<h3>分页机制和分段机制有哪些共同点和区别？</h3>\n<p><strong>共同点</strong>：</p>\n<ul>\n<li>都是非连续内存管理的方式。</li>\n<li>都采用了地址映射的方法，将虚拟地址映射到物理地址，以实现对内存的管理和保护。</li>\n</ul>\n<p><strong>区别</strong>：</p>\n<ul>\n<li>分页机制以页面为单位进行内存管理，而分段机制以段为单位进行内存管理。页的大小是固定的，由操作系统决定，通常为 2 的幂次方。而段的大小不固定，取决于我们当前运行的程序。</li>\n<li>页是物理单位，即操作系统将物理内存划分成固定大小的页面，每个页面的大小通常是 2 的幂次方，例如 4KB、8KB 等等。而段则是逻辑单位，是为了满足程序对内存空间的逻辑需求而设计的，通常根据程序中数据和代码的逻辑结构来划分。</li>\n<li>分段机制容易出现外部内存碎片，即在段与段之间留下碎片空间(不足以映射给虚拟地址空间中的段)。分页机制解决了外部内存碎片的问题，但仍然可能会出现内部内存碎片。</li>\n<li>分页机制采用了页表来完成虚拟地址到物理地址的映射，页表通过一级页表和二级页表来实现多级映射；而分段机制则采用了段表来完成虚拟地址到物理地址的映射，每个段表项中记录了该段的起始地址和长度信息。</li>\n<li>分页机制对程序没有任何要求，程序只需要按照虚拟地址进行访问即可；而分段机制需要程序员将程序分为多个段，并且显式地使用段寄存器来访问不同的段。</li>\n</ul>\n<h3>段页机制</h3>\n<p>结合了段式管理和页式管理的一种内存管理机制。程序视角中，内存被划分为多个逻辑段，每个逻辑段进一步被划分为固定大小的页。</p>\n<p>在段页式机制下，地址翻译的过程分为两个步骤：</p>\n<ol>\n<li><strong>段式地址映射（虚拟地址 → 线性地址）：</strong>\n<ul>\n<li>虚拟地址 = 段选择符（段号）+ 段内偏移。</li>\n<li>根据段号查段表，找到段基址，加上段内偏移得到线性地址。</li>\n</ul>\n</li>\n<li><strong>页式地址映射（线性地址 → 物理地址）：</strong>\n<ul>\n<li>线性地址 = 页号 + 页内偏移。</li>\n<li>根据页号查页表，找到物理页框号，加上页内偏移得到物理地址。</li>\n</ul>\n</li>\n</ol>\n<h3>局部性原理</h3>\n<p>要想更好地理解虚拟内存技术，必须要知道计算机中著名的 <strong>局部性原理（Locality Principle）</strong>。另外，局部性原理既适用于程序结构，也适用于数据结构，是非常重要的一个概念。</p>\n<p>局部性原理是指在程序执行过程中，数据和指令的访问存在一定的空间和时间上的局部性特点。其中，时间局部性是指一个数据项或指令在一段时间内被反复使用的特点，空间局部性是指一个数据项或指令在一段时间内与其相邻的数据项或指令被反复使用的特点。</p>\n<p>在分页机制中，页表的作用是将虚拟地址转换为物理地址，从而完成内存访问。在这个过程中，局部性原理的作用体现在两个方面：</p>\n<ul>\n<li><strong>时间局部性</strong>：由于程序中存在一定的循环或者重复操作，因此会反复访问同一个页或一些特定的页，这就体现了时间局部性的特点。为了利用时间局部性，分页机制中通常采用缓存机制来提高页面的命中率，即将最近访问过的一些页放入缓存中，如果下一次访问的页已经在缓存中，就不需要再次访问内存，而是直接从缓存中读取。</li>\n<li><strong>空间局部性</strong>：由于程序中数据和指令的访问通常是具有一定的空间连续性的，因此当访问某个页时，往往会顺带访问其相邻的一些页。为了利用空间局部性，分页机制中通常采用预取技术来预先将相邻的一些页读入内存缓存中，以便在未来访问时能够直接使用，从而提高访问速度。</li>\n</ul>\n<p>总之，局部性原理是计算机体系结构设计的重要原则之一，也是许多优化算法的基础。在分页机制中，利用时间局部性和空间局部性，采用缓存和预取技术，可以提高页面的命中率，从而提高内存访问效率</p>\n<h2>文件系统</h2>\n<h3>文件系统主要做了什么？</h3>\n<p>文件系统主要负责管理和组织计算机存储设备上的文件和目录，其功能包括以下几个方面：</p>\n<ol>\n<li><strong>存储管理</strong>：将文件数据存储到物理存储介质中，并且管理空间分配，以确保每个文件都有足够的空间存储，并避免文件之间发生冲突。</li>\n<li><strong>文件管理</strong>：文件的创建、删除、移动、重命名、压缩、加密、共享等等。</li>\n<li><strong>目录管理</strong>：目录的创建、删除、移动、重命名等等。</li>\n<li><strong>文件访问控制</strong>：管理不同用户或进程对文件的访问权限，以确保用户只能访问其被授权访问的文件，以保证文件的安全性和保密性。</li>\n</ol>\n<h3>硬链接和软链接有什么区别？</h3>\n<p>在 Linux/类 Unix 系统上，文件链接（File Link）是一种特殊的文件类型，可以在文件系统中指向另一个文件。常见的文件链接类型有两种：</p>\n<p><strong>1、硬链接（Hard Link）</strong></p>\n<ul>\n<li>在 Linux/类 Unix 文件系统中，每个文件和目录都有一个唯一的索引节点（inode）号，用来标识该文件或目录。硬链接通过 inode 节点号建立连接，硬链接和源文件的 inode 节点号相同，两者对文件系统来说是完全平等的（可以看作是互为硬链接，源头是同一份文件），删除其中任何一个对另外一个没有影响，可以通过给文件设置硬链接文件来防止重要文件被误删。</li>\n<li>只有删除了源文件和所有对应的硬链接文件，该文件才会被真正删除。</li>\n<li>硬链接具有一些限制，不能对目录以及不存在的文件创建硬链接，并且，硬链接也不能跨越文件系统。</li>\n<li><code>ln</code> 命令用于创建硬链接。</li>\n</ul>\n<p><strong>2、软链接（Symbolic Link 或 Symlink）</strong></p>\n<ul>\n<li>软链接和源文件的 inode 节点号不同，而是指向一个文件路径。</li>\n<li>源文件删除后，软链接依然存在，但是指向的是一个无效的文件路径。</li>\n<li>软连接类似于 Windows 系统中的快捷方式。</li>\n<li>不同于硬链接，可以对目录或者不存在的文件创建软链接，并且，软链接可以跨越文件系统。</li>\n<li><code>ln -s</code> 命令用于创建软链接。</li>\n</ul>\n<h3>硬链接为什么不能跨文件系统？</h3>\n<p>我们之前提到过，硬链接是通过 inode 节点号建立连接的，而硬链接和源文件共享相同的 inode 节点号。</p>\n<p>然而，每个文件系统都有自己的独立 inode 表，且每个 inode 表只维护该文件系统内的 inode。如果在不同的文件系统之间创建硬链接，可能会导致 inode 节点号冲突的问题，即目标文件的 inode 节点号已经在该文件系统中被使用。</p>\n<h3>提高文件系统性能的方式有哪些？</h3>\n<ul>\n<li><strong>优化硬件</strong>：使用高速硬件设备（如 SSD、NVMe）替代传统的机械硬盘，使用 RAID（Redundant Array of Independent Disks）等技术提高磁盘性能。</li>\n<li><strong>选择合适的文件系统选型</strong>：不同的文件系统具有不同的特性，对于不同的应用场景选择合适的文件系统可以提高系统性能。</li>\n<li><strong>运用缓存</strong>：访问磁盘的效率比较低，可以运用缓存来减少磁盘的访问次数。不过，需要注意缓存命中率，缓存命中率过低的话，效果太差。</li>\n<li><strong>避免磁盘过度使用</strong>：注意磁盘的使用率，避免将磁盘用满，尽量留一些剩余空间，以免对文件系统的性能产生负面影响。</li>\n<li><strong>对磁盘进行合理的分区</strong>：合理的磁盘分区方案，能够使文件系统在不同的区域存储文件，从而减少文件碎片，提高文件读写性能。</li>\n</ul>\n<h3>常见的磁盘调度算法有哪些？</h3>\n<p>磁盘调度算法是操作系统中对磁盘访问请求进行排序和调度的算法，其目的是提高磁盘的访问效率。</p>\n<p>一次磁盘读写操作的时间由磁盘寻道/寻找时间、延迟时间和传输时间决定。磁盘调度算法可以通过改变到达磁盘请求的处理顺序，减少磁盘寻道时间和延迟时间。</p>\n<p>常见的磁盘调度算法有下面这 6 种（其他还有很多磁盘调度算法都是基于这些算法改进得来的）：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/disk-scheduling-algorithms.png\" alt=\"常见的磁盘调度算法\"></p>\n<ol>\n<li><strong>先来先服务算法（First-Come First-Served，FCFS）</strong>：按照请求到达磁盘调度器的顺序进行处理，先到达的请求的先被服务。FCFS 算法实现起来比较简单，不存在算法开销。不过，由于没有考虑磁头移动的路径和方向，平均寻道时间较长。同时，该算法容易出现饥饿问题，即一些后到的磁盘请求可能需要等待很长时间才能得到服务。</li>\n<li><strong>最短寻道时间优先算法（Shortest Seek Time First，SSTF）</strong>：也被称为最佳服务优先（Shortest Service Time First，SSTF）算法，优先选择距离当前磁头位置最近的请求进行服务。SSTF 算法能够最小化磁头的寻道时间，但容易出现饥饿问题，即磁头附近的请求不断被服务，远离磁头的请求长时间得不到响应。实际应用中，需要优化一下该算法的实现，避免出现饥饿问题。</li>\n<li><strong>扫描算法（SCAN）</strong>：也被称为电梯（Elevator）算法，基本思想和电梯非常类似。磁头沿着一个方向扫描磁盘，如果经过的磁道有请求就处理，直到到达磁盘的边界，然后改变移动方向，依此往复。SCAN 算法能够保证所有的请求得到服务，解决了饥饿问题。但是，如果磁头从一个方向刚扫描完，请求才到的话。这个请求就需要等到磁头从相反方向过来之后才能得到处理。</li>\n<li><strong>循环扫描算法（Circular Scan，C-SCAN）</strong>：SCAN 算法的变体，只在磁盘的一侧进行扫描，并且只按照一个方向扫描，直到到达磁盘边界，然后回到磁盘起点，重新开始循环。</li>\n<li><strong>边扫描边观察算法（LOOK）</strong>：SCAN 算法中磁头到了磁盘的边界才改变移动方向，这样可能会做很多无用功，因为磁头移动方向上可能已经没有请求需要处理了。LOOK 算法对 SCAN 算法进行了改进，如果磁头移动方向上已经没有别的请求，就可以立即改变磁头移动方向，依此往复。也就是边扫描边观察指定方向上还有无请求，因此叫 LOOK。</li>\n<li><strong>均衡循环扫描算法（C-LOOK）</strong>：C-SCAN 只有到达磁盘边界时才能改变磁头移动方向，并且磁头返回时也需要返回到磁盘起点，这样可能会做很多无用功。C-LOOK 算法对 C-SCAN 算法进行了改进，如果磁头移动的方向上已经没有磁道访问请求了，就可以立即让磁头返回，并且磁头只需要返回到有磁道访问请求的位置即可。</li>\n</ol>\n<h2>参考</h2>\n<ul>\n<li>《计算机操作系统—汤小丹》第四版</li>\n<li>《深入理解计算机系统》</li>\n<li>《重学操作系统》</li>\n<li>《现代操作系统原理与实现》</li>\n<li>王道考研操作系统知识点整理：<a href=\"https://wizardforcel.gitbooks.io/wangdaokaoyan-os/content/13.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://wizardforcel.gitbooks.io/wangdaokaoyan-os/content/13.html</a></li>\n<li>内存管理之伙伴系统与 SLAB：<a href=\"https://blog.csdn.net/qq_44272681/article/details/124199068\" target=\"_blank\" rel=\"noopener noreferrer\">https://blog.csdn.net/qq_44272681/article/details/124199068</a></li>\n<li>为什么 Linux 需要虚拟内存：<a href=\"https://draveness.me/whys-the-design-os-virtual-memory/\" target=\"_blank\" rel=\"noopener noreferrer\">https://draveness.me/whys-the-design-os-virtual-memory/</a></li>\n<li>程序员的自我修养（七）：内存缺页错误：<a href=\"https://liam.page/2017/09/01/page-fault/\" target=\"_blank\" rel=\"noopener noreferrer\">https://liam.page/2017/09/01/page-fault/</a></li>\n<li>虚拟内存的那点事儿：<a href=\"https://juejin.cn/post/6844903507594575886\" target=\"_blank\" rel=\"noopener noreferrer\">https://juejin.cn/post/6844903507594575886</a></li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/cs-basics/operating-system/memory-management-roles.png",
      "date_published": "2023-04-09T12:09:19.000Z",
      "date_modified": "2026-03-10T15:16:51.000Z",
      "authors": [],
      "tags": [
        "计算机基础"
      ]
    },
    {
      "title": "Java 20 新特性概览",
      "url": "https://javaguide.cn/java/new-features/java20.html",
      "id": "https://javaguide.cn/java/new-features/java20.html",
      "summary": "总结 JDK 20 的语言与并发改动，延续虚拟线程与模式匹配相关增强。",
      "content_html": "<p>JDK 20 于 2023 年 3 月 21 日发布，非长期支持版本。</p>\n<p>根据开发计划，下一个 LTS 版本就是将于 2023 年 9 月发布的 JDK 21。</p>\n<p>JDK 20 共有 7 个新特性，这篇文章会挑选其中较为重要的一些新特性进行详细介绍：</p>\n<ul>\n<li><a href=\"https://openjdk.org/jeps/429\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 429: Scoped Values（作用域值）</a>（第一次孵化）</li>\n<li><a href=\"https://openjdk.org/jeps/432\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 432: Record Patterns（记录模式）</a>（第二次预览）</li>\n<li><a href=\"https://openjdk.org/jeps/433\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 433: Pattern Matching for switch（switch 模式匹配）</a>（第四次预览）</li>\n<li><a href=\"https://openjdk.org/jeps/434\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 434: Foreign Function &amp; Memory API（外部函数和内存 API）</a>（第二次预览）</li>\n<li><a href=\"https://openjdk.org/jeps/436\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 436: Virtual Threads（虚拟线程）</a>（第二次预览）</li>\n<li><a href=\"https://openjdk.org/jeps/437\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 437: Structured Concurrency（结构化并发）</a>（第二次孵化）</li>\n<li><a href=\"https://openjdk.org/jeps/438\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 438: Vector API（向量 API）</a>（第五次孵化）</li>\n</ul>\n<p>下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png\" alt=\" JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间\"></p>\n<h2>JEP 429: Scoped Values（作用域值，第一次孵化）</h2>\n<p>作用域值（Scoped Values）它可以在线程内和线程间共享不可变的数据，优于线程局部变量，尤其是在使用大量虚拟线程时。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">final</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ScopedValue</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;...></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> V </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ScopedValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// In some method</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ScopedValue</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">where</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(V, </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">           .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(() </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> { ... </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() ... </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">call</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> methods ... });</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// In a method called directly or indirectly from the lambda expression</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> V</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ...</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>作用域值允许在大型程序中的组件之间安全有效地共享数据，而无需求助于方法参数。</p>\n<p>关于作用域值的详细介绍，推荐阅读<a href=\"https://www.happycoders.eu/java/scoped-values/\" target=\"_blank\" rel=\"noopener noreferrer\">作用域值常见问题解答</a>这篇文章。</p>\n<h2>JEP 432: Record Patterns（记录模式，第二次预览）</h2>\n<p>记录模式（Record Patterns） 可对 record 的值进行解构，也就是更方便地从记录类（Record Class）中提取数据。并且，还可以嵌套记录模式和类型模式结合使用，以实现强大的、声明性的和可组合的数据导航和处理形式。</p>\n<p>记录模式不能单独使用，而是要与 instanceof 或 switch 模式匹配一同使用。</p>\n<p>先以 instanceof 为例简单演示一下。</p>\n<p>简单定义一个记录类：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">record</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Shape</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> type</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> unit)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">{}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>没有记录模式之前：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Shape</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> circle </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Shape</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Circle\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (circle </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Shape</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> shape) {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">  System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Area of \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> shape</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">type</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \" is : \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Math</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">PI</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> *</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Math</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">pow</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">shape</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(), </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>有了记录模式之后：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Shape</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> circle </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Shape</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Circle\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (circle </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Shape</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> type</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> unit)) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">  System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Area of \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> type </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \" is : \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Math</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">PI</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> *</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Math</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">pow</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(unit, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">));</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>再看看记录模式与 switch 的配合使用。</p>\n<p>定义一些类：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">interface</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Shape</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">record</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Circle</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">double</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> radius) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Shape</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">record</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Square</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">double</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> side) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Shape</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">record</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Rectangle</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">double</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> double</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> width) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Shape</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>没有记录模式之前：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Shape</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> shape </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Circle</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">switch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (shape) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Circle</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> c</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"The shape is Circle with area: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Math</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">PI</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> *</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> c</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">radius</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> c</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">radius</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        break</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Square</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> s</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"The shape is Square with area: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> s</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">side</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> s</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">side</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        break</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Rectangle</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> r</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"The shape is Rectangle with area: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> r</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> r</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">width</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        break</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    default:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Unknown Shape\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        break</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>有了记录模式之后：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Shape</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> shape </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Circle</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">switch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(shape) {</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  case</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Circle</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">double</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> radius)</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"The shape is Circle with area: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Math</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">PI</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> *</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> radius </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> radius);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    break</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  case</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Square</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">double</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> side)</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"The shape is Square with area: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> side </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> side);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    break</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  case</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> Rectangle</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">double</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> double</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> width)</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"The shape is Rectangle with area: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> length </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> width);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    break</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  default:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Unknown Shape\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    break</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>记录模式可以避免不必要的转换，使得代码更简洁易读。而且，用了记录模式后不必再担心 <code>null</code> 或者 <code>NullPointerException</code>，代码更安全可靠。</p>\n<p>记录模式在 Java 19 进行了第一次预览， 由 <a href=\"https://openjdk.org/jeps/405\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 405</a> 提出。JDK 20 中是第二次预览，由 <a href=\"https://openjdk.org/jeps/432\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 432</a> 提出。这次的改进包括：</p>\n<ul>\n<li>添加对通用记录模式类型参数推断的支持，</li>\n<li>添加对记录模式的支持以出现在增强语句的标题中<code>for</code></li>\n<li>删除对命名记录模式的支持。</li>\n</ul>\n<p><strong>注意</strong>：不要把记录模式和 <a href=\"/java/new-features/java16.html\" target=\"_blank\">JDK16</a> 正式引入的记录类搞混了。</p>\n<h2>JEP 433: Pattern Matching for switch（switch 模式匹配，第四次预览）</h2>\n<p>正如 <code>instanceof</code> 一样， <code>switch</code> 也紧跟着增加了类型匹配自动转换功能。</p>\n<p><code>instanceof</code> 代码示例：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// Old code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> String) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> s </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (String)o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    ...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> use</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> s </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// New code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> s) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    ...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> use</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> s </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>switch</code> 代码示例：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// Old code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> formatter</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> o) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> formatted </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"unknown\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        formatted </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"int %d\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> l) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        formatted </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"long %d\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, l);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Double</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> d) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        formatted </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"double %f\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, d);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> s) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        formatted </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"String %s\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, s);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> formatted</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// New code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> formatterPatternSwitch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> o) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> switch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"int %d\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> l    </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"long %d\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, l);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Double</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> d  </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"double %f\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, d);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> s  </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"String %s\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, s);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        default</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        -></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>switch</code> 模式匹配分别在 Java17、Java18、Java19 中进行了预览，Java20 是第四次预览了。每一次的预览基本都会有一些小改进，这里就不细提了。</p>\n<h2>JEP 434: Foreign Function &amp; Memory API（外部函数和内存 API，第二次预览）</h2>\n<p>Java 程序可以通过该 API 与 Java 运行时之外的代码和数据进行互操作。通过高效地调用外部函数（即 JVM 之外的代码）和安全地访问外部内存（即不受 JVM 管理的内存），该 API 使 Java 程序能够调用本机库并处理本机数据，而不会像 JNI 那样危险和脆弱。</p>\n<p>外部函数和内存 API 在 Java 17 中进行了第一轮孵化，由 <a href=\"https://openjdk.java.net/jeps/412\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 412</a> 提出。Java 18 中进行了第二次孵化，由<a href=\"https://openjdk.org/jeps/419\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 419</a> 提出。Java 19 中是第一次预览，由 <a href=\"https://openjdk.org/jeps/424\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 424</a> 提出。</p>\n<p>JDK 20 中是第二次预览，由 <a href=\"https://openjdk.org/jeps/434\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 434</a> 提出，这次的改进包括：</p>\n<ul>\n<li><code>MemorySegment</code> 和 <code>MemoryAddress</code> 抽象的统一</li>\n<li>增强的 <code>MemoryLayout</code> 层次结构</li>\n<li><code>MemorySession</code>拆分为<code>Arena</code>和<code>SegmentScope</code>，以促进跨维护边界的段共享。</li>\n</ul>\n<p>在 <a href=\"/java/new-features/java19.html\" target=\"_blank\">Java 19 新特性概览</a> 中，我有详细介绍到外部函数和内存 API，这里就不再做额外的介绍了。</p>\n<h2>JEP 436: Virtual Threads（虚拟线程，第二次预览）</h2>\n<p>虚拟线程（Virtual Thread）是 JDK 而不是 OS 实现的轻量级线程（Lightweight Process，LWP），由 JVM 调度。许多虚拟线程共享同一个操作系统线程，虚拟线程的数量可以远大于操作系统线程的数量。</p>\n<p>在引入虚拟线程之前，<code>java.lang.Thread</code> 包已经支持所谓的平台线程，也就是没有虚拟线程之前，我们一直使用的线程。JVM 调度程序通过平台线程（载体线程）来管理虚拟线程，一个平台线程可以在不同的时间执行不同的虚拟线程（多个虚拟线程挂载在一个平台线程上），当虚拟线程被阻塞或等待时，平台线程可以切换到执行另一个虚拟线程。</p>\n<p>虚拟线程、平台线程和系统内核线程的关系图如下所示（图源：<a href=\"https://medium.com/javarevisited/how-to-use-java-19-virtual-threads-c16a32bad5f7\" target=\"_blank\" rel=\"noopener noreferrer\">How to Use Java 19 Virtual Threads</a>）：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/virtual-threads-platform-threads-kernel-threads-relationship.png\" alt=\"虚拟线程、平台线程和系统内核线程的关系\"></p>\n<p>关于平台线程和系统内核线程的对应关系多提一点：在 Windows 和 Linux 等主流操作系统中，Java 线程采用的是一对一的线程模型，也就是一个平台线程对应一个系统内核线程。Solaris 系统是一个特例，HotSpot VM 在 Solaris 上支持多对多和一对一。具体可以参考 R 大的回答: <a href=\"https://www.zhihu.com/question/23096638/answer/29617153\" target=\"_blank\" rel=\"noopener noreferrer\">JVM 中的线程模型是用户级的么？</a>。</p>\n<p>相比较于平台线程来说，虚拟线程是廉价且轻量级的，使用完后立即被销毁，因此它们不需要被重用或池化，每个任务可以有自己专属的虚拟线程来运行。虚拟线程暂停和恢复来实现线程之间的切换，避免了上下文切换的额外耗费，兼顾了多线程的优点，简化了高并发程序的复杂，可以有效减少编写、维护和观察高吞吐量并发应用程序的工作量。</p>\n<p>虚拟线程在其他多线程语言中已经被证实是十分有用的，比如 Go 中的 Goroutine、Erlang 中的进程。</p>\n<p>知乎有一个关于 Java 19 虚拟线程的讨论，感兴趣的可以去看看：<a href=\"https://www.zhihu.com/question/536743167\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.zhihu.com/question/536743167</a> 。</p>\n<p>Java 虚拟线程的详细解读和原理可以看下面这几篇文章：</p>\n<ul>\n<li><a href=\"https://javaguide.cn/java/concurrent/virtual-thread.html\" target=\"_blank\" rel=\"noopener noreferrer\">虚拟线程极简入门</a></li>\n<li><a href=\"https://mp.weixin.qq.com/s/yyApBXxpXxVwttr01Hld6Q\" target=\"_blank\" rel=\"noopener noreferrer\">Java19 正式 GA！看虚拟线程如何大幅提高系统吞吐量</a></li>\n<li><a href=\"https://www.cnblogs.com/throwable/p/16758997.html\" target=\"_blank\" rel=\"noopener noreferrer\">虚拟线程 - VirtualThread 源码透视</a></li>\n</ul>\n<p>虚拟线程在 Java 19 中进行了第一次预览，由<a href=\"https://openjdk.org/jeps/425\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 425</a>提出。JDK 20 中是第二次预览，做了一些细微变化，这里就不细提了。</p>\n<p>最后，我们来看一下四种创建虚拟线程的方法：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 1、通过 Thread.ofVirtual() 创建</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Runnable</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> fn </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> () </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // your code here</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> thread </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">ofVirtual</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(fn)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                      .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 2、通过 Thread.startVirtualThread() 创建</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> thread </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">startVirtualThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(() </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // your code here</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">});</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 3、通过 Executors.newVirtualThreadPerTaskExecutor() 创建</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> executorService </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Executors</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newVirtualThreadPerTaskExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">executorService</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">submit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(() </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // your code here</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">});</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CustomThread</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> implements</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Runnable</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"CustomThread run\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//4、通过 ThreadFactory 创建</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">CustomThread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> customThread </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> CustomThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 获取线程工厂类</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">ThreadFactory</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> factory </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">ofVirtual</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">factory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 创建虚拟线程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> thread </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> factory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(customThread);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 启动线程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>通过上述列举的 4 种创建虚拟线程的方式可以看出，官方为了降低虚拟线程的门槛，尽力复用原有的 <code>Thread</code> 线程类，这样可以平滑的过渡到虚拟线程的使用。</p>\n<h2>JEP 437: Structured Concurrency（结构化并发，第二次孵化）</h2>\n<p>Java 19 引入了结构化并发，一种多线程编程方法，目的是为了通过结构化并发 API 来简化多线程编程，并不是为了取代<code>java.util.concurrent</code>，目前处于孵化器阶段。</p>\n<p>结构化并发将不同线程中运行的多个任务视为单个工作单元，从而简化错误处理、提高可靠性并增强可观察性。也就是说，结构化并发保留了单线程代码的可读性、可维护性和可观察性。</p>\n<p>结构化并发的基本 API 是<a href=\"https://download.java.net/java/early_access/loom/docs/api/jdk.incubator.concurrent/jdk/incubator/concurrent/StructuredTaskScope.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>StructuredTaskScope</code></a>。<code>StructuredTaskScope</code> 支持将任务拆分为多个并发子任务，在它们自己的线程中执行，并且子任务必须在主任务继续之前完成。</p>\n<p><code>StructuredTaskScope</code> 的基本用法如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> scope </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> StructuredTaskScope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 使用fork方法派生线程来执行子任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Future</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> future1 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fork</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task1);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Future</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> future2 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fork</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task2);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 等待线程完成</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">join</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 结果的处理可能包括处理或重新抛出异常</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        ...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> process</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> results</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">exceptions </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// close</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>结构化并发非常适合虚拟线程，虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程，从而允许非常多的虚拟线程。</p>\n<p>JDK 20 中对结构化并发唯一变化是更新为支持在任务范围内创建的线程<code>StructuredTaskScope</code>继承范围值 这简化了跨线程共享不可变数据，详见<a href=\"https://openjdk.org/jeps/429\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 429</a>。</p>\n<h2>JEP 438: Vector API（向量 API，第五次孵化）</h2>\n<p>向量计算由对向量的一系列操作组成。向量 API 用来表达向量计算，该计算可以在运行时可靠地编译为支持的 CPU 架构上的最佳向量指令，从而实现优于等效标量计算的性能。</p>\n<p>向量 API 的目标是为用户提供简洁易用且与平台无关的表达范围广泛的向量计算。</p>\n<p>向量（Vector） API 最初由 <a href=\"https://openjdk.java.net/jeps/338\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 338</a> 提出，并作为<a href=\"http://openjdk.java.net/jeps/11\" target=\"_blank\" rel=\"noopener noreferrer\">孵化 API</a>集成到 Java 16 中。第二轮孵化由 <a href=\"https://openjdk.java.net/jeps/414\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 414</a> 提出并集成到 Java 17 中，第三轮孵化由 <a href=\"https://openjdk.java.net/jeps/417\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 417</a> 提出并集成到 Java 18 中，第四轮由 <a href=\"https://openjdk.java.net/jeps/426\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 426</a> 提出并集成到了 Java 19 中。</p>\n<p>Java20 的这次孵化基本没有改变向量 API ，只是进行了一些错误修复和性能增强，详见 <a href=\"https://openjdk.org/jeps/438\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 438</a>。</p>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png",
      "date_published": "2023-03-27T07:25:00.000Z",
      "date_modified": "2026-01-27T07:16:00.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "从校招入职腾讯的四年工作总结",
      "url": "https://javaguide.cn/high-quality-technical-articles/personal-experience/four-year-work-in-tencent-summary.html",
      "id": "https://javaguide.cn/high-quality-technical-articles/personal-experience/four-year-work-in-tencent-summary.html",
      "summary": "从校招入职腾讯的四年工作总结：围绕技术知识与面试总结梳理关键概念、常见问题与实践要点，帮助你高效学习与备战面试。",
      "content_html": "<p>程序员是一个流动性很大的职业，经常会有新面孔的到来，也经常会有老面孔的离开，有主动离开的，也有被动离职的。</p>\n<p>再加上这几年卷得厉害，做的事更多了，拿到的却更少了，互联网好像也没有那么香了。</p>\n<p>人来人往，变动无常的状态，其实也早已习惯。</p>\n<p>打工人的唯一出路，无外乎精进自己的专业技能，提升自己的核心竞争力，这样无论有什么变动，走到哪里，都能有口饭吃。</p>\n<p>今天分享一位博主，校招入职腾讯，工作四年后，离开的故事。</p>\n<p>至于为什么离开，我也不清楚，可能是有其他更好的选择，或者是觉得当前的工作对自己的提升有限。</p>\n<p><strong>下文中的“我”，指这位作者本人。</strong></p>\n<blockquote>\n<p>原文地址：<a href=\"https://zhuanlan.zhihu.com/p/602517682\" target=\"_blank\" rel=\"noopener noreferrer\">https://zhuanlan.zhihu.com/p/602517682</a></p>\n</blockquote>\n<p>研究生毕业后， 一直在腾讯工作，不知不觉就过了四年。个人本身没有刻意总结的习惯，以前只顾着往前奔跑了，忘了停下来思考总结。记得看过一个职业规划文档，说的三年一个阶段，五年一个阶段的说法，现在恰巧是四年，同时又从腾讯离开，该做一个总结了。</p>\n<p>先对自己这四年做一个简单的评价吧：个人认为，没有完全的浪费和辜负这四年的光阴。为何要这么说了？因为我发现和别人对比，好像意义不大，比我混的好的人很多；比我混的差的人也不少。说到底，我只是一个普普通通的人，才不惊人，技不压众，接受自己的平凡，然后看自己做的，是否让自己满意就好。</p>\n<p>下面具体谈几点吧，我主要想聊下工作，绩效，EPC，嫡系看法，最后再谈下收获。</p>\n<h2>工作情况</h2>\n<p>我在腾讯内部没有转过岗，但是做过的项目也还是比较丰富的，包括：BUGLY、分布式调用链（Huskie)、众包系统（SOHO)，EPC 度量系统。其中一些是对外的，一些是内部系统，可能有些大家不知道。还是比较感谢这些项目经历，既有纯业务的系统，也有偏框架的系统，让我学到了不少知识。</p>\n<p>接下来，简单介绍一下每个项目吧，毕竟每一个项目都付出了很多心血的：</p>\n<p>BUGLY，这是一个终端 Crash 联网上报的系统，很多 APP 都接入了。Huskie，这是一个基于 zipkin 搭建的分布式调用链跟踪项目。SOHO，这是一个众包系统，主要是将数据标准和语音采集任务众包出去，让人家做。EPC 度量系统，这是研发效能度量系统，主要是度量研发效能情况的。这里我谈一下对于业务开发的理解和认识，很多人可能都跟我最开始一样，有一个疑惑，整天做业务开发如何成长？换句话说，就是说整天做 CRUD，如何成长？我开始也有这样的疑惑，后来我转变了观念。</p>\n<p>我觉得对于系统的复杂度，可以粗略的分为技术复杂度和业务复杂度，对于业务系统，就是业务复杂度高一些，对于框架系统就是技术复杂度偏高一些。解决这两种复杂度，都具有很大的挑战。</p>\n<p>此前做过的众包系统，就是各种业务逻辑，搞过去，搞过来，其实这就是业务复杂度高。为了解决这个问题，我们开始探索和实践领域驱动（DDD），确实带来了一些帮助，不至于系统那么混乱了。同时，我觉得这个过程中，自己对于 DDD 的感悟，对于我后来的项目系统划分和设计以及开发都带来了帮助。</p>\n<p>当然 DDD 不是银弹，我也不是吹嘘它有多好，只是了解了它后，有时候设计和开发时，能换一种思路。</p>\n<p>可以发现，其实平时咱们做业务，想做好，其实也没那么容易，如果可以多探索多实践，将一些好的方法或思想或架构引入进来，与个人和业务都会有有帮助。</p>\n<h2>绩效情况</h2>\n<p>我在腾讯工作四年，腾讯半年考核一次，一共考核八次，回想了下，四年来的绩效情况为:三星，三星，五星，三星，五星，四星，四星，三星。统计一下， 四五星占比刚好一半。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/640.png\" alt></p>\n<p>PS：还好以前有奖杯，不然一点念想都没了。(现在腾讯似乎不发了）</p>\n<p>印象比较深的是两次五星获得经历。第一次五星是工作的第二年，那一年是在做众包项目，因为项目本身难度不大，因此我把一些精力投入到了团队的基础建设中，帮团队搭建了 java 以及 golang 的项目脚手架，又做了几次中心技术分享，最终 Leader 觉得我表现比较突出，因此给了我五星。看来，主动一些，与个人与团队都是有好处的，最终也能获得一些回报。</p>\n<p>第二次五星，就是与 EPC 有关了。说一个搞笑的事，我也是后来才知道的，项目初期，总监去汇报时，给老板演示系统，加载了很久指标才刷出来，总监很不好意思的说正在优化；过了一段时间，又去汇报演示，结果又很尴尬的刷了很久才出来，总监无赖表示还是在优化。没想到，自己曾经让总监这么丢脸，哈哈。好吧，说一下结果，最终，我自己写了一个查询引擎替换了 Mondrian，之后再也没有出现那种尴尬的情况了。随之而来，也给了好绩效鼓励。做 EPC 度量项目，我觉得自己成长很大，比如抗压能力，当你从零到一搭建一个系统时，会有一个先扛住再优化的过程，此外如果你的项目很重要，尤其是数据相关，那么任何一点问题，都可能让你神经紧绷，得想尽办法降低风险和故障。此外，另一个不同的感受就是，以前得项目，我大多是开发者，而这个系统，我是 Owner 负责人，当你 Owner 一个系统时，你得时刻负责，同时还需要思考系统的规划和方向，此外还需要分配好需求和把控进度，角色体验跟以前完全不一样。</p>\n<h2>谈谈 EPC</h2>\n<p>很多人都骂 EPC，或者笑 EPC，作为度量平台核心开发者之一，我来谈谈客观的看法。</p>\n<p>其实 EPC 初衷是好的，希望通过全方位多维度的研效指标，来度量研发效能各环节的质量，进而反推业务，提升研发效能。然而，最终在实践的过程中，才发现，客观条件并不支持（工具还没建设好）；此外，一味的追求指标数据，使得下面的人想方设法让指标好看，最终违背了初衷。</p>\n<p>为什么，说 EPC 好了，其实如果你仔细了解下 EPC，你就会发现，他是一套相当完善且比较先进的指标度量体系。覆盖了需求，代码，缺陷，测试，持续集成，运营部署各个环节。</p>\n<p>此外，这个过程中，虽然一些人和一些业务做弊，但绝大多数业务还是做出了改变的，比如微视那边的人反馈是，以前的代码写的跟屎一样，当有了 EPC 后，代码质量好了很多。虽然最后微视还是亡了，但是大厦将倾，EPC 是救不了的，亡了也更不能怪 EPC。</p>\n<h2>谈谈嫡系</h2>\n<p>大家都说腾讯，嫡系文化盛行。但其实我觉得在那个公司都一样吧。这也符合事物的基本规律，人们只相信自己信任并熟悉的人。作为领导，你难道会把把重要的事情交给自己不熟悉的人吗？</p>\n<p>其实我也不知道我算不算嫡系，脉脉上有人问过”怎么知道自己算不算嫡系”，下面有一个回答，我觉得很妙：如果你不知道你是不是嫡系，那你就不是。哈哈，这么说来，我可能不是。</p>\n<p>但另一方面，后来我负责了团队内很重要的事情，应该是中心内都算很重要的事，我独自负责一个方向，直接向总监汇报，似乎又有点像。</p>\n<p>网上也有其他说法，一针见血，是不是嫡系，就看钱到不到位，这么说也有道理。我在 7 级时，就发了股票，自我感觉，还是不错的。我当时以为不出意外的话，我以后的钱途和发展是不是就会一帆风顺。不出意外就出了意外，第二年，EPC 不达预期，部门总经理和总监都被换了，中心来了一个新的总监。</p>\n<p>好吧，又要重新建立信任了。再到后来，是不是嫡系已经不重要了，因为大环境不好，又加上裁员，大家主动的被动的差不多都走了。</p>\n<p>总结一下，嫡系的存在，其实情有可原。怎么样成为嫡系了？其实我也不知道。不过，我觉得，与其思考怎么成为嫡系，不如思考怎么展现自己的价值和能力，当别人发现你的价值和能力了，那自然更多的机会就会给予你，有了机会，只要把握住了，那就有更多的福利了。</p>\n<h2>再谈收获</h2>\n<p>收获，什么叫做收获了？个人觉得无论是外在的物质，技能，职级；还是内在的感悟，认识，都算收获。</p>\n<p>先说一些可量化的吧，我觉得有:</p>\n<ul>\n<li>级别上，升上了九级，高级工程师。虽然大家都在说腾讯职级缩水，但是有没有高工的能力自己其实是知道的，我个人感觉，通过我这几年的努力，我算是达到了我当时认为的我需要在高工时达到的状态；</li>\n<li>绩效上，自我评价，个人不是一个特别卷的人，或者说不会为了卷而卷。但是，如果我认定我应该把它做好得，我的 Owner 意识，以及负责态度，我觉得还是可以的。最终在腾讯四年的绩效也还算过的去。再谈一些其他软技能方面:</li>\n</ul>\n<p><strong>1、文档能力</strong></p>\n<p>作为程序员，文档能力其实是一项很重要的能力。其实我也没觉得自己文档能力有多好，但是前后两任总监，都说我的文档不错，那看来，我可能在平均水准之上。</p>\n<p><strong>2、明确方向</strong></p>\n<p>最后，说一个更虚的，但是我觉得最有价值的收获: 我逐渐明确了，或者确定了以后的方向和路，那就是走数据开发。</p>\n<p>其实，找到并确定一个目标很难，身边有清晰目标和方向的人很少，大多数是迷茫的。</p>\n<p>前一段时间，跟人聊天，谈到职业规划，说是可以从两个角度思考：</p>\n<ul>\n<li>选一个业务方向，比如电商，广告，不断地积累业务领域知识和业务相关技能，随着经验的不断积累，最终你就是这个领域的专家。</li>\n<li>深入一个技术方向，不断钻研底层技术知识，这样就有希望成为此技术专家。坦白来说，虽然我深入研究并实践过领域驱动设计，也用来建模和解决了一些复杂业务问题，但是发自内心的，我其实更喜欢钻研技术，同时，我又对大数据很感兴趣。因此，我决定了，以后的方向，就做数据相关的工作。</li>\n</ul>\n<p>腾讯的四年，是我的第一份工作经历，认识了很多厉害的人，学到了很多。最后自己主动离开，也算走的体面（即使损失了大礼包），还是感谢腾讯。</p>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/640.png",
      "date_published": "2023-03-24T11:23:46.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [
        {
          "name": "pioneeryi"
        }
      ],
      "tags": [
        "技术文章精选集"
      ]
    },
    {
      "title": "Redis持久化机制详解",
      "url": "https://javaguide.cn/database/redis/redis-persistence.html",
      "id": "https://javaguide.cn/database/redis/redis-persistence.html",
      "summary": "深入解析Redis三种持久化机制RDB快照、AOF日志和混合持久化的工作原理、配置方法和优缺点对比，帮助你选择适合业务场景的持久化策略。",
      "content_html": "<p>使用缓存的时候，我们经常需要对内存中的数据进行持久化也就是将内存中的数据写入到硬盘中。大部分原因是为了之后重用数据（比如重启机器、机器故障之后恢复数据），或者是为了做数据同步（比如 Redis 集群的主从节点通过 RDB 文件同步数据）。</p>\n<p>Redis 不同于 Memcached 的很重要一点就是，Redis 支持持久化，而且支持 3 种持久化方式:</p>\n<ul>\n<li>快照（snapshotting，RDB）</li>\n<li>只追加文件（append-only file, AOF）</li>\n<li>RDB 和 AOF 的混合持久化(Redis 4.0 新增)</li>\n</ul>\n<p>官方文档地址：<a href=\"https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/\" target=\"_blank\" rel=\"noopener noreferrer\">https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/</a> 。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/redis/redis4.0-persitence.png\" alt></p>\n<p><strong>本文基于 Redis 7.0+ 版本</strong>。不同版本的持久化机制有重要差异，使用前请确认你的 Redis 版本：</p>\n<p>| 版本           | 持久化默认方式 | 重要特性                |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/database/redis/redis4.0-persitence.png",
      "date_published": "2023-03-23T13:09:30.000Z",
      "date_modified": "2026-04-06T06:53:34.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "Redis常见阻塞原因总结",
      "url": "https://javaguide.cn/database/redis/redis-common-blocking-problems-summary.html",
      "id": "https://javaguide.cn/database/redis/redis-common-blocking-problems-summary.html",
      "summary": "全面总结Redis常见的阻塞原因，包括O(n)复杂度命令、bigkey操作、AOF日志刷盘、RDB快照创建、主从同步等场景，帮助你排查和预防Redis性能问题。",
      "content_html": "<p><a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/xingqiu.png\" alt=\"JavaGuide官方知识星球\"></a></p>\n<blockquote>\n<p>本文整理完善自：<a href=\"https://mp.weixin.qq.com/s/0Nqfq_eQrUb12QH6eBbHXA\" target=\"_blank\" rel=\"noopener noreferrer\">https://mp.weixin.qq.com/s/0Nqfq_eQrUb12QH6eBbHXA</a> ，作者：阿 Q 说代码</p>\n</blockquote>\n<p>这篇文章会详细总结一下可能导致 Redis 阻塞的情况，这些情况也是影响 Redis 性能的关键因素，使用 Redis 的时候应该格外注意！</p>\n<h2>O(n) 命令</h2>\n<p>Redis 中的大部分命令都是 O(1)时间复杂度，但也有少部分 O(n) 时间复杂度的命令，例如：</p>\n<ul>\n<li><code>KEYS *</code>：会返回所有符合规则的 key。</li>\n<li><code>HGETALL</code>：会返回一个 Hash 中所有的键值对。</li>\n<li><code>LRANGE</code>：会返回 List 中指定范围内的元素。</li>\n<li><code>SMEMBERS</code>：返回 Set 中的所有元素。</li>\n<li><code>SINTER</code>/<code>SUNION</code>/<code>SDIFF</code>：计算多个 Set 的交集/并集/差集。</li>\n<li>……</li>\n</ul>\n<p>由于这些命令时间复杂度是 O(n)，有时候也会全表扫描，随着 n 的增大，执行耗时也会越长，从而导致客户端阻塞。不过， 这些命令并不是一定不能使用，但是需要明确 N 的值。另外，有遍历的需求可以使用 <code>HSCAN</code>、<code>SSCAN</code>、<code>ZSCAN</code> 代替。</p>\n<p>除了这些 O(n)时间复杂度的命令可能会导致阻塞之外， 还有一些时间复杂度可能在 O(N) 以上的命令，例如：</p>\n<ul>\n<li><code>ZRANGE</code>/<code>ZREVRANGE</code>：返回指定 Sorted Set 中指定排名范围内的所有元素。时间复杂度为 O(log(n)+m)，n 为所有元素的数量， m 为返回的元素数量，当 m 和 n 相当大时，O(n) 的时间复杂度更小。</li>\n<li><code>ZREMRANGEBYRANK</code>/<code>ZREMRANGEBYSCORE</code>：移除 Sorted Set 中指定排名范围/指定 score 范围内的所有元素。时间复杂度为 O(log(n)+m)，n 为所有元素的数量， m 被删除元素的数量，当 m 和 n 相当大时，O(n) 的时间复杂度更小。</li>\n<li>……</li>\n</ul>\n<h2>SAVE 创建 RDB 快照</h2>\n<p>Redis 提供了两个命令来生成 RDB 快照文件：</p>\n<ul>\n<li><code>save</code> : 同步保存操作，会阻塞 Redis 主线程；</li>\n<li><code>bgsave</code> : fork 出一个子进程，子进程执行，不会阻塞 Redis 主线程，默认选项。</li>\n</ul>\n<p>默认情况下，Redis 默认配置会使用 <code>bgsave</code> 命令。如果手动使用 <code>save</code> 命令生成 RDB 快照文件的话，就会阻塞主线程。</p>\n<h2>AOF</h2>\n<h3>AOF 日志记录阻塞</h3>\n<p>Redis AOF 持久化机制是在执行完命令之后再记录日志，这和关系型数据库（如 MySQL）通常都是执行命令之前记录日志（方便故障恢复）不同。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/redis/redis-aof-write-log-disc.png\" alt=\"AOF 记录日志过程\"></p>\n<p><strong>为什么是在执行完命令之后记录日志呢？</strong></p>\n<ul>\n<li>避免额外的检查开销，AOF 记录日志不会对命令进行语法检查；</li>\n<li>在命令执行完之后再记录，不会阻塞当前的命令执行。</li>\n</ul>\n<p>这样也带来了风险（我在前面介绍 AOF 持久化的时候也提到过）：</p>\n<ul>\n<li>如果刚执行完命令 Redis 就宕机会导致对应的修改丢失；</li>\n<li><strong>可能会阻塞后续其他命令的执行（AOF 记录日志是在 Redis 主线程中进行的）</strong>。</li>\n</ul>\n<h3>AOF 刷盘阻塞</h3>\n<p>开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令，Redis 就会将该命令写入到 AOF 缓冲区 <code>server.aof_buf</code> 中，然后再根据 <code>appendfsync</code> 配置来决定何时将其同步到硬盘中的 AOF 文件。</p>\n<p>在 Redis 的配置文件中存在三种不同的 AOF 持久化方式（ <code>fsync</code>策略），它们分别是：</p>\n<ol>\n<li><code>appendfsync always</code>：主线程调用 <code>write</code> 执行写操作后，<strong>主线程</strong>立即会调用 <code>fsync</code> 函数同步 AOF 文件（刷盘），<code>fsync</code> 完成后线程返回。<code>always</code> 策略由<strong>主线程直接执行 fsync</strong>，而非后台线程。这种方式数据最安全，但每个写操作都会同步阻塞主线程，严重降低 Redis 的性能（<code>write</code> + <code>fsync</code>）。</li>\n<li><code>appendfsync everysec</code>：主线程调用 <code>write</code> 执行写操作后立即返回，由后台线程（ <code>aof_fsync</code> 线程）每秒钟调用 <code>fsync</code> 函数（系统调用）同步一次 AOF 文件（<code>write</code>+<code>fsync</code>，<code>fsync</code>间隔为 1 秒）</li>\n<li><code>appendfsync no</code>：主线程调用 <code>write</code> 执行写操作后立即返回，让操作系统决定何时进行同步，Linux 下一般为 30 秒一次（<code>write</code>但不<code>fsync</code>，<code>fsync</code> 的时机由操作系统决定）。</li>\n</ol>\n<p>当后台线程（ <code>aof_fsync</code> 线程）调用 <code>fsync</code> 函数同步 AOF 文件时，需要等待，直到写入完成。当磁盘压力太大的时候，会导致 <code>fsync</code> 操作发生阻塞，主线程调用 <code>write</code> 函数时也会被阻塞。<code>fsync</code> 完成后，主线程执行 <code>write</code> 才能成功返回。</p>\n<p>关于 AOF 工作流程的详细介绍可以查看：<a href=\"/database/redis/redis-persistence.html\" target=\"_blank\">Redis 持久化机制详解</a>，有助于理解 AOF 刷盘阻塞。</p>\n<h3>AOF 重写阻塞</h3>\n<ol>\n<li>fork 出一条子线程来将文件重写，在执行 <code>BGREWRITEAOF</code> 命令时，Redis 服务器会维护一个 AOF 重写缓冲区，该缓冲区会在子线程创建新 AOF 文件期间，记录服务器执行的所有写命令。</li>\n<li>当子线程完成创建新 AOF 文件的工作之后，服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾，使得新的 AOF 文件保存的数据库状态与现有的数据库状态一致。</li>\n<li>最后，服务器用新的 AOF 文件替换旧的 AOF 文件，以此来完成 AOF 文件重写操作。</li>\n</ol>\n<p>阻塞就是出现在第 2 步的过程中，将缓冲区中新数据写到新文件的过程中会产生<strong>阻塞</strong>。</p>\n<p>相关阅读：<a href=\"https://cloud.tencent.com/developer/article/1633077\" target=\"_blank\" rel=\"noopener noreferrer\">Redis AOF 重写阻塞问题分析</a>。</p>\n<h2>大 Key</h2>\n<p>如果一个 key 对应的 value 所占用的内存比较大，那这个 key 就可以看作是 bigkey。具体多大才算大呢？有一个不是特别精确的参考标准：</p>\n<ul>\n<li>string 类型的 value 超过 1MB</li>\n<li>复合类型（列表、哈希、集合、有序集合等）的 value 包含的元素超过 5000 个（对于复合类型的 value 来说，不一定包含的元素越多，占用的内存就越多）。</li>\n</ul>\n<p>大 key 造成的阻塞问题如下：</p>\n<ul>\n<li>客户端超时阻塞：由于 Redis 执行命令是单线程处理，然后在操作大 key 时会比较耗时，那么就会阻塞 Redis，从客户端这一视角看，就是很久很久都没有响应。</li>\n<li>引发网络阻塞：每次获取大 key 产生的网络流量较大，如果一个 key 的大小是 1 MB，每秒访问量为 1000，那么每秒会产生 1000MB 的流量，这对于普通千兆网卡的服务器来说是灾难性的。</li>\n<li>阻塞工作线程：如果使用 del 删除大 key 时，会阻塞工作线程，这样就没办法处理后续的命令。</li>\n</ul>\n<h3>查找大 key</h3>\n<p>当我们在使用 Redis 自带的 <code>--bigkeys</code> 参数查找大 key 时，最好选择在从节点上执行该命令，因为主节点上执行时，会<strong>阻塞</strong>主节点。</p>\n<ul>\n<li>\n<p>我们还可以使用 SCAN 命令来查找大 key；</p>\n</li>\n<li>\n<p>通过分析 RDB 文件来找出 big key，这种方案的前提是 Redis 采用的是 RDB 持久化。网上有现成的工具：</p>\n</li>\n<li>\n<ul>\n<li>redis-rdb-tools：Python 语言写的用来分析 Redis 的 RDB 快照文件用的工具</li>\n<li>rdb_bigkeys：Go 语言写的用来分析 Redis 的 RDB 快照文件用的工具，性能更好。</li>\n</ul>\n</li>\n</ul>\n<h3>删除大 key</h3>\n<p>删除操作的本质是要释放键值对占用的内存空间。</p>\n<p>释放内存只是第一步，为了更加高效地管理内存空间，在应用程序释放内存时，<strong>操作系统需要把释放掉的内存块插入一个空闲内存块的链表</strong>，以便后续进行管理和再分配。这个过程本身需要一定时间，而且会<strong>阻塞</strong>当前释放内存的应用程序。</p>\n<p>所以，如果一下子释放了大量内存，空闲内存块链表操作时间就会增加，相应地就会造成 Redis 主线程的阻塞，如果主线程发生了阻塞，其他所有请求可能都会超时，超时越来越多，会造成 Redis 连接耗尽，产生各种异常。</p>\n<p>删除大 key 时建议采用分批次删除和异步删除的方式进行。</p>\n<h2>清空数据库</h2>\n<p>清空数据库和上面 bigkey 删除也是同样道理，<code>flushdb</code>、<code>flushall</code> 也涉及到删除和释放所有的键值对，也是 Redis 的阻塞点。</p>\n<h2>集群扩容</h2>\n<p>Redis 集群可以进行节点的动态扩容缩容，这一过程目前还处于半自动状态，需要人工介入。</p>\n<p>在扩缩容的时候，需要进行数据迁移。而 Redis 为了保证迁移的一致性，迁移所有操作都是同步操作。</p>\n<p>执行迁移时，两端的 Redis 均会进入时长不等的阻塞状态，对于小 Key，该时间可以忽略不计，但如果一旦 Key 的内存使用过大，严重的时候会触发集群内的故障转移，造成不必要的切换。</p>\n<h2>Swap（内存交换）</h2>\n<p><strong>什么是 Swap？</strong> Swap 直译过来是交换的意思，Linux 中的 Swap 常被称为内存交换或者交换分区。类似于 Windows 中的虚拟内存，就是当内存不足的时候，把一部分硬盘空间虚拟成内存使用，从而解决内存容量不足的情况。因此，Swap 分区的作用就是牺牲硬盘，增加内存，解决 VPS 内存不够用或者爆满的问题。</p>\n<p>Swap 对于 Redis 来说是非常致命的，Redis 保证高性能的一个重要前提是所有的数据在内存中。如果操作系统把 Redis 使用的部分内存换出硬盘，由于内存与硬盘的读写速度差几个数量级，会导致发生交换后的 Redis 性能急剧下降。</p>\n<p>识别 Redis 发生 Swap 的检查方法如下：</p>\n<p>1、查询 Redis 进程号</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">redis-cli</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> -p</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 6383</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> info</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> server</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> | </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">grep</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> process_id</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">process_id:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 4476</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>2、根据进程号查询内存交换信息</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">cat</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> /proc/4476/smaps</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> | </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">grep</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> Swap</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">Swap:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 0kB</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">Swap:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 0kB</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">Swap:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 4kB</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">Swap:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 0kB</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">Swap:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 0kB</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">.....</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>如果交换量都是 0KB 或者个别的是 4KB，则正常。</p>\n<p>预防内存交换的方法：</p>\n<ul>\n<li>保证机器充足的可用内存</li>\n<li>确保所有 Redis 实例设置最大可用内存(maxmemory)，防止极端情况 Redis 内存不可控的增长</li>\n<li>降低系统使用 swap 优先级，如<code>echo 10 &gt; /proc/sys/vm/swappiness</code></li>\n</ul>\n<h2>CPU 竞争</h2>\n<p>Redis 是典型的 CPU 密集型应用，不建议和其他多核 CPU 密集型服务部署在一起。当其他进程过度消耗 CPU 时，将严重影响 Redis 的吞吐量。</p>\n<p>可以通过<code>redis-cli --stat</code>获取当前 Redis 使用情况。通过<code>top</code>命令获取进程对 CPU 的利用率等信息 通过<code>info commandstats</code>统计信息分析出命令不合理开销时间，查看是否是因为高算法复杂度或者过度的内存优化问题。</p>\n<h2>网络问题</h2>\n<p>连接拒绝、网络延迟，网卡软中断等网络问题也可能会导致 Redis 阻塞。</p>\n<h2>参考</h2>\n<ul>\n<li>Redis 阻塞的 6 大类场景分析与总结：<a href=\"https://mp.weixin.qq.com/s/eaZCEtTjTuEmXfUubVHjew\" target=\"_blank\" rel=\"noopener noreferrer\">https://mp.weixin.qq.com/s/eaZCEtTjTuEmXfUubVHjew</a></li>\n<li>Redis 开发与运维笔记-Redis 的噩梦-阻塞：<a href=\"https://mp.weixin.qq.com/s/TDbpz9oLH6ifVv6ewqgSgA\" target=\"_blank\" rel=\"noopener noreferrer\">https://mp.weixin.qq.com/s/TDbpz9oLH6ifVv6ewqgSgA</a></li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/database/redis/redis-aof-write-log-disc.png",
      "date_published": "2023-03-23T10:25:39.000Z",
      "date_modified": "2026-04-06T06:53:48.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "MySQL查询缓存详解",
      "url": "https://javaguide.cn/database/mysql/mysql-query-cache.html",
      "id": "https://javaguide.cn/database/mysql/mysql-query-cache.html",
      "summary": "深入解析MySQL查询缓存的工作原理、配置管理及其优缺点，分析为什么MySQL 8.0移除了查询缓存功能，以及生产环境中的最佳实践建议。",
      "content_html": "<p>缓存是一个有效且实用的系统性能优化手段，无论是操作系统，还是各类应用软件与 Web 服务，均广泛采用了缓存机制。</p>\n<p>然而，有经验的 DBA 都建议生产环境中把 MySQL 自带的 Query Cache（查询缓存）给关掉。而且，从 MySQL 5.7.20 开始，就已经默认弃用查询缓存了。在 MySQL 8.0 及之后，更是直接删除了查询缓存的功能。</p>\n<p>这又是为什么呢？查询缓存真就这么鸡肋么?</p>\n<p>带着如下几个问题，我们正式进入本文。</p>\n<ul>\n<li>MySQL 查询缓存是什么？适用范围？</li>\n<li>MySQL 缓存规则是什么？</li>\n<li>MySQL 缓存的优缺点是什么？</li>\n<li>MySQL 缓存对性能有什么影响？</li>\n</ul>\n<h2>MySQL 查询缓存介绍</h2>\n<p>MySQL 体系架构如下图所示：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/mysql/mysql-architecture.png\" alt></p>\n<p>为了提高完全相同的查询语句的响应速度，MySQL Server 会对查询语句进行 Hash 计算得到一个 Hash 值。MySQL Server 不会对 SQL 做任何处理，SQL 必须完全一致 Hash 值才会一样。得到 Hash 值之后，通过该 Hash 值到查询缓存中匹配该查询的结果。</p>\n<ul>\n<li>如果匹配（命中），则将查询的结果集直接返回给客户端，不必再解析、执行查询。</li>\n<li>如果没有匹配（未命中），则将 Hash 值和结果集保存在查询缓存中，以便以后使用。</li>\n</ul>\n<p>也就是说，<strong>一个查询语句（select）到了 MySQL Server 之后，会先到查询缓存看看，如果曾经执行过的话，就直接返回结果集给客户端。</strong></p>\n<p><img src=\"https://oss.javaguide.cn/javaguide/13526879-3037b144ed09eb88.png\" alt></p>\n<h2>MySQL 查询缓存管理和配置</h2>\n<p>通过 <code>show variables like '%query_cache%'</code>命令可以查看查询缓存相关的信息。</p>\n<p>8.0 版本之前的话，打印的信息可能是下面这样的：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">mysql</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">show</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> variables</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> like</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '%query_cache%'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">+</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div>",
      "image": "https://oss.javaguide.cn/github/javaguide/mysql/mysql-architecture.png",
      "date_published": "2023-03-16T03:33:45.000Z",
      "date_modified": "2026-03-10T15:16:51.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "程序员的技术成长战略",
      "url": "https://javaguide.cn/high-quality-technical-articles/advanced-programmer/the-growth-strategy-of-the-technological-giant.html",
      "id": "https://javaguide.cn/high-quality-technical-articles/advanced-programmer/the-growth-strategy-of-the-technological-giant.html",
      "summary": "程序员的技术成长战略：围绕技术知识与面试总结梳理关键概念、常见问题与实践要点，帮助你高效学习与备战面试。",
      "content_html": "<blockquote>\n<p><strong>推荐语</strong>：波波老师的一篇文章，写的非常好，不光是对技术成长有帮助，其他领域也是同样适用的！建议反复阅读，形成一套自己的技术成长策略。</p>\n<p><strong>原文地址：</strong> <a href=\"https://mp.weixin.qq.com/s/YrN8T67s801-MRo01lCHXA\" target=\"_blank\" rel=\"noopener noreferrer\">https://mp.weixin.qq.com/s/YrN8T67s801-MRo01lCHXA</a></p>\n</blockquote>\n<h2>1. 前言</h2>\n<p>在波波的微信技术交流群里头，经常有学员问关于技术人该如何学习成长的问题，虽然是微信交流，但我依然可以感受到小伙伴们焦虑的心情。</p>\n<p><strong>技术人为啥焦虑？</strong> 恕我直言，说白了是胆识不足格局太小。胆就是胆量，焦虑的人一般对未来的不确定性怀有恐惧。识就是见识，焦虑的人一般看不清楚周围世界，也看不清自己和适合自己的道路。格局也称志向，容易焦虑的人通常视野窄志向小。如果从战略和管理的视角来看，就是对自己和周围世界的认知不足，没有一个清晰和长期的学习成长战略，也没有可执行的阶段性目标计划+严格的执行。</p>\n<p>因为问此类问题的学员很多，让我感觉有点烦了，为了避免重复回答，所以我专门总结梳理了这篇长文，试图统一来回答这类问题。如果后面还有学员问类似问题，我会引导他们来读这篇文章，然后让他们用三个月、一年甚至更长的时间，去思考和回答这样一个问题：<strong>你的技术成长战略究竟是什么？</strong> 如果你想清楚了这个问题，有清晰和可落地的答案，那么恭喜你，你只需按部就班执行就好，根本无需焦虑，你实现自己的战略目标并做出成就只是一个时间问题；否则，你仍然需要通过不断磨炼+思考，务必去搞清楚这个人生的大问题！！！</p>\n<p>下面我们来看一些行业技术大牛是怎么做的。</p>\n<h2>二. 跟技术大牛学成长战略</h2>\n<p>我们知道软件设计是有设计模式(Design Pattern)的，其实技术人的成长也是有成长模式(Growth Pattern)的。波波经常在 Linkedin 上看一些技术大牛的成长履历，探究其中的成长模式，从而启发制定自己的技术成长战略。</p>\n<p>当然，很少有技术大牛会清晰地告诉你他们的技术成长战略，以及每一年的细分落地计划。但是，这并不妨碍我们通过他们的过往履历和产出成果，去溯源他们的技术成长战略。实际上， <strong>越是牛逼的技术人，他们的技术成长战略和路径越是清晰，我们越容易从中探究出一些成功的模式。</strong></p>\n<h3>2.1 系统性能专家案例</h3>\n<p>国内的开发者大都热衷于系统性能优化，有些人甚至三句话离不开高性能/高并发，但真正能深入这个领域，做到专家级水平的却寥寥无几。</p>\n<p>我这边要特别介绍的这个技术大牛叫 <strong>Brendan Gregg</strong> ，他是系统性能领域经典书《System Performance: Enterprise and the Cloud》(中文版<a href=\"https://www.amazon.cn/dp/B08GC261P9\" target=\"_blank\" rel=\"noopener noreferrer\">《性能之巅：洞悉系统、企业和云计算》</a>)的作者，也是著名的<a href=\"https://github.com/brendangregg/FlameGraph\" target=\"_blank\" rel=\"noopener noreferrer\">性能分析利器火焰图(Flame Graph)</a>的作者。</p>\n<p>Brendan Gregg 之前是 Netflix 公司的高级性能架构师，在 Netflix 工作近 7 年。2022 年 4 月，他离开了 Netflix 去了 Intel，担任院士职位。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/cdb11ce2f1c3a69fd19e922a7f5f59bf.png\" alt></p>\n<p>总体上，他已经在系统性能领域深耕超过 10 年，<a href=\"https://www.linkedin.com/in/brendangregg/\" target=\"_blank\" rel=\"noopener noreferrer\">Brendan Gregg 的过往履历</a>可以在 linkedin 上看到。在这 10 年间，除了书籍以外，Brendan Gregg 还产出了超过上百份和系统性能相关的技术文档，演讲视频/ppt，还有各种工具软件，相关内容都整整齐齐地分享在<a href=\"http://www.brendangregg.com/\" target=\"_blank\" rel=\"noopener noreferrer\">他的技术博客</a>上，可以说他是一个非常高产的技术大牛。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/format,png-20230309231802218.png\" alt=\"性能工具\"></p>\n<p>上图来自 Brendan Gregg 的新书《BPF Performance Tools: Linux System and Application Observability》。从这个图可以看出，Brendan Gregg 对系统性能领域的掌握程度，已经深挖到了硬件、操作系统和应用的每一个角落，可以说是 360 度无死角，整个计算机系统对他来说几乎都是透明的。波波认为，Brendan Gregg 是名副其实的，世界级的，系统性能领域的大神级人物。</p>\n<h3>2.2 从开源到企业案例</h3>\n<p>我要分享的第二个技术大牛是 <strong>Jay Kreps</strong>，他是知名的开源消息中间件 Kafka 的创始人/架构师，也是 Confluent 公司的联合创始人和 CEO，Confluent 公司是围绕 Kafka 开发企业级产品和服务的技术公司。</p>\n<p>从<a href=\"https://www.linkedin.com/in/jaykreps/\" target=\"_blank\" rel=\"noopener noreferrer\">Jay Kreps 的 Linkedin 的履历</a>上我们可以看出，Jay Kreps 之前在 Linkedin 工作了 7 年多(2007.6 ~ 2014. 9)，从高级工程师、工程主管，一直做到首席资深工程师。Kafka 大致是在 2010 年，Jay Kreps 在 Linkedin 发起的一个项目，解决 Linkedin 内部的大数据采集、存储和消费问题。之后，他和他的团队一直专注 Kafka 的打磨，开源(2011 年初)和社区生态的建设。</p>\n<p>到 2014 年底，Kafka 在社区已经非常成功，有了一个比较大的用户群，于是 Jay Kreps 就和几个早期作者一起离开了 Linkedin，成立了<a href=\"https://tech.163.com/14/1107/18/AAFG92LD00094ODU.html\" target=\"_blank\" rel=\"noopener noreferrer\">Confluent 公司</a>，开始了 Kafka 和周边产品的企业化服务道路。今年(2020.4 月)，Confluent 公司已经获得 E 轮 2.5 亿美金融资，公司估值达到 45 亿美金。从 Kafka 诞生到现在，Jay Kreps 差不多在这个产品和公司上投入了整整 10 年。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/format,png-20230309231805796.png\" alt=\"Confluent创始人三人组\"></p>\n<p>上图是 Confluent 创始人三人组，一个非常有意思的组合，一个中国人(左)，一个印度人(右)，中间的 Jay Kreps 是美国人。</p>\n<p>我之所以对 Kafka 和 Jay Kreps 的印象特别深刻，是因为在 2012 年下半年，我在携程框架部也是专门搞大数据采集的，我还开发过一套功能类似 Kafka 的 Log Collector + Agent 产品。我记得同时期有不止 4 个同类型的开源产品：Facebook Scribe、Apache Chukwa、Apache Flume 和 Apache Kafka。现在回头看，只有 Kafka 走到现在发展得最好，这个和创始人的专注和持续投入是分不开的，当然背后和几个创始人的技术大格局也是分不开的。</p>\n<p>当年我对战略性思维几乎没有概念，还处在<strong>什么技术都想学、认为各种项目做得越多越牛的阶段</strong>。搞了半年的数据采集以后，我就掉头搞其它“更有趣的”项目去了(从这个事情的侧面，也可以看出我当年的技术格局是很小的)。中间我陆续关注过 Jay 的一些创业动向，但是没想到他能把 Confluent 公司发展到目前这个规模。现在回想，其实在十年前，Jay Kreps 对自己的技术成长就有比较明确的战略性思考，也具有大的技术格局和成事的一些必要特质。Jay Kreps 和 Kafka 给我上了一堂生动的技术战略和实践课。</p>\n<h3>2.3 技术媒体大 V 案例</h3>\n<p>介绍到这里，有些同学可能会反驳说：波波你讲的这些大牛都是学历背景好，功底扎实起点高，所以他们才更能成功。其实不然，这里我再要介绍一位技术媒体界的大 V 叫 Brad Traversy，大家可以看<a href=\"https://www.linkedin.com/in/bradtraversy/\" target=\"_blank\" rel=\"noopener noreferrer\">他的 Linkedin 简历</a>，背景很一般，学历差不多是一个非正规的社区大学(相当于大专)，没有正规大厂工作经历，有限几份工作一直是在做网站外包。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/30d6d67dc6dd5f9251f2f01af4de53fc.png\" alt></p>\n<p>但是！！！Brad Traversy 目前是技术媒体领域的一个大 V，当前<a href=\"https://www.youtube.com/c/TraversyMedia\" target=\"_blank\" rel=\"noopener noreferrer\">他在 Youtube 上的频道</a>有 138 万多的订阅量，10 年累计输出 Web 开发和编程相关教学视频超过 800 个。Brad Traversy 也是 <a href=\"https://www.udemy.com/user/brad-traversy/\" target=\"_blank\" rel=\"noopener noreferrer\">Udemy</a> 上的一个成功讲师，目前已经在 Udemy 上累计输出课程 19 门，购课学生数量近 42 万。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/160b0bc4f689413757b9b5e2448f940b.png\" alt></p>\n<p>Brad Traversy 目前是自由职业者，他的 Youtube 广告+Udemy 课程的收入相当不错。</p>\n<p>就是这样一位技术媒体大 V，你很难想象，在年轻的时候，贴在他身上的标签是：不良少年，酗酒，抽烟，吸毒，纹身，进监狱。。。直</p>\n<p>到结婚后的第一个孩子诞生，他才开始担起责任做出改变，然后凭借对技术的一腔热情，开始在 Youtube 平台上持续输出免费课程。从此他找到了适合自己的战略目标，然后人生开始发生各种积极的变化。。。如果大家对 Brad Traversy 的过往经历感兴趣，推荐观看他在 Youtube 上的自述视频<a href=\"https://www.youtube.com/watch?v=zA9krklwADI\" target=\"_blank\" rel=\"noopener noreferrer\">《My Struggles &amp; Success》</a>。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/format,png-20230309231830686.png\" alt=\"My Struggles &amp; Success\"></p>\n<p>我粗略浏览了<a href=\"https://www.youtube.com/c/TraversyMedia/videos\" target=\"_blank\" rel=\"noopener noreferrer\">Brad Traversy 在 Youtube 上的所有视频</a>，10 年总计输出 800+视频，平均每年 80+。第一个视频提交于 2010 年 8 月，刚开始几年几乎没有订阅量，2017 年 1 月订阅量才到 50k，这中间差不多隔了 6 年。2017.10 月订阅量猛增到 200k，2018 年 3 月订阅量到 300k。当前 2021.1 月，订阅量达到 138 万。可以认为从 2017 开始，也就是在积累了 6 ～ 7 年后，他的订阅量开始出现拐点。<strong>如果把这些数据画出来，将会是一条非常漂亮的复利曲线</strong>。</p>\n<h3>2.4 案例小结</h3>\n<p>Brendan Gregg，Jay Kreps 和 Brad Traversy 三个人走的技术路线各不相同，但是他们的成功具有共性或者说模式：</p>\n<p><strong>1、找到了适合自己的长期战略目标。</strong></p>\n<ul>\n<li>Brendan Gregg: 成为系统性能领域顶级专家</li>\n<li>Jay Kreps：开创基于 Kafka 开源消息队列的企业服务公司，并将公司做到上市</li>\n<li>Brad Traversy: 成为技术媒体领域大 V 和课程讲师，并以此作为自己的职业</li>\n</ul>\n<p><strong>2、专注深耕一个(或有限几个相关的)细分领域(Niche)，保持定力，不随便切换领域。</strong></p>\n<ul>\n<li>Brendan Gregg：系统性能领域</li>\n<li>Jay Kreps: 消息中间件/实时计算领域+创业</li>\n<li>Brad Traversy: 技术媒体/教学领域，方向 Web 开发 + 编程语言</li>\n</ul>\n<p><strong>3、长期投入，三人都持续投入了 10 年。</strong></p>\n<p><strong>4、年度细分计划+持续可量化的价值产出(Persistent &amp; Measurable Value Output)。</strong></p>\n<ul>\n<li>Brendan Gregg：除公司日常工作产出以外，每年有超过 10 份以上的技术文档和演讲视频产出，平均每年有 2.5 个开源工具产出。十年共产出书籍 2 本，其中《System Performance》已经更新到第二版。</li>\n<li>Jay Kreps：总体有开源产品+公司产出，1 本书产出，每年有 Kafka 和周边产品发版若干。</li>\n<li>Brad Traversy: 每年有 Youtube 免费视频产出（平均每年 80+）+Udemy 收费视频课产出(平均每年 1.5 门)。</li>\n</ul>\n<p><strong>5、以终为始是牛人和普通人的一大区别。</strong></p>\n<p>普通人通常走一步算一步，很少长远规划。牛人通 常是先有远大目标，然后采用倒推法，将大目标细化到每年/月/周的详细落地计划。Brendan Gregg，Jay Kreps 和 Brad Traversy 三人都是以终为始的典型。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/format,png-20230309231833871.png\" alt=\"以终为始\"></p>\n<p>上面总结了几位技术大牛的成长模式，其中一个重点就是：这些大牛的成长都是通过 <strong>持续有价值产出(Persistent Valuable Output)</strong> 来驱动的。持续产出为啥如此重要，这个还要从下面的学习金字塔说起。</p>\n<h2>三、学习金字塔和刻意训练</h2>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/format,png-20230309231836811.png\" alt=\"学习金字塔\"></p>\n<p>学习金字塔是美国缅因州国家训练实验室的研究成果，它认为：</p>\n<blockquote>\n<ol>\n<li>我们平时上课听讲之后，学习内容平均留存率大致只有 5%左右；</li>\n<li>书本阅读的平均留存率大致只有 10%左右；</li>\n<li>学习配上视听效果的课程，平均留存率大致在 20%左右；</li>\n<li>老师实际动手做实验演示后的平均留存率大致在 30%左右；</li>\n<li>小组讨论(尤其是辩论后)的平均留存率可以达到 50%左右；</li>\n<li>在实践中实际应用所学之后，平均留存率可以达到 75%左右；</li>\n<li>在实践的基础上，再把所学梳理出来，转而再传授给他人后，平均留存率可以达到 90%左右。</li>\n</ol>\n</blockquote>\n<p>上面列出的 7 种学习方法，前四种称为 <strong>被动学习</strong> ，后三种称为 <strong>主动学习</strong>。</p>\n<p>拿学游泳做个类比，被动学习相当于你看别人游泳，而主动学习则是你自己要下水去游。我们知道游泳或者跑步之类的运动是要燃烧身体卡路里的，这样才能达到锻炼身体和长肌肉的效果(肌肉是卡路里燃烧的结果)。如果你只是看别人游泳，自己不实际去游，是不会长肌肉的。同样的，主动学习也是要燃烧脑部卡路里的，这样才能达到训练大脑和长脑部“肌肉”的效果。</p>\n<p>我们也知道，燃烧身体的卡路里，通常会让人感觉不舒适，如果燃烧身体卡路里会让人感觉舒适的话，估计这个世界上应该不会有胖子这类人。同样，燃烧脑部卡路里也会让人感觉不适、紧张、出汗或语无伦次，如果燃烧脑部卡路里会让人感觉舒适的话，估计这个世界上人人都很聪明，人人都能发挥最大潜能。当然，这些不舒适是短期的，长期会使你更健康和聪明。波波一直认为， <strong>人与人之间的先天身体其实都差不多，但是后天身体素质和能力有差异，这些差异，很大程度是由后天对身体和大脑的训练质量、频度和强度所造成的。</strong></p>\n<p>明白这个道理之后，心智成熟和自律的人就会对自己进行持续地 <strong>刻意训练</strong> 。这个刻意训练包括对身体的训练，比如波波现在每天坚持跑步 3km，走 3km，每天做 60 个仰卧起坐，5 分钟平板撑等等，每天保持让身体燃烧一定量的卡路里。刻意训练也包括对大脑的训练，比如波波现在每天做项目写代码 coding(训练脑+手)，平均每天在 B 站上输出十分钟免费视频(训练脑+口头表达)，另外有定期总结输出公众号文章(训练脑+文字表达)，还有每天打半小时左右的平衡球(下图)或古墓丽影游戏(训练小脑+手)，每天保持让大脑燃烧一定量的卡路里，并保持一定强度(适度不适感)。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/format,png-20230309231839985.png\" alt=\"平衡球游戏\"></p>\n<p>关于刻意训练的专业原理和方法论，推荐看书籍《刻意练习》。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/format,png-20230309231842735.png\" alt=\"刻意练习\"></p>\n<p>注意，如果你平时从来不做举重锻炼的，那么某天突然做举重会很不适应甚至受伤。脑部训练也是一样的，如果你从来没有做过视频输出，那么刚开始做会很不适应，做出来的视频质量会很差。不过没有关系，任何训练都是一个循序渐进，不断强化的过程。等大脑相关区域的&quot;肌肉&quot;长出来以后，会逐步进入正循环，后面会越来越顺畅，相关&quot;肌肉&quot;会越来越发达。所以，和健身一样，健脑也不能遇到困难就放弃，需要循序渐进(Incremental)+持续地(Persistent)刻意训练。</p>\n<p>理解了学习金字塔和刻意训练以后，现在再来看 Brendan Gregg，Jay Kreps 和 Brad Traversy 这些大牛的做法，他们的学习成长都是建立在持续有价值产出的基础上的，这些产出都是刻意训练+燃烧脑部卡路里的成果。他们的产出要么是建立在实践基础上的产出，例如 Jay Kreps 的 Kafka 开源项目和 Confluent 公司；要么是在实践的基础上，再整理传授给其他人的产出，例如，Brendan Greeg 的技术演讲 ppt/视频，书籍，还有 Brad Traversy 的教学视频等等。换句话说，他们一直在学习金字塔的 5 ～ 7 层主动和高效地学习。并且，他们的学习产出还可以获得用户使用，有客户价值(Customer Value)，有用户就有反馈和度量。记住，有反馈和度量的学习，也称闭环学习，它是能够不断改进提升的；反之，没有反馈和度量的学习，无法改进提升。</p>\n<p>现在，你也应该明白，晒个书单秀个技能图谱很简单，读个书上个课也不难。但是要你给出 5 ～ 10 年的总体技术成长战略，再基于这个战略给出每年的细分落地计划(尤其是产出计划)，然后再严格按计划执行，这的确是很难的事情。这需要大量的实践训练+深度思考，要燃烧大量的脑部卡路里！但这是上天设置的进化法则，成长为真正的技术大牛如同成长为一流的运动员，是需要通过燃烧与之相匹配量的卡路里来交换的。成长为真正的技术大牛，也是需要通过产出与之匹配的社会价值来交换的，只有这样社会才能正常进化。你推进了社会进化，社会才会回馈你。如果不是这样，社会就无法正常进化。</p>\n<h2>四、战略思维的诞生</h2>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/dc87167f53b243d49f9f4e8c7fe530a1~tplv-k3u1fbpfcp-zoom-1.png\" alt=\"思考周期和机会点\"></p>\n<p>一般毕业生刚进入企业工作的时候，思考大都是以天/星期/月为单位的，基本上都是今天学个什么技术，明天学个什么语言，很少会去思考一年甚至更长的目标。这是个眼前漆黑看不到的懵懂时期，捕捉到机会点的能力和概率都非常小。</p>\n<p>工作了三年以后，悟性好的人通常会以一年为思考周期，制定和实施一些年度计划。这个时期是相信天赋和比拼能力的阶段，可以捕捉到一些小机会。</p>\n<p>工作了五年以后，一些悟性好的人会产生出一定的胆识和眼光，他们会以 3 ～ 5 年为周期来制定和实施计划，开始主动布局去捕捉一些中型机会点。</p>\n<p>工作了十年以后，悟性高的人会看到模式和规则变化，例如看出行业发展模式，还有人才的成长模式等，于是开始诞生出战略性思维。然后他们会以 5 ～ 10 年为周期来制定和实施自己的战略计划，开始主动布局去捕捉一些中大机会点。Brendan Gregg，Jay Kreps 和 Brad Traversy 都是属于这个阶段的人。</p>\n<p>当然还有很少一些更牛的时代精英，他们能够看透时代和人性，他们的思考是以一生甚至更长时间为单位的，这些超人不在本文讨论范围内。</p>\n<h2>五、建议</h2>\n<p><strong>1、以 5 ～ 10 年为周期去布局谋划你的战略。</strong></p>\n<p>现在大学生毕业的年龄一般在 22 ～ 23 岁，那么在工作了十年后，也就是在你 32 ～ 33 岁的时候，你也差不多看了十年了，应该对自己和周围的世界(你的行业和领域)有一个比较深刻的领悟了。<strong>如果你到这个年纪还懵懵懂懂，今天抓东明天抓西，那么只能说你的胆识格局是相当的低</strong>。在当前 IT 行业竞争这么激烈的情况下，到 35 岁被下岗可能就在眼前了。</p>\n<p>有了战略性思考，你应该以 5 ～ 10 年为周期去布局谋划你的战略。以 Brendan Gregg，Jay Kreps 和 Brad Traversy 这些大牛为例，<strong>人生若真的要干点成就出来，投入周期一般都要十年的</strong>。从 33 岁开始，你大致有 3 个十年，因为到 60 岁以后，一般人都老眼昏花干不了大事了。如果你悟性差一点，到 40 岁才开始规划，那么你大致还有 2 个十年。如果你规划好了，这 2 ～ 3 个十年可以成就不小的事业。否则，你很可能一生都成就不了什么事业，或者一直在帮助别人成就别人的事业。</p>\n<p><strong>2、专注自己的精力。</strong></p>\n<p>考虑到人生能干事业的时间也就是 2 ～ 3 个十年，你会发现人生其实很短暂，这时候你会把精力都投入到实现你的十年战略上去，没有时间再浪费在比如网上的闲聊和扯皮争论上去。</p>\n<p><strong>3、细分落地计划尤其是产出计划。</strong></p>\n<p>有了十年战略方向，下一步是每年的细分落地计划，尤其是产出计划。这些计划主要应该工作在学习金字塔的 5/6/7 层。<strong>产出应该是刻意训练+燃烧卡路里的结果，每天让身体和大脑都保持燃烧一定量的卡路里</strong>。</p>\n<p><strong>4、产出有价值的东西形成正反馈。</strong></p>\n<p>产出应该有客户价值，自己能学习(自己成长进化)，对别人还有用(推动社会成长进化)，这样可以得到<strong>用户回馈和度量</strong>，形成一个闭环，可以持续改进和提升你的学习。</p>\n<p><strong>5、少即是多。</strong></p>\n<p>深耕一个(或有限几个相关的)领域。所有细分计划应该紧密围绕你的战略展开。克制内心欲望，不要贪多和分心，不要被喧嚣的世界所迷惑。</p>\n<p><strong>6、战略方向+细分计划都要写下来，定期 review 优化。</strong></p>\n<p><strong>7、要有定力，持续努力。</strong></p>\n<p>曲则全、枉则直，战略实现是不可能直线的。战略方向和细分计划通常要按需调整，尤其在早期，但是最终要收敛。如果老是变不收敛，就是缺乏战略定力，是个必须思考和解决的大问题。</p>\n<p>别人的成长战略可以参考，但是不要刻意去模仿，你有你自己的颜色，<strong>你应该成为独一无二的你</strong>。</p>\n<p>战略方向和细分计划明确了，接下来就是按部就班执行，十年如一日铁打不动。</p>\n<p><strong>8、慢就是快。</strong></p>\n<p>战略目标的实现也和种树一样是生长出来的，需要时间耐心栽培，记住<strong>慢就是快。</strong>焦虑纠结的时候，像念经一样默念王阳明《传习录》中的教诲：</p>\n<blockquote>\n<p>立志用功，如种树然。方其根芽，犹未有干；及其有干，尚未有枝；枝而后叶，叶而后花实。初种根时，只管栽培灌溉。勿作枝想，勿作花想，勿作实想。悬想何益？但不忘栽培之功，怕没有枝叶花实？</p>\n<p>译文：</p>\n<p>实现战略目标，就像种树一样。刚开始只是一个小根芽，树干还没有长出来；树干长出来了，枝叶才能慢慢长出来；树枝长出来，然后才能开花和结果。刚开始种树的时候，只管栽培灌溉，别老是纠结枝什么时候长出来，花什么时候开，果实什么时候结出来。纠结有什么好处呢？只要你坚持投入栽培，还怕没有枝叶花实吗？</p>\n</blockquote>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/high-quality-technical-articles/cdb11ce2f1c3a69fd19e922a7f5f59bf.png",
      "date_published": "2023-02-23T04:45:00.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [
        {
          "name": "波波微课"
        }
      ],
      "tags": [
        "技术文章精选集"
      ]
    },
    {
      "title": "网络攻击常见手段总结（安全）",
      "url": "https://javaguide.cn/cs-basics/network/network-attack-means.html",
      "id": "https://javaguide.cn/cs-basics/network/network-attack-means.html",
      "summary": "总结常见 TCP/IP 攻击与防护思路，覆盖 DDoS、IP/ARP 欺骗、中间人等手段，强调工程防护实践。",
      "content_html": "<blockquote>\n<p>本文整理完善自<a href=\"https://mp.weixin.qq.com/s/AZwWrOlLxRSSi-ywBgZ0fA\" target=\"_blank\" rel=\"noopener noreferrer\">TCP/IP 常见攻击手段 - 暖蓝笔记 - 2021</a>这篇文章。</p>\n</blockquote>\n<p>这篇文章的内容主要是介绍 TCP/IP 常见攻击手段，尤其是 DDoS 攻击，也会补充一些其他的常见网络攻击手段。</p>\n<h2>IP 欺骗</h2>\n<h3>IP 是什么?</h3>\n<p>在网络中，所有的设备都会分配一个地址。这个地址就仿佛小蓝的家地址「<strong>多少号多少室</strong>」，这个号就是分配给整个子网的，「<strong>室</strong>」对应的号码即分配给子网中计算机的，这就是网络中的地址。「号」对应的号码为网络号，「<strong>室</strong>」对应的号码为主机号，这个地址的整体就是 <strong>IP 地址</strong>。</p>\n<h3>通过 IP 地址我们能知道什么？</h3>\n<p>通过 IP 地址，我们就可以知道判断访问对象服务器的位置，从而将消息发送到服务器。一般发送者发出的消息首先经过子网的集线器，转发到最近的路由器，然后根据路由位置访问下一个路由器的位置，直到终点</p>\n<p><strong>IP 头部格式</strong> :</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/843fd07074874ee0b695eca659411b42~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<h3>IP 欺骗技术是什么？</h3>\n<p>骗呗，拐骗，诱骗！</p>\n<p>IP 欺骗技术就是<strong>伪造</strong>某台主机的 IP 地址的技术。通过 IP 地址的伪装使得某台主机能够<strong>伪装</strong>另外的一台主机，而这台主机往往具有某种特权或者被另外的主机所信任。</p>\n<p>假设现在有一个合法用户 <strong>(1.1.1.1)</strong> 已经同服务器建立正常的连接，攻击者构造攻击的 TCP 数据，伪装自己的 IP 为 <strong>1.1.1.1</strong>，并向服务器发送一个带有 RST 位的 TCP 数据段。服务器接收到这样的数据后，认为从 <strong>1.1.1.1</strong> 发送的连接有错误，就会清空缓冲区中建立好的连接。</p>\n<p>这时，如果合法用户 <strong>1.1.1.1</strong> 再发送合法数据，服务器就已经没有这样的连接了，该用户就必须从新开始建立连接。攻击时，伪造大量的 IP 地址，向目标发送 RST 数据，使服务器不对合法用户服务。虽然 IP 地址欺骗攻击有着相当难度，但我们应该清醒地意识到，这种攻击非常广泛，入侵往往从这种攻击开始。</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/7547a145adf9404aa3a05f01f5ca2e32~tplv-k3u1fbpfcp-zoom-1.png\" alt=\"IP 欺骗 DDoS 攻击\"></p>\n<h3>如何缓解 IP 欺骗？</h3>\n<p>虽然无法预防 IP 欺骗，但可以采取措施来阻止伪造数据包渗透网络。<strong>入口过滤</strong> 是防范欺骗的一种极为常见的防御措施，如 BCP38（通用最佳实践文档）所示。入口过滤是一种数据包过滤形式，通常在<a href=\"https://www.cloudflare.com/learning/serverless/glossary/what-is-edge-computing/\" target=\"_blank\" rel=\"noopener noreferrer\">网络边缘</a>设备上实施，用于检查传入的 IP 数据包并确定其源标头。如果这些数据包的源标头与其来源不匹配或者看上去很可疑，则拒绝这些数据包。一些网络还实施出口过滤，检查退出网络的 IP 数据包，确保这些数据包具有合法源标头，以防止网络内部用户使用 IP 欺骗技术发起出站恶意攻击。</p>\n<h2>SYN Flood(洪水)</h2>\n<h3>SYN Flood 是什么？</h3>\n<p>SYN Flood 是互联网上最原始、最经典的 DDoS（Distributed Denial of Service，分布式拒绝服务）攻击之一，旨在耗尽可用服务器资源，致使服务器无法传输合法流量</p>\n<p>SYN Flood 利用了 TCP 协议的三次握手机制，攻击者通常利用工具或者控制僵尸主机向服务器发送海量的变源 IP 地址或变源端口的 TCP SYN 报文，服务器响应了这些报文后就会生成大量的半连接，当系统资源被耗尽后，服务器将无法提供正常的服务。<br>\n增加服务器性能，提供更多的连接能力对于 SYN Flood 的海量报文来说杯水车薪，防御 SYN Flood 的关键在于判断哪些连接请求来自于真实源，屏蔽非真实源的请求以保障正常的业务请求能得到服务。</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/2b3d2d4dc8f24890b5957df1c7d6feb8~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<h3>TCP SYN Flood 攻击原理是什么？</h3>\n<p><strong>TCP SYN Flood</strong> 攻击利用的是 <strong>TCP</strong> 的三次握手（<strong>SYN -&gt; SYN/ACK -&gt; ACK</strong>），假设连接发起方是 A，连接接受方是 B，即 B 在某个端口（<strong>Port</strong>）上监听 A 发出的连接请求，过程如下图所示，左边是 A，右边是 B。</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/a39355a1ea404323a11ca6644e009183~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>A 首先发送 <strong>SYN</strong>（Synchronization）消息给 B，要求 B 做好接收数据的准备；B 收到后反馈 <strong>SYN-ACK</strong>（Synchronization-Acknowledgement） 消息给 A，这个消息的目的有两个：</p>\n<ul>\n<li>向 A 确认已做好接收数据的准备，</li>\n<li>同时要求 A 也做好接收数据的准备，此时 B 已向 A 确认好接收状态，并等待 A 的确认，连接处于<strong>半开状态（Half-Open）</strong>，顾名思义只开了一半；A 收到后再次发送 <strong>ACK</strong> (Acknowledgement) 消息给 B，向 B 确认也做好了接收数据的准备，至此三次握手完成，「<strong>连接</strong>」就建立了，</li>\n</ul>\n<p>大家注意到没有，最关键的一点在于双方是否都按对方的要求进入了<strong>可以接收消息</strong>的状态。而这个状态的确认主要是双方将要使用的<strong>消息序号(<strong>SequenceNum)，</strong>TCP</strong> 为保证消息按发送顺序抵达接收方的上层应用，需要用<strong>消息序号</strong>来标记消息的发送先后顺序的。</p>\n<p><strong>TCP</strong>是「<strong>双工</strong>」(Duplex)连接，同时支持双向通信，也就是双方同时可向对方发送消息，其中 <strong>SYN</strong> 和 <strong>SYN-ACK</strong> 消息开启了 A→B 的单向通信通道（B 获知了 A 的消息序号）；<strong>SYN-ACK</strong> 和 <strong>ACK</strong> 消息开启了 B→A 单向通信通道（A 获知了 B 的消息序号）。</p>\n<p>上面讨论的是双方在诚实守信，正常情况下的通信。</p>\n<p>但实际情况是，网络可能不稳定会丢包，使握手消息不能抵达对方，也可能是对方故意不按规矩来，故意延迟或不发送握手确认消息。</p>\n<p>假设 B 通过某 <strong>TCP</strong> 端口提供服务，B 在收到 A 的 <strong>SYN</strong> 消息时，积极的反馈了 <strong>SYN-ACK</strong> 消息，使连接进入<strong>半开状态</strong>，因为 B 不确定自己发给 A 的 <strong>SYN-ACK</strong> 消息或 A 反馈的 ACK 消息是否会丢在半路，所以会给每个待完成的半开连接都设一个<strong>Timer</strong>，如果超过时间还没有收到 A 的 <strong>ACK</strong> 消息，则重新发送一次 <strong>SYN-ACK</strong> 消息给 A，直到重试超过一定次数时才会放弃。</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/7ff1daddcec44d61994f254e664987b4~tplv-k3u1fbpfcp-zoom-1.png\" alt=\"图片\"></p>\n<p>B 为帮助 A 能顺利连接，需要<strong>分配内核资源</strong>维护半开连接，那么当 B 面临海量的连接 A 时，如上图所示，<strong>SYN Flood</strong> 攻击就形成了。攻击方 A 可以控制肉鸡向 B 发送大量 SYN 消息但不响应 ACK 消息，或者干脆伪造 SYN 消息中的 <strong>Source IP</strong>，使 B 反馈的 <strong>SYN-ACK</strong> 消息石沉大海，导致 B 被大量注定不能完成的半开连接占据，直到资源耗尽，停止响应正常的连接请求。</p>\n<h3>SYN Flood 的常见形式有哪些？</h3>\n<p><strong>恶意用户可通过三种不同方式发起 SYN Flood 攻击</strong>：</p>\n<ol>\n<li><strong>直接攻击：</strong> 不伪造 IP 地址的 SYN 洪水攻击称为直接攻击。在此类攻击中，攻击者完全不屏蔽其 IP 地址。由于攻击者使用具有真实 IP 地址的单一源设备发起攻击，因此很容易发现并清理攻击者。为使目标机器呈现半开状态，黑客将阻止个人机器对服务器的 SYN-ACK 数据包做出响应。为此，通常采用以下两种方式实现：部署防火墙规则，阻止除 SYN 数据包以外的各类传出数据包；或者，对传入的所有 SYN-ACK 数据包进行过滤，防止其到达恶意用户机器。实际上，这种方法很少使用（即便使用过也不多见），因为此类攻击相当容易缓解 – 只需阻止每个恶意系统的 IP 地址。哪怕攻击者使用僵尸网络（如 <a href=\"https://www.cloudflare.com/learning/ddos/glossary/mirai-botnet/\" target=\"_blank\" rel=\"noopener noreferrer\">Mirai 僵尸网络</a>），通常也不会刻意屏蔽受感染设备的 IP。</li>\n<li><strong>欺骗攻击：</strong> 恶意用户还可以伪造其发送的各个 SYN 数据包的 IP 地址，以便阻止缓解措施并加大身份暴露难度。虽然数据包可能经过伪装，但还是可以通过这些数据包追根溯源。此类检测工作很难开展，但并非不可实现；特别是，如果 Internet 服务提供商 (ISP) 愿意提供帮助，则更容易实现。</li>\n<li><strong>分布式攻击（DDoS）：</strong> 如果使用僵尸网络发起攻击，则追溯攻击源头的可能性很低。随着混淆级别的攀升，攻击者可能还会命令每台分布式设备伪造其发送数据包的 IP 地址。哪怕攻击者使用僵尸网络（如 Mirai 僵尸网络），通常也不会刻意屏蔽受感染设备的 IP。</li>\n</ol>\n<h3>如何缓解 SYN Flood？</h3>\n<h4>扩展积压工作队列</h4>\n<p>目标设备安装的每个操作系统都允许具有一定数量的半开连接。若要响应大量 SYN 数据包，一种方法是增加操作系统允许的最大半开连接数目。为成功扩展最大积压工作，系统必须额外预留内存资源以处理各类新请求。如果系统没有足够的内存，无法应对增加的积压工作队列规模，将对系统性能产生负面影响，但仍然好过拒绝服务。</p>\n<h4>回收最先创建的 TCP 半开连接</h4>\n<p>另一种缓解策略是在填充积压工作后覆盖最先创建的半开连接。这项策略要求完全建立合法连接的时间低于恶意 SYN 数据包填充积压工作的时间。当攻击量增加或积压工作规模小于实际需求时，这项特定的防御措施将不奏效。</p>\n<h4>SYN Cookie</h4>\n<p>此策略要求服务器创建 Cookie。为避免在填充积压工作时断开连接，服务器使用 SYN-ACK 数据包响应每一项连接请求，而后从积压工作中删除 SYN 请求，同时从内存中删除请求，保证端口保持打开状态并做好重新建立连接的准备。如果连接是合法请求并且已将最后一个 ACK 数据包从客户端机器发回服务器，服务器将重建（存在一些限制）SYN 积压工作队列条目。虽然这项缓解措施势必会丢失一些 TCP 连接信息，但好过因此导致对合法用户发起拒绝服务攻击。</p>\n<h2>UDP Flood(洪水)</h2>\n<h3>UDP Flood 是什么？</h3>\n<p><strong>UDP Flood</strong> 也是一种拒绝服务攻击，将大量的用户数据报协议（<strong>UDP</strong>）数据包发送到目标服务器，目的是压倒该设备的处理和响应能力。防火墙保护目标服务器也可能因 <strong>UDP</strong> 泛滥而耗尽，从而导致对合法流量的拒绝服务。</p>\n<h3>UDP Flood 攻击原理是什么？</h3>\n<p><strong>UDP Flood</strong> 主要通过利用服务器响应发送到其中一个端口的 <strong>UDP</strong> 数据包所采取的步骤。在正常情况下，当服务器在特定端口接收到 <strong>UDP</strong> 数据包时，会经过两个步骤：</p>\n<ul>\n<li>服务器首先检查是否正在运行正在侦听指定端口的请求的程序。</li>\n<li>如果没有程序在该端口接收数据包，则服务器使用 <strong>ICMP</strong>（ping）数据包进行响应，以通知发送方目的地不可达。</li>\n</ul>\n<p>举个例子。假设今天要联系酒店的小蓝，酒店客服接到电话后先查看房间的列表来确保小蓝在客房内，随后转接给小蓝。</p>\n<p>首先，接待员接收到呼叫者要求连接到特定房间的电话。接待员然后需要查看所有房间的清单，以确保客人在房间中可用，并愿意接听电话。碰巧的是，此时如果突然间所有的电话线同时亮起来，那么他们就会很快就变得不堪重负了。</p>\n<p>当服务器接收到每个新的 <strong>UDP</strong> 数据包时，它将通过步骤来处理请求，并利用该过程中的服务器资源。发送 <strong>UDP</strong> 报文时，每个报文将包含源设备的 <strong>IP</strong> 地址。在这种类型的 <strong>DDoS</strong> 攻击期间，攻击者通常不会使用自己的真实 <strong>IP</strong> 地址，而是会欺骗 <strong>UDP</strong> 数据包的源 <strong>IP</strong> 地址，从而阻止攻击者的真实位置被暴露并潜在地饱和来自目标的响应数据包服务器。</p>\n<p>由于目标服务器利用资源检查并响应每个接收到的 <strong>UDP</strong> 数据包的结果，当接收到大量 <strong>UDP</strong> 数据包时，目标的资源可能会迅速耗尽，导致对正常流量的拒绝服务。</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/23dbbc8243a84ed181e088e38bffb37a~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<h3>如何缓解 UDP Flooding？</h3>\n<p>大多数操作系统部分限制了 <strong>ICMP</strong> 报文的响应速率，以中断需要 ICMP 响应的 <strong>DDoS</strong> 攻击。这种缓解的一个缺点是在攻击过程中，合法的数据包也可能被过滤。如果 <strong>UDP Flood</strong> 的容量足够高以使目标服务器的防火墙的状态表饱和，则在服务器级别发生的任何缓解都将不足以应对目标设备上游的瓶颈。</p>\n<h2>HTTP Flood(洪水)</h2>\n<h3>HTTP Flood 是什么？</h3>\n<p>HTTP Flood 是一种大规模的 DDoS（Distributed Denial of Service，分布式拒绝服务）攻击，旨在利用 HTTP 请求使目标服务器不堪重负。目标因请求而达到饱和，且无法响应正常流量后，将出现拒绝服务，拒绝来自实际用户的其他请求。</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/aa64869551d94c8d89fa80eaf4395bfa~tplv-k3u1fbpfcp-zoom-1.png\" alt=\"HTTP 洪水攻击\"></p>\n<h3>HTTP Flood 的攻击原理是什么？</h3>\n<p>HTTP 洪水攻击是“第 7 层”DDoS 攻击的一种。第 7 层是 OSI 模型的应用程序层，指的是 HTTP 等互联网协议。HTTP 是基于浏览器的互联网请求的基础，通常用于加载网页或通过互联网发送表单内容。缓解应用程序层攻击特别复杂，因为恶意流量和正常流量很难区分。</p>\n<p>为了获得最大效率，恶意行为者通常会利用或创建僵尸网络，以最大程度地扩大攻击的影响。通过利用感染了恶意软件的多台设备，攻击者可以发起大量攻击流量来进行攻击。</p>\n<p>HTTP 洪水攻击有两种：</p>\n<ul>\n<li><strong>HTTP GET 攻击</strong>：在这种攻击形式下，多台计算机或其他设备相互协调，向目标服务器发送对图像、文件或其他资产的多个请求。当目标被传入的请求和响应所淹没时，来自正常流量源的其他请求将被拒绝服务。</li>\n<li><strong>HTTP POST 攻击</strong>：一般而言，在网站上提交表单时，服务器必须处理传入的请求并将数据推送到持久层（通常是数据库）。与发送 POST 请求所需的处理能力和带宽相比，处理表单数据和运行必要数据库命令的过程相对密集。这种攻击利用相对资源消耗的差异，直接向目标服务器发送许多 POST 请求，直到目标服务器的容量饱和并拒绝服务为止。</li>\n</ul>\n<h3>如何防护 HTTP Flood？</h3>\n<p>如前所述，缓解第 7 层攻击非常复杂，而且通常要从多方面进行。一种方法是对发出请求的设备实施质询，以测试它是否是机器人，这与在线创建帐户时常用的 CAPTCHA 测试非常相似。通过提出 JavaScript 计算挑战之类的要求，可以缓解许多攻击。</p>\n<p>其他阻止 HTTP 洪水攻击的途径包括使用 Web 应用程序防火墙 (WAF)、管理 IP 信誉数据库以跟踪和有选择地阻止恶意流量，以及由工程师进行动态分析。Cloudflare 具有超过 2000 万个互联网设备的规模优势，能够分析来自各种来源的流量并通过快速更新的 WAF 规则和其他防护策略来缓解潜在的攻击，从而消除应用程序层 DDoS 流量。</p>\n<h2>DNS Flood(洪水)</h2>\n<h3>DNS Flood 是什么？</h3>\n<p>域名系统（DNS）服务器是互联网的“电话簿“；互联网设备通过这些服务器来查找特定 Web 服务器以便访问互联网内容。DNS Flood 攻击是一种分布式拒绝服务（DDoS）攻击，攻击者用大量流量淹没某个域的 DNS 服务器，以尝试中断该域的 DNS 解析。如果用户无法找到电话簿，就无法查找到用于调用特定资源的地址。通过中断 DNS 解析，DNS Flood 攻击将破坏网站、API 或 Web 应用程序响应合法流量的能力。很难将 DNS Flood 攻击与正常的大流量区分开来，因为这些大规模流量往往来自多个唯一地址，查询该域的真实记录，模仿合法流量。</p>\n<h3>DNS Flood 的攻击原理是什么？</h3>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/97ea11a212924900b10d159226783887~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>域名系统的功能是将易于记忆的名称（例如 <a href=\"http://example.com\" target=\"_blank\" rel=\"noopener noreferrer\">example.com</a>）转换成难以记住的网站服务器地址（例如 192.168.0.1），因此成功攻击 DNS 基础设施将导致大多数人无法使用互联网。DNS Flood 攻击是一种相对较新的基于 DNS 的攻击，这种攻击是在高带宽<a href=\"https://www.cloudflare.com/learning/ddos/glossary/internet-of-things-iot/\" target=\"_blank\" rel=\"noopener noreferrer\">物联网（IoT）</a><a href=\"https://www.cloudflare.com/learning/ddos/what-is-a-ddos-botnet/\" target=\"_blank\" rel=\"noopener noreferrer\">僵尸网络</a>（如 <a href=\"https://www.cloudflare.com/learning/ddos/glossary/mirai-botnet/\" target=\"_blank\" rel=\"noopener noreferrer\">Mirai</a>）兴起后激增的。DNS Flood 攻击使用 IP 摄像头、DVR 盒和其他 IoT 设备的高带宽连接直接淹没主要提供商的 DNS 服务器。来自 IoT 设备的大量请求淹没 DNS 提供商的服务，阻止合法用户访问提供商的 DNS 服务器。</p>\n<p>DNS Flood 攻击不同于 <a href=\"https://www.cloudflare.com/zh-cn/learning/ddos/dns-amplification-ddos-attack/\" target=\"_blank\" rel=\"noopener noreferrer\">DNS 放大攻击</a>。与 DNS Flood 攻击不同，DNS 放大攻击反射并放大不安全 DNS 服务器的流量，以便隐藏攻击的源头并提高攻击的有效性。DNS 放大攻击使用连接带宽较小的设备向不安全的 DNS 服务器发送无数请求。这些设备对非常大的 DNS 记录发出小型请求，但在发出请求时，攻击者伪造返回地址为目标受害者。这种放大效果让攻击者能借助有限的攻击资源来破坏较大的目标。</p>\n<h3>如何防护 DNS Flood?</h3>\n<p>DNS Flood 对传统上基于放大的攻击方法做出了改变。借助轻易获得的高带宽僵尸网络，攻击者现能针对大型组织发动攻击。除非被破坏的 IoT 设备得以更新或替换，否则抵御这些攻击的唯一方法是使用一个超大型、高度分布式的 DNS 系统，以便实时监测、吸收和阻止攻击流量。</p>\n<h2>TCP 重置攻击</h2>\n<p>在 <strong>TCP</strong> 重置攻击中，攻击者通过向通信的一方或双方发送伪造的消息，告诉它们立即断开连接，从而使通信双方连接中断。正常情况下，如果客户端收发现到达的报文段对于相关连接而言是不正确的，<strong>TCP</strong> 就会发送一个重置报文段，从而导致 <strong>TCP</strong> 连接的快速拆卸。</p>\n<p><strong>TCP</strong> 重置攻击利用这一机制，通过向通信方发送伪造的重置报文段，欺骗通信双方提前关闭 TCP 连接。如果伪造的重置报文段完全逼真，接收者就会认为它有效，并关闭 <strong>TCP</strong> 连接，防止连接被用来进一步交换信息。服务端可以创建一个新的 <strong>TCP</strong> 连接来恢复通信，但仍然可能会被攻击者重置连接。万幸的是，攻击者需要一定的时间来组装和发送伪造的报文，所以一般情况下这种攻击只对长连接有杀伤力，对于短连接而言，你还没攻击呢，人家已经完成了信息交换。</p>\n<p>从某种意义上来说，伪造 <strong>TCP</strong> 报文段是很容易的，因为 <strong>TCP/IP</strong> 都没有任何内置的方法来验证服务端的身份。有些特殊的 IP 扩展协议（例如 <code>IPSec</code>）确实可以验证身份，但并没有被广泛使用。客户端只能接收报文段，并在可能的情况下使用更高级别的协议（如 <code>TLS</code>）来验证服务端的身份。但这个方法对 <strong>TCP</strong> 重置包并不适用，因为 <strong>TCP</strong> 重置包是 <strong>TCP</strong> 协议本身的一部分，无法使用更高级别的协议进行验证。</p>\n<h2>模拟攻击</h2>\n<blockquote>\n<p>以下实验是在 <code>OSX</code> 系统中完成的，其他系统请自行测试。</p>\n</blockquote>\n<p>现在来总结一下伪造一个 <strong>TCP</strong> 重置报文要做哪些事情：</p>\n<ul>\n<li>嗅探通信双方的交换信息。</li>\n<li>截获一个 <code>ACK</code> 标志位置位 1 的报文段，并读取其 <code>ACK</code> 号。</li>\n<li>伪造一个 TCP 重置报文段（<code>RST</code> 标志位置为 1），其序列号等于上面截获的报文的 <code>ACK</code> 号。这只是理想情况下的方案，假设信息交换的速度不是很快。大多数情况下为了增加成功率，可以连续发送序列号不同的重置报文。</li>\n<li>将伪造的重置报文发送给通信的一方或双方，时其中断连接。</li>\n</ul>\n<p>为了实验简单，我们可以使用本地计算机通过 <code>localhost</code> 与自己通信，然后对自己进行 TCP 重置攻击。需要以下几个步骤：</p>\n<ul>\n<li>在两个终端之间建立一个 TCP 连接。</li>\n<li>编写一个能嗅探通信双方数据的攻击程序。</li>\n<li>修改攻击程序，伪造并发送重置报文。</li>\n</ul>\n<p>下面正式开始实验。</p>\n<blockquote>\n<p>建立 TCP 连接</p>\n</blockquote>\n<p>可以使用 netcat 工具来建立 TCP 连接，这个工具很多操作系统都预装了。打开第一个终端窗口，运行以下命令：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">nc</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> -nvl</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 8000</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>这个命令会启动一个 TCP 服务，监听端口为 <code>8000</code>。接着再打开第二个终端窗口，运行以下命令：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">nc</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 127.0.0.1</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 8000</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>该命令会尝试与上面的服务建立连接，在其中一个窗口输入一些字符，就会通过 TCP 连接发送给另一个窗口并打印出来。</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/df0508cbf26446708cf98f8ad514dbea~tplv-k3u1fbpfcp-zoom-1.gif\" alt></p>\n<blockquote>\n<p>嗅探流量</p>\n</blockquote>\n<p>编写一个攻击程序，使用 Python 网络库 <code>scapy</code> 来读取两个终端窗口之间交换的数据，并将其打印到终端上。代码比较长，下面为一部份，完整代码后台回复 TCP 攻击，代码的核心是调用 <code>scapy</code> 的嗅探方法：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/27feb834aa9d4b629fd938611ac9972e~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>这段代码告诉 <code>scapy</code> 在 <code>lo0</code> 网络接口上嗅探数据包，并记录所有 TCP 连接的详细信息。</p>\n<ul>\n<li><strong>iface</strong> : 告诉 scapy 在 <code>lo0</code>（localhost）网络接口上进行监听。</li>\n<li><strong>lfilter</strong> : 这是个过滤器，告诉 scapy 忽略所有不属于指定的 TCP 连接（通信双方皆为 <code>localhost</code>，且端口号为 <code>8000</code>）的数据包。</li>\n<li><strong>prn</strong> : scapy 通过这个函数来操作所有符合 <code>lfilter</code> 规则的数据包。上面的例子只是将数据包打印到终端，下文将会修改函数来伪造重置报文。</li>\n<li><strong>count</strong> : scapy 函数返回之前需要嗅探的数据包数量。</li>\n</ul>\n<blockquote>\n<p>发送伪造的重置报文</p>\n</blockquote>\n<p>下面开始修改程序，发送伪造的 TCP 重置报文来进行 TCP 重置攻击。根据上面的解读，只需要修改 prn 函数就行了，让其检查数据包，提取必要参数，并利用这些参数来伪造 TCP 重置报文并发送。</p>\n<p>例如，假设该程序截获了一个从（<code>src_ip</code>, <code>src_port</code>）发往 （<code>dst_ip</code>, <code>dst_port</code>）的报文段，该报文段的 ACK 标志位已置为 1，ACK 号为 <code>100,000</code>。攻击程序接下来要做的是：</p>\n<ul>\n<li>由于伪造的数据包是对截获的数据包的响应，所以伪造数据包的源 <code>IP/Port</code> 应该是截获数据包的目的 <code>IP/Port</code>，反之亦然。</li>\n<li>将伪造数据包的 <code>RST</code> 标志位置为 1，以表示这是一个重置报文。</li>\n<li>将伪造数据包的序列号设置为截获数据包的 ACK 号，因为这是发送方期望收到的下一个序列号。</li>\n<li>调用 <code>scapy</code> 的 <code>send</code> 方法，将伪造的数据包发送给截获数据包的发送方。</li>\n</ul>\n<p>对于我的程序而言，只需将这一行取消注释，并注释这一行的上面一行，就可以全面攻击了。按照步骤 1 的方法设置 TCP 连接，打开第三个窗口运行攻击程序，然后在 TCP 连接的其中一个终端输入一些字符串，你会发现 TCP 连接被中断了！</p>\n<blockquote>\n<p>进一步实验</p>\n</blockquote>\n<ol>\n<li>可以继续使用攻击程序进行实验，将伪造数据包的序列号加减 1 看看会发生什么，是不是确实需要和截获数据包的 <code>ACK</code> 号完全相同。</li>\n<li>打开 <code>Wireshark</code>，监听 lo0 网络接口，并使用过滤器 <code>ip.src == 127.0.0.1 &amp;&amp; ip.dst == 127.0.0.1 &amp;&amp; tcp.port == 8000</code> 来过滤无关数据。你可以看到 TCP 连接的所有细节。</li>\n<li>在连接上更快速地发送数据流，使攻击更难执行。</li>\n</ol>\n<h2>中间人攻击</h2>\n<p>猪八戒要向小蓝表白，于是写了一封信给小蓝，结果第三者小黑拦截到了这封信，把这封信进行了篡改，于是乎在他们之间进行搞破坏行动。这个马文才就是中间人，实施的就是中间人攻击。好我们继续聊聊什么是中间人攻击。</p>\n<h3>什么是中间人?</h3>\n<p>攻击中间人攻击英文名叫 Man-in-the-MiddleAttack，简称「MITM 攻击」。指攻击者与通讯的两端分别创建独立的联系，并交换其所收到的数据，使通讯的两端认为他们正在通过一个私密的连接与对方 直接对话，但事实上整个会话都被攻击者完全控制。我们画一张图：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/d69b74e63981472b852797f2fa08976f~tplv-k3u1fbpfcp-zoom-1.png\" alt=\"图片\"></p>\n<p>从这张图可以看到，中间人其实就是攻击者。通过这种原理，有很多实现的用途，比如说，你在手机上浏览不健康网站的时候，手机就会提示你，此网站可能含有病毒，是否继续访问还是做其他的操作等等。</p>\n<h3>中间人攻击的原理是什么？</h3>\n<p>举个例子，我和公司签了一个一份劳动合同，一人一份合同。不晓得哪个可能改了合同内容，不知道真假了，怎么搞？只好找专业的机构来鉴定，自然就要花钱。</p>\n<p>在安全领域有句话：<strong>我们没有办法杜绝网络犯罪，只好想办法提高网络犯罪的成本</strong>。既然没法杜绝这种情况，那我们就想办法提高作案的成本，今天我们就简单了解下基本的网络安全知识，也是面试中的高频面试题了。</p>\n<p>为了避免双方说活不算数的情况，双方引入第三家机构，将合同原文给可信任的第三方机构，只要这个机构不监守自盗，合同就相对安全。</p>\n<p><strong>如果第三方机构内部不严格或容易出现纰漏？</strong></p>\n<p>虽然我们将合同原文给第三方机构了，为了防止内部人员的更改，需要采取什么措施呢</p>\n<p>一种可行的办法是引入 <strong>摘要算法</strong> 。即合同和摘要一起，为了简单的理解摘要。大家可以想象这个摘要为一个函数，这个函数对原文进行了加密，会产生一个唯一的散列值，一旦原文发生一点点变化，那么这个散列值将会变化。</p>\n<h4>有哪些常用的摘要算法呢？</h4>\n<p>目前比较常用的加密算法有消息摘要算法和安全散列算法(<strong>SHA</strong>)。<strong>MD5</strong> 是将任意长度的文章转化为一个 128 位的散列值，可是在 2004 年，<strong>MD5</strong> 被证实了容易发生碰撞，即两篇原文产生相同的摘要。这样的话相当于直接给黑客一个后门，轻松伪造摘要。</p>\n<p>所以在大部分的情况下都会选择 <strong>SHA 算法</strong> 。</p>\n<p><strong>出现内鬼了怎么办？</strong></p>\n<p>看似很安全的场面了，理论上来说杜绝了篡改合同的做法。主要某个员工同时具有修改合同和摘要的权利，那搞事儿就是时间的问题了，毕竟没哪个系统可以完全的杜绝员工接触敏感信息，除非敏感信息都不存在。所以能不能考虑将合同和摘要分开存储呢</p>\n<p><strong>那如何确保员工不会修改合同呢？</strong></p>\n<p>这确实蛮难的，不过办法总比困难多。我们将合同放在双方手中，摘要放在第三方机构，篡改难度进一步加大</p>\n<p><strong>那么员工万一和某个用户串通好了呢？</strong></p>\n<p>看来放在第三方的机构还是不好使，同样存在不小风险。所以还需要寻找新的方案，这就出现了 <strong>数字签名和证书</strong>。</p>\n<h4>数字证书和签名有什么用？</h4>\n<p>同样的，举个例子。Sum 和 Mike 两个人签合同。Sum 首先用 <strong>SHA</strong> 算法计算合同的摘要，然后用自己私钥将摘要加密，得到数字签名。Sum 将合同原文、签名，以及公钥三者都交给 Mike</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/e4b7d6fca78b45c8840c12411b717f2f~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>如果 Sum 想要证明合同是 Mike 的，那么就要使用 Mike 的公钥，将这个签名解密得到摘要 x，然后 Mike 计算原文的 sha 摘要 Y，随后对比 x 和 y，如果两者相等，就认为数据没有被篡改</p>\n<p>在这样的过程中，Mike 是不能更改 Sum 的合同，因为要修改合同不仅仅要修改原文还要修改摘要，修改摘要需要提供 Mike 的私钥，私钥即 Sum 独有的密码，公钥即 Sum 公布给他人使用的密码</p>\n<p>总之，公钥加密的数据只能私钥可以解密。私钥加密的数据只有公钥可以解密，这就是 <strong>非对称加密</strong> 。</p>\n<p>隐私保护？不是吓唬大家，信息是透明的兄 die，不过尽量去维护个人的隐私吧，今天学习对称加密和非对称加密。</p>\n<p>大家先读读这个字&quot;钥&quot;,是读&quot;yao&quot;，我以前也是，其实读&quot;yue&quot;</p>\n<h4>什么是对称加密？</h4>\n<p>对称加密，顾名思义，加密方与解密方使用同一钥匙(秘钥)。具体一些就是，发送方通过使用相应的加密算法和秘钥，对将要发送的信息进行加密；对于接收方而言，使用解密算法和相同的秘钥解锁信息，从而有能力阅读信息。</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/ef81cb5e2f0a4d3d9ac5a44ecf97e3cc~tplv-k3u1fbpfcp-zoom-1.png\" alt=\"图片\"></p>\n<h4>常见的对称加密算法有哪些？</h4>\n<p><strong>DES</strong></p>\n<p>DES 使用的密钥表面上是 64 位的，然而只有其中的 56 位被实际用于算法，其余 8 位可以被用于奇偶校验，并在算法中被丢弃。因此，<strong>DES</strong> 的有效密钥长度为 56 位，通常称 <strong>DES</strong> 的密钥长度为 56 位。假设秘钥为 56 位，采用暴力破 Jie 的方式，其秘钥个数为 2 的 56 次方，那么每纳秒执行一次解密所需要的时间差不多 1 年的样子。当然，没人这么干。<strong>DES</strong> 现在已经不是一种安全的加密方法，主要因为它使用的 56 位密钥过短。</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/9eb3a2bf6cf14132a890bc3447480eeb~tplv-k3u1fbpfcp-zoom-1.jpeg\" alt></p>\n<p><strong>IDEA</strong></p>\n<p>国际数据加密算法(International Data Encryption Algorithm)。秘钥长度 128 位，优点没有专利的限制。</p>\n<p><strong>AES</strong></p>\n<p>当 DES 被破解以后，没过多久推出了 <strong>AES</strong> 算法，提供了三种长度供选择，128 位、192 位和 256，为了保证性能不受太大的影响，选择 128 即可。</p>\n<p><strong>SM1 和 SM4</strong></p>\n<p>之前几种都是国外的，我们国内自行研究了国密 <strong>SM1</strong>和 <strong>SM4</strong>。其中 S 都属于国家标准，算法公开。优点就是国家的大力支持和认可</p>\n<p><strong>总结</strong>：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/578961e3175540e081e1432c409b075a~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<h4>常见的非对称加密算法有哪些？</h4>\n<p>在对称加密中，发送方与接收方使用相同的秘钥。那么在非对称加密中则是发送方与接收方使用的不同的秘钥。其主要解决的问题是防止在秘钥协商的过程中发生泄漏。比如在对称加密中，小蓝将需要发送的消息加密，然后告诉你密码是 123balala,ok,对于其他人而言，很容易就能劫持到密码是 123balala。那么在非对称的情况下，小蓝告诉所有人密码是 123balala,对于中间人而言，拿到也没用，因为没有私钥。所以，非对称密钥其实主要解决了密钥分发的难题。如下图</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/153cf04a0ecc43c38003f3a1ab198cc0~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>其实我们经常都在使用非对称加密，比如使用多台服务器搭建大数据平台 hadoop，为了方便多台机器设置免密登录，是不是就会涉及到秘钥分发。再比如搭建 docker 集群也会使用相关非对称加密算法。</p>\n<p>常见的非对称加密算法：</p>\n<ul>\n<li>\n<p>RSA（RSA 加密算法，RSA Algorithm）：安全性基于大整数分解的计算难度，应用广泛，兼容性好。缺点是性能相对较慢，且密钥越长（如 2048/4096 位）安全性越高，但运算开销也随之增大。</p>\n</li>\n<li>\n<p>ECC：基于椭圆曲线提出。是目前加密强度最高的非对称加密算法</p>\n</li>\n<li>\n<p>SM2：同样基于椭圆曲线问题设计。最大优势就是国家认可和大力支持。</p>\n</li>\n</ul>\n<p>总结：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/28b96fb797904d4b818ee237cdc7614c~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<h4>常见的散列算法有哪些？</h4>\n<p>这个大家应该更加熟悉了，比如我们平常使用的 MD5 校验，在很多时候，我并不是拿来进行加密，而是用来获得唯一性 ID。在做系统的过程中，存储用户的各种密码信息，通常都会通过散列算法，最终存储其散列值。</p>\n<p><strong>MD5</strong>（不推荐）</p>\n<p>MD5 可以用来生成一个 128 位的消息摘要，它是目前应用比较普遍的散列算法，具体的应用场景你可以自行  参阅。虽然，因为算法的缺陷，它的唯一性已经被破解了，但是大部分场景下，这并不会构成安全问题。但是，如果不是长度受限（32 个字符），我还是不推荐你继续使用 <strong>MD5</strong> 的。</p>\n<p><strong>SHA</strong></p>\n<p>安全散列算法。<strong>SHA</strong> 包括<strong>SHA-1</strong>、<strong>SHA-2</strong>和<strong>SHA-3</strong>三个版本。该算法的基本思想是：接收一段明文数据，通过不可逆的方式将其转换为固定长度的密文。简单来说，SHA 将输入数据（即预映射或消息）转化为固定长度、较短的输出值，称为散列值（或信息摘要、信息认证码）。SHA-1 已被证明不够安全，因此逐渐被 SHA-2 取代，而 SHA-3 则作为 SHA 系列的最新版本，采用不同的结构（Keccak 算法）提供更高的安全性和灵活性。</p>\n<p><strong>SM3</strong></p>\n<p>国密算法<strong>SM3</strong>。加密强度和 SHA-256 算法 相差不多。主要是受到了国家的支持。</p>\n<p><strong>总结</strong>：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/79c3c2f72d2f44c7abf2d73a49024495~tplv-k3u1fbpfcp-zoom-1.png\" alt=\"图片\"></p>\n<p><strong>大部分情况下使用对称加密，具有比较不错的安全性。如果需要分布式进行秘钥分发，考虑非对称。如果不需要可逆计算则散列算法。</strong> 因为这段时间有这方面需求，就看了一些这方面的资料，入坑信息安全，就怕以后洗发水都不用买。谢谢大家查看！</p>\n<h4>第三方机构和证书机制有什么用？</h4>\n<p>问题还有，此时如果 Sum 否认给过 Mike 的公钥和合同，不久 gg 了</p>\n<p>所以需要 Sum 过的话做过的事儿需要足够的信誉，这就引入了 <strong>第三方机构和证书机制</strong> 。</p>\n<p>证书之所以会有信用，是因为证书的签发方拥有信用。所以如果 Sum 想让 Mike 承认自己的公钥，Sum 不会直接将公钥给 Mike ，而是提供由第三方机构，含有公钥的证书。如果 Mike 也信任这个机构，法律都认可，那 ik，信任关系成立</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/b1a3dbf87e3e41ff894f39512a10f66d~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>如上图所示，Sum 将自己的申请提交给机构，产生证书的原文。机构用自己的私钥签名 Sum 的申请原文（先根据原文内容计算摘要，再用私钥加密），得到带有签名信息的证书。Mike 拿到带签名信息的证书，通过第三方机构的公钥进行解密，获得 Sum 证书的摘要、证书的原文。有了 Sum 证书的摘要和原文，Mike 就可以进行验签。验签通过，Mike 就可以确认 Sum 的证书的确是第三方机构签发的。</p>\n<p>用上面这样一个机制，合同的双方都无法否认合同。这个解决方案的核心在于需要第三方信用服务机构提供信用背书。这里产生了一个最基础的信任链，如果第三方机构的信任崩溃，比如被黑客攻破，那整条信任链条也就断裂了</p>\n<p>为了让这个信任条更加稳固，就需要环环相扣，打造更长的信任链，避免单点信任风险</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/1481f0409da94ba6bb0fee69bf0996f8~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>上图中，由信誉最好的根证书机构提供根证书，然后根证书机构去签发二级机构的证书；二级机构去签发三级机构的证书；最后有由三级机构去签发 Sum 证书。</p>\n<p>如果要验证 Sum 证书的合法性，就需要用三级机构证书中的公钥去解密 Sum 证书的数字签名。</p>\n<p>如果要验证三级机构证书的合法性，就需要用二级机构的证书去解密三级机构证书的数字签名。</p>\n<p>如果要验证二级结构证书的合法性，就需要用根证书去解密。</p>\n<p>以上，就构成了一个相对长一些的信任链。如果其中一方想要作弊是非常困难的，除非链条中的所有机构同时联合起来，进行欺诈。</p>\n<h3>中间人攻击如何避免?</h3>\n<p>既然知道了中间人攻击的原理也知道了他的危险，现在我们看看如何避免。相信我们都遇到过下面这种状况：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/0dde4b76be6240699312d822a3fe1ed3~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>出现这个界面的很多情况下，都是遇到了中间人攻击的现象，需要对安全证书进行及时地监测。而且大名鼎鼎的 github 网站，也曾遭遇过中间人攻击：</p>\n<p>想要避免中间人攻击的方法目前主要有两个：</p>\n<ul>\n<li>客户端不要轻易相信证书：因为这些证书极有可能是中间人。</li>\n<li>App 可以提前预埋证书在本地：意思是我们本地提前有一些证书，这样其他证书就不能再起作用了。</li>\n</ul>\n<h2>DDOS</h2>\n<p>通过上面的描述，总之即好多种攻击都是 <strong>DDOS</strong> 攻击，所以简单总结下这个攻击相关内容。</p>\n<p>其实，像全球互联网各大公司，均遭受过大量的 <strong>DDoS</strong>。</p>\n<p>2018 年，GitHub 在一瞬间遭到高达 1.35Tbps 的带宽攻击。这次 DDoS 攻击几乎可以堪称是互联网有史以来规模最大、威力最大的 DDoS 攻击了。在 GitHub 遭到攻击后，仅仅一周后，DDoS 攻击又开始对 Google、亚马逊甚至 Pornhub 等网站进行了 DDoS 攻击。后续的 DDoS 攻击带宽最高也达到了 1Tbps。</p>\n<h3>DDoS 攻击究竟是什么？</h3>\n<p>DDos 全名 Distributed Denial of Service，翻译成中文就是<strong>分布式拒绝服务</strong>。指的是处于不同位置的多个攻击者同时向一个或数个目标发动攻击，是一种分布的、协同的大规模攻击方式。单一的 DoS 攻击一般是采用一对一方式的，它利用网络协议和操作系统的一些缺陷，采用<strong>欺骗和伪装</strong>的策略来进行网络攻击，使网站服务器充斥大量要求回复的信息，消耗网络带宽或系统资源，导致网络或系统不胜负荷以至于瘫痪而停止提供正常的网络服务。</p>\n<blockquote>\n<p>举个例子</p>\n</blockquote>\n<p>我开了一家有五十个座位的重庆火锅店，由于用料上等，童叟无欺。平时门庭若市，生意特别红火，而对面二狗家的火锅店却无人问津。二狗为了对付我，想了一个办法，叫了五十个人来我的火锅店坐着却不点菜，让别的客人无法吃饭。</p>\n<p>上面这个例子讲的就是典型的 DDoS 攻击，一般来说是指攻击者利用“肉鸡”对目标网站在较短的时间内发起大量请求，大规模消耗目标网站的主机资源，让它无法正常服务。在线游戏、互联网金融等领域是 DDoS 攻击的高发行业。</p>\n<p>攻击方式很多，比如 <strong>ICMP Flood</strong>、<strong>UDP Flood</strong>、<strong>NTP Flood</strong>、<strong>SYN Flood</strong>、<strong>CC 攻击</strong>、<strong>DNS Query Flood</strong>等等。</p>\n<h3>如何应对 DDoS 攻击？</h3>\n<h4>高防服务器</h4>\n<p>还是拿开的重庆火锅店举例，高防服务器就是我给重庆火锅店增加了两名保安，这两名保安可以让保护店铺不受流氓骚扰，并且还会定期在店铺周围巡逻防止流氓骚扰。</p>\n<p>高防服务器主要是指能独立硬防御 50Gbps 以上的服务器，能够帮助网站拒绝服务攻击，定期扫描网络主节点等，这东西是不错，就是贵~</p>\n<h4>黑名单</h4>\n<p>面对火锅店里面的流氓，我一怒之下将他们拍照入档，并禁止他们踏入店铺，但是有的时候遇到长得像的人也会禁止他进入店铺。这个就是设置黑名单，此方法秉承的就是“错杀一千，也不放一百”的原则，会封锁正常流量，影响到正常业务。</p>\n<h4>DDoS 清洗</h4>\n<p><strong>DDos</strong> 清洗，就是我发现客人进店几分钟以后，但是一直不点餐，我就把他踢出店里。</p>\n<p><strong>DDoS</strong> 清洗会对用户请求数据进行实时监控，及时发现 <strong>DOS</strong> 攻击等异常流量，在不影响正常业务开展的情况下清洗掉这些异常流量。</p>\n<h4>CDN 加速</h4>\n<p>CDN 加速，我们可以这么理解：为了减少流氓骚扰，我干脆将火锅店开到了线上，承接外卖服务，这样流氓找不到店在哪里，也耍不来流氓了。</p>\n<p>在现实中，CDN 服务将网站访问流量分配到了各个节点中，这样一方面隐藏网站的真实 IP，另一方面即使遭遇 <strong>DDoS</strong> 攻击，也可以将流量分散到各个节点中，防止源站崩溃。</p>\n<h2>参考</h2>\n<ul>\n<li>HTTP 洪水攻击 - CloudFlare：<a href=\"https://www.cloudflare.com/zh-cn/learning/ddos/http-flood-ddos-attack/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.cloudflare.com/zh-cn/learning/ddos/http-flood-ddos-attack/</a></li>\n<li>SYN 洪水攻击：<a href=\"https://www.cloudflare.com/zh-cn/learning/ddos/syn-flood-ddos-attack/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.cloudflare.com/zh-cn/learning/ddos/syn-flood-ddos-attack/</a></li>\n<li>什么是 IP 欺骗？：<a href=\"https://www.cloudflare.com/zh-cn/learning/ddos/glossary/ip-spoofing/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.cloudflare.com/zh-cn/learning/ddos/glossary/ip-spoofing/</a></li>\n<li>什么是 DNS 洪水？| DNS 洪水 DDoS 攻击：<a href=\"https://www.cloudflare.com/zh-cn/learning/ddos/dns-flood-ddos-attack/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.cloudflare.com/zh-cn/learning/ddos/dns-flood-ddos-attack/</a></li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/p3-juejin/843fd07074874ee0b695eca659411b42~tplv-k3u1fbpfcp-zoom-1.png",
      "date_published": "2023-02-19T09:28:45.000Z",
      "date_modified": "2026-03-23T08:33:10.000Z",
      "authors": [],
      "tags": [
        "计算机基础"
      ]
    },
    {
      "title": "SQL常见面试题总结（1）",
      "url": "https://javaguide.cn/database/sql/sql-questions-01.html",
      "id": "https://javaguide.cn/database/sql/sql-questions-01.html",
      "summary": "SQL常见面试题总结第一篇，涵盖SELECT检索数据、WHERE条件过滤、ORDER BY排序、DISTINCT去重、LIMIT分页等基础查询操作及牛客真题解析。",
      "content_html": "<blockquote>\n<p>题目来源于：<a href=\"https://www.nowcoder.com/exam/oj?page=1&amp;tab=SQL%E7%AF%87&amp;topicId=298\" target=\"_blank\" rel=\"noopener noreferrer\">牛客题霸 - SQL 必知必会</a></p>\n</blockquote>\n<h2>检索数据</h2>\n<p><code>SELECT</code> 用于从数据库中查询数据。</p>\n<h3>从 Customers 表中检索所有的 ID</h3>\n<p>现有表 <code>Customers</code> 如下：</p>\n<p>| cust_id |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/csdn/d1794312b448516831369f869814ab39.png",
      "date_published": "2023-02-17T05:40:22.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "SQL语法基础知识总结",
      "url": "https://javaguide.cn/database/sql/sql-syntax-summary.html",
      "id": "https://javaguide.cn/database/sql/sql-syntax-summary.html",
      "summary": "SQL语法基础知识总结，系统讲解DDL数据定义、DML数据操作、DQL数据查询、DCL数据控制语言，涵盖表操作、约束、索引、事务、连接查询等核心知识点。",
      "content_html": "<blockquote>\n<p>本文整理完善自下面这两份资料：</p>\n<ul>\n<li><a href=\"https://juejin.cn/post/6844903790571700231\" target=\"_blank\" rel=\"noopener noreferrer\">SQL 语法速成手册</a></li>\n<li><a href=\"https://www.begtut.com/mysql/mysql-tutorial.html\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL 超全教程</a></li>\n</ul>\n</blockquote>\n<h2>基本概念</h2>\n<h3>数据库术语</h3>\n<ul>\n<li><code>数据库（database）</code> - 保存有组织的数据的容器（通常是一个文件或一组文件）。</li>\n<li><code>数据表（table）</code> - 某种特定类型数据的结构化清单。</li>\n<li><code>模式（schema）</code> - 关于数据库和表的布局及特性的信息。模式定义了数据在表中如何存储，包含存储什么样的数据，数据如何分解，各部分信息如何命名等信息。数据库和表都有模式。</li>\n<li><code>列（column）</code> - 表中的一个字段。所有表都是由一个或多个列组成的。</li>\n<li><code>行（row）</code> - 表中的一个记录。</li>\n<li><code>主键（primary key）</code> - 一列（或一组列），其值能够唯一标识表中每一行。</li>\n</ul>\n<h3>SQL 语法</h3>\n<p>SQL（Structured Query Language)，标准 SQL 由 ANSI 标准委员会管理，从而称为 ANSI SQL。各个 DBMS 都有自己的实现，如 PL/SQL、Transact-SQL 等。</p>\n<h4>SQL 语法结构</h4>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/cb684d4c75fc430e92aaee226069c7da~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>SQL 语法结构包括：</p>\n<ul>\n<li><strong><code>子句</code></strong> - 是语句和查询的组成成分。（在某些情况下，这些都是可选的。）</li>\n<li><strong><code>表达式</code></strong> - 可以产生任何标量值，或由列和行的数据库表</li>\n<li><strong><code>谓词</code></strong> - 给需要评估的 SQL 三值逻辑（3VL）（true/false/unknown）或布尔真值指定条件，并限制语句和查询的效果，或改变程序流程。</li>\n<li><strong><code>查询</code></strong> - 基于特定条件检索数据。这是 SQL 的一个重要组成部分。</li>\n<li><strong><code>语句</code></strong> - 可以持久地影响纲要和数据，也可以控制数据库事务、程序流程、连接、会话或诊断。</li>\n</ul>\n<h4>SQL 语法要点</h4>\n<ul>\n<li><strong>SQL 语句不区分大小写</strong>，但是数据库表名、列名和值是否区分，依赖于具体的 DBMS 以及配置。例如：<code>SELECT</code> 与 <code>select</code>、<code>Select</code> 是相同的。</li>\n<li><strong>多条 SQL 语句必须以分号（<code>;</code>）分隔</strong>。</li>\n<li>处理 SQL 语句时，<strong>所有空格都被忽略</strong>。</li>\n</ul>\n<p>SQL 语句可以写成一行，也可以分写为多行。</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 一行 SQL 语句</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">UPDATE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> user </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SET</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> username</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'robot'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">password</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'robot'</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> username </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 'root'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 多行 SQL 语句</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">UPDATE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> user</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SET</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> username</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'robot'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">password</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'robot'</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> username </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 'root'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>SQL 支持三种注释：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">## 注释1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 注释2</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/* 注释3 */</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>SQL 分类</h3>\n<h4>数据定义语言（DDL）</h4>\n<p>数据定义语言（Data Definition Language，DDL）是 SQL 语言集中负责数据结构定义与数据库对象定义的语言。</p>\n<p>DDL 的主要功能是<strong>定义数据库对象</strong>。</p>\n<p>DDL 的核心指令是 <code>CREATE</code>、<code>ALTER</code>、<code>DROP</code>。</p>\n<h4>数据操纵语言（DML）</h4>\n<p>数据操纵语言（Data Manipulation Language, DML）是用于数据库操作，对数据库其中的对象和数据运行访问工作的编程语句。</p>\n<p>DML 的主要功能是 <strong>访问数据</strong>，因此其语法都是以<strong>读写数据库</strong>为主。</p>\n<p>DML 的核心指令是 <code>INSERT</code>、<code>UPDATE</code>、<code>DELETE</code>、<code>SELECT</code>。这四个指令合称 CRUD(Create, Read, Update, Delete)，即增删改查。</p>\n<h4>事务控制语言（TCL）</h4>\n<p>事务控制语言 (Transaction Control Language, TCL) 用于<strong>管理数据库中的事务</strong>。这些用于管理由 DML 语句所做的更改。它还允许将语句分组为逻辑事务。</p>\n<p>TCL 的核心指令是 <code>COMMIT</code>、<code>ROLLBACK</code>。</p>\n<h4>数据控制语言（DCL）</h4>\n<p>数据控制语言 (Data Control Language, DCL) 是一种可对数据访问权进行控制的指令，它可以控制特定用户账户对数据表、查看表、预存程序、用户自定义函数等数据库对象的控制权。</p>\n<p>DCL 的核心指令是 <code>GRANT</code>、<code>REVOKE</code>。</p>\n<p>DCL 以<strong>控制用户的访问权限</strong>为主，因此其指令作法并不复杂，可利用 DCL 控制的权限有：<code>CONNECT</code>、<code>SELECT</code>、<code>INSERT</code>、<code>UPDATE</code>、<code>DELETE</code>、<code>EXECUTE</code>、<code>USAGE</code>、<code>REFERENCES</code>。</p>\n<p>根据不同的 DBMS 以及不同的安全性实体，其支持的权限控制也有所不同。</p>\n<p><strong>我们先来介绍 DML 语句用法。 DML 的主要功能是读写数据库实现增删改查。</strong></p>\n<h2>增删改查</h2>\n<p>增删改查，又称为 CRUD，数据库基本操作中的基本操作。</p>\n<h3>插入数据</h3>\n<p><code>INSERT INTO</code> 语句用于向表中插入新记录。</p>\n<p><strong>插入完整的行</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"># 插入一行</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">INSERT INTO</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> user</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">VALUES</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'root'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'root'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'xxxx@163.com'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"># 插入多行</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">INSERT INTO</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> user</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">VALUES</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'root'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'root'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'xxxx@163.com'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">), (</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">12</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'user1'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'user1'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'xxxx@163.com'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">), (</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">18</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'user2'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'user2'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'xxxx@163.com'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>插入行的一部分</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">INSERT INTO</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> user(username, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">password</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, email)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">VALUES</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'admin'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'admin'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'xxxx@163.com'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>插入查询出来的数据</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">INSERT INTO</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> user(username)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> name</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> account;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>更新数据</h3>\n<p><code>UPDATE</code> 语句用于更新表中的记录。</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">UPDATE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> user</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SET</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> username</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'robot'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">password</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'robot'</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> username </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 'root'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>删除数据</h3>\n<ul>\n<li><code>DELETE</code> 语句用于删除表中的记录。</li>\n<li><code>TRUNCATE TABLE</code> 可以清空表，也就是删除所有行。说明：<code>TRUNCATE</code> 语句不属于 DML 语法而是 DDL 语法。</li>\n</ul>\n<p><strong>删除表中的指定数据</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">DELETE</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> user</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> username </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 'robot'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>清空表中的数据</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">TRUNCATE</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> TABLE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> user;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><h3>查询数据</h3>\n<p><code>SELECT</code> 语句用于从数据库中查询数据。</p>\n<p><code>DISTINCT</code> 用于返回唯一不同的值。它作用于所有列，也就是说所有列的值都相同才算相同。</p>\n<p><code>LIMIT</code> 限制返回的行数。可以有两个参数，第一个参数为起始行，从 0 开始；第二个参数为返回的总行数。</p>\n<ul>\n<li><code>ASC</code>：升序（默认）</li>\n<li><code>DESC</code>：降序</li>\n</ul>\n<p><strong>查询单列</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> prod_name</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> products;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>查询多列</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> prod_id, prod_name, prod_price</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> products;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>查询所有列</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> *</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> products;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>查询不同的值</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT DISTINCT</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">vend_id </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> products;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>限制查询结果</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 返回前 5 行</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> mytable </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">LIMIT</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 5</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> mytable </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">LIMIT</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">5</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 返回第 3 ~ 5 行</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> mytable </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">LIMIT</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>排序</h2>\n<p><code>order by</code> 用于对结果集按照一个列或者多个列进行排序。默认按照升序对记录进行排序，如果需要按照降序对记录进行排序，可以使用 <code>desc</code> 关键字。</p>\n<p><code>order by</code> 对多列排序的时候，先排序的列放前面，后排序的列放后面。并且，不同的列可以有不同的排序规则。</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> products</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> prod_price </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">DESC</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, prod_name </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ASC</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>分组</h2>\n<p><strong><code>group by</code></strong>：</p>\n<ul>\n<li><code>group by</code> 子句将记录分组到汇总行中。</li>\n<li><code>group by</code> 为每个组返回一个记录。</li>\n<li><code>group by</code> 通常还涉及聚合<code>count</code>，<code>max</code>，<code>sum</code>，<code>avg</code> 等。</li>\n<li><code>group by</code> 可以按一列或多列进行分组。</li>\n<li><code>group by</code> 按分组字段进行排序后，<code>order by</code> 可以以汇总字段来进行排序。</li>\n</ul>\n<p><strong>分组</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> cust_name, </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">COUNT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(cust_address) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">AS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> addr_num</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> Customers </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">GROUP BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> cust_name;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>分组后排序</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> cust_name, </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">COUNT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(cust_address) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">AS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> addr_num</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> Customers </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">GROUP BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> cust_name</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">ORDER BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> cust_name </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">DESC</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong><code>having</code></strong>：</p>\n<ul>\n<li><code>having</code> 用于对汇总的 <code>group by</code> 结果进行过滤。</li>\n<li><code>having</code> 一般都是和 <code>group by</code> 连用。</li>\n<li><code>where</code> 和 <code>having</code> 可以在相同的查询中。</li>\n</ul>\n<p><strong>使用 WHERE 和 HAVING 过滤数据</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> cust_name, </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">COUNT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(*) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">AS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> NumberOfOrders</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> Customers</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> cust_email </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">IS NOT NULL</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">GROUP BY</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> cust_name</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">HAVING</span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\"> COUNT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(*) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong><code>having</code> vs <code>where</code></strong>：</p>\n<ul>\n<li><code>where</code>：过滤过滤指定的行，后面不能加聚合函数（分组函数）。<code>where</code> 在<code>group by</code> 前。</li>\n<li><code>having</code>：过滤分组，一般都是和 <code>group by</code> 连用，不能单独使用。<code>having</code> 在 <code>group by</code> 之后。</li>\n</ul>\n<h2>子查询</h2>\n<p>子查询是嵌套在较大查询中的 SQL 查询，也称内部查询或内部选择，包含子查询的语句也称为外部查询或外部选择。简单来说，子查询就是指将一个 <code>select</code> 查询（子查询）的结果作为另一个 SQL 语句（主查询）的数据来源或者判断条件。</p>\n<p>子查询可以嵌入 <code>SELECT</code>、<code>INSERT</code>、<code>UPDATE</code> 和 <code>DELETE</code> 语句中，也可以和 <code>=</code>、<code>&lt;</code>、<code>&gt;</code>、<code>IN</code>、<code>BETWEEN</code>、<code>EXISTS</code> 等运算符一起使用。</p>\n<p>子查询常用在 <code>WHERE</code> 子句和 <code>FROM</code> 子句后边：</p>\n<ul>\n<li>当用于 <code>WHERE</code> 子句时，根据不同的运算符，子查询可以返回单行单列、多行单列、单行多列数据。子查询就是要返回能够作为 <code>WHERE</code> 子句查询条件的值。</li>\n<li>当用于 <code>FROM</code> 子句时，一般返回多行多列数据，相当于返回一张临时表，这样才符合 <code>FROM</code> 后面是表的规则。这种做法能够实现多表联合查询。</li>\n</ul>\n<blockquote>\n<p>注意：MYSQL 数据库从 4.1 版本才开始支持子查询，早期版本是不支持的。</p>\n</blockquote>\n<p>用于 <code>WHERE</code> 子句的子查询的基本语法如下：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">select</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> column_name </span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[, column_name ]</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">from</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">   table1 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[, table2 ]</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">where</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  column_name operator</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">select</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> column_name </span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[, column_name ]</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    from</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> table1 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[, table2 ]</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    [where]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><ul>\n<li>子查询需要放在括号<code>( )</code>内。</li>\n<li><code>operator</code> 表示用于 where 子句的运算符。</li>\n</ul>\n<p>用于 <code>FROM</code> 子句的子查询的基本语法如下：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">select</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> column_name </span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[, column_name ]</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">from</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">select</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> column_name </span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[, column_name ]</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">      from</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> table1 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[, table2 ]</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">      [where]</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">as</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> temp_table_name</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">where</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  condition</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>用于 <code>FROM</code> 的子查询返回的结果相当于一张临时表，所以需要使用 AS 关键字为该临时表起一个名字。</p>\n<p><strong>子查询的子查询</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> cust_name, cust_contact</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> customers</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> cust_id </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">IN</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> cust_id</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                  FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> orders</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                  WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> order_num </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">IN</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> order_num</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                                      FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> orderitems</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                                      WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> prod_id </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 'RGAN01'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">));</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>内部查询首先在其父查询之前执行，以便可以将内部查询的结果传递给外部查询。执行过程可以参考下图：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/c439da1f5d4e4b00bdfa4316b933d764~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<h3>WHERE</h3>\n<ul>\n<li><code>WHERE</code> 子句用于过滤记录，即缩小访问数据的范围。</li>\n<li><code>WHERE</code> 后跟一个返回 <code>true</code> 或 <code>false</code> 的条件。</li>\n<li><code>WHERE</code> 可以与 <code>SELECT</code>，<code>UPDATE</code> 和 <code>DELETE</code> 一起使用。</li>\n<li>可以在 <code>WHERE</code> 子句中使用的操作符。</li>\n</ul>\n<p>| 运算符  | 描述                                                   |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/p3-juejin/cb684d4c75fc430e92aaee226069c7da~tplv-k3u1fbpfcp-zoom-1.png",
      "date_published": "2023-02-17T05:40:22.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "Gradle核心概念总结",
      "url": "https://javaguide.cn/tools/gradle/gradle-core-concepts.html",
      "id": "https://javaguide.cn/tools/gradle/gradle-core-concepts.html",
      "summary": "Gradle 就是一个运行在 JVM 上的自动化的项目构建工具，用来帮助我们自动构建项目。",
      "content_html": "<blockquote>\n<p>这部分内容主要根据 Gradle 官方文档整理，做了对应的删减，主要保留比较重要的部分，不涉及实战，主要是一些重要概念的介绍。</p>\n</blockquote>\n<p>Gradle 这部分内容属于可选内容，可以根据自身需求决定是否学习，目前国内还是使用 Maven 普遍一些。</p>\n<h2>Gradle 介绍</h2>\n<p>Gradle 官方文档是这样介绍的 Gradle 的：</p>\n<blockquote>\n<p>Gradle is an open-source <a href=\"https://en.wikipedia.org/wiki/Build_automation\" target=\"_blank\" rel=\"noopener noreferrer\">build automation</a> tool flexible enough to build almost any type of software. Gradle makes few assumptions about what you’re trying to build or how to build it. This makes Gradle particularly flexible.</p>\n<p>Gradle 是一个开源的构建自动化工具，它足够灵活，可以构建几乎任何类型的软件。Gradle 对你要构建什么或者如何构建它做了很少的假设。这使得 Gradle 特别灵活。</p>\n</blockquote>\n<p>简单来说，Gradle 就是一个运行在 JVM 上的自动化的项目构建工具，用来帮助我们自动构建项目。</p>\n<p>对于开发者来说，Gradle 的主要作用主要有 3 个：</p>\n<ol>\n<li><strong>项目构建</strong>：提供标准的、跨平台的自动化项目构建方式。</li>\n<li><strong>依赖管理</strong>：方便快捷的管理项目依赖的资源（jar 包），避免资源间的版本冲突问题。</li>\n<li><strong>统一开发结构</strong>：提供标准的、统一的项目结构。</li>\n</ol>\n<p>Gradle 构建脚本是使用 Groovy 或 Kotlin 语言编写的，表达能力非常强，也足够灵活。</p>\n<h2>Groovy 介绍</h2>\n<p>Gradle 是运行在 JVM 上的一个程序，它可以使用 Groovy 来编写构建脚本。</p>\n<p>Groovy 是运行在 JVM 上的脚本语言，是基于 Java 扩展的动态语言，它的语法和 Java 非常的相似，可以使用 Java 的类库。Groovy 可以用于面向对象编程，也可以用作纯粹的脚本语言。在语言的设计上它吸纳了 Java、Python、Ruby 和 Smalltalk 语言的优秀特性，比如动态类型转换、闭包和元编程支持。</p>\n<p>我们可以用学习 Java 的方式去学习 Groovy ，学习成本相对来说还是比较低的，即使开发过程中忘记 Groovy 语法，也可以用 Java 语法继续编码。</p>\n<p>基于 JVM 的语言有很多种比如 Groovy，Kotlin，Java，Scala，他们最终都会编译生成 Java 字节码文件并在 JVM 上运行。</p>\n<h2>Gradle 优势</h2>\n<p>Gradle 是新一代的构建系统，具有高效和灵活等诸多优势，广泛用于 Java 开发。不仅 Android 将其作为官方构建系统, 越来越多的 Java 项目比如 Spring Boot 也慢慢迁移到 Gradle。</p>\n<ul>\n<li>在灵活性上，Gradle 支持基于 Groovy 语言编写脚本，侧重于构建过程的灵活性，适合于构建复杂度较高的项目，可以完成非常复杂的构建。</li>\n<li>在粒度性上，Gradle 构建的粒度细化到了每一个 task 之中。并且它所有的 Task 源码都是开源的，在我们掌握了这一整套打包流程后，我们就可以通过去修改它的 Task 去动态改变其执行流程。</li>\n<li>在扩展性上，Gradle 支持插件机制，所以我们可以复用这些插件，就如同复用库一样简单方便。</li>\n</ul>\n<h2>Gradle Wrapper 介绍</h2>\n<p>Gradle 官方文档是这样介绍的 Gradle Wrapper 的：</p>\n<blockquote>\n<p>The recommended way to execute any Gradle build is with the help of the Gradle Wrapper (in short just “Wrapper”). The Wrapper is a script that invokes a declared version of Gradle, downloading it beforehand if necessary. As a result, developers can get up and running with a Gradle project quickly without having to follow manual installation processes saving your company time and money.</p>\n<p>执行 Gradle 构建的推荐方法是借助 Gradle Wrapper(简而言之就是“Wrapper”)。Wrapper 它是一个脚本，调用了已经声明的 Gradle 版本，如果需要的话，可以预先下载它。因此，开发人员可以快速启动并运行 Gradle 项目，而不必遵循手动安装过程，从而为公司节省时间和金钱。</p>\n</blockquote>\n<p>我们可以称 Gradle Wrapper 为 Gradle 包装器，它将 Gradle 再次包装，让所有的 Gradle 构建方法在 Gradle 包装器的帮助下运行。</p>\n<p>Gradle Wrapper 的工作流程图如下（图源<a href=\"https://docs.gradle.org/current/userguide/gradle_wrapper.html\" target=\"_blank\" rel=\"noopener noreferrer\">Gradle Wrapper 官方文档介绍</a>）：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/csdn/efa7a0006b04051e2b84cd116c6ccdfc.png\" alt=\"包装器工作流程\"></p>\n<p>整个流程主要分为下面 3 步：</p>\n<ol>\n<li>首先当我们刚创建的时候，如果指定的版本没有被下载，就先会去 Gradle 的服务器中下载对应版本的压缩包；</li>\n<li>下载完成后需要先进行解压缩并且执行批处理文件；</li>\n<li>后续项目每次构建都会重用这个解压过的 Gradle 版本。</li>\n</ol>\n<p>Gradle Wrapper 会给我们带来下面这些好处：</p>\n<ol>\n<li>在给定的 Gradle 版本上标准化项目，从而实现更可靠和健壮的构建。</li>\n<li>可以让我们的电脑中不安装 Gradle 环境也可以运行 Gradle 项目。</li>\n<li>为不同的用户和执行环境（例如 IDE 或持续集成服务器）提供新的 Gradle 版本就像更改 Wrapper 定义一样简单。</li>\n</ol>\n<h3>生成 Gradle Wrapper</h3>\n<p>如果想要生成 Gradle Wrapper 的话，需要本地配置好 Gradle 环境变量。Gradle 中已经内置了 Wrapper Task，在项目根目录执行执行<code>gradle wrapper</code>命令即可帮助我们生成 Gradle Wrapper。</p>\n<p>执行命令 <code>gradle wrapper</code> 命令时可以指定一些参数来控制 wrapper 的生成。具体有如下两个配置参数：</p>\n<ul>\n<li><code>--gradle-version</code> 用于指定使用的 Gradle 的版本</li>\n<li><code>--gradle-distribution-url</code> 用于指定下载 Gradle 版本的 URL，该值的规则是 <code>http://services.gradle.org/distributions/gradle-${gradleVersion}-bin.zip</code></li>\n</ul>\n<p>执行<code>gradle wrapper</code>命令之后，Gradle Wrapper 就生成完成了，项目根目录中生成如下文件：</p>\n<div class=\"language-plain line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"plain\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-plain\"><span class=\"line\"><span>├── gradle</span></span>\n<span class=\"line\"><span>│   └── wrapper</span></span>\n<span class=\"line\"><span>│       ├── gradle-wrapper.jar</span></span>\n<span class=\"line\"><span>│       └── gradle-wrapper.properties</span></span>\n<span class=\"line\"><span>├── gradlew</span></span>\n<span class=\"line\"><span>└── gradlew.bat</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>每个文件的含义如下：</p>\n<ul>\n<li><code>gradle-wrapper.jar</code>：包含了 Gradle 运行时的逻辑代码。</li>\n<li><code>gradle-wrapper.properties</code>：定义了 Gradle 的版本号和 Gradle 运行时的行为属性。</li>\n<li><code>gradlew</code>：Linux 平台下，用于执行 Gralde 命令的包装器脚本。</li>\n<li><code>gradlew.bat</code>：Windows 平台下，用于执行 Gralde 命令的包装器脚本。</li>\n</ul>\n<p><code>gradle-wrapper.properties</code> 文件的内容如下：</p>\n<div class=\"language-properties line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"properties\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-properties\"><span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#C678DD\">distributionBase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#98C379\">GRADLE_USER_HOME</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#C678DD\">distributionPath</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#98C379\">wrapper/dists</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#C678DD\">distributionUrl</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#98C379\">https\\://services.gradle.org/distributions/gradle-6.0.1-bin.zip</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#C678DD\">zipStoreBase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#98C379\">GRADLE_USER_HOME</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#C678DD\">zipStorePath</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#98C379\">wrapper/dists</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><ul>\n<li><code>distributionBase</code>：Gradle 解包后存储的父目录。</li>\n<li><code>distributionPath</code>：<code>distributionBase</code>指定目录的子目录。<code>distributionBase+distributionPath</code>就是 Gradle 解包后的存放的具体目录。</li>\n<li><code>distributionUrl</code>：Gradle 指定版本的压缩包下载地址。</li>\n<li><code>zipStoreBase</code>：Gradle 压缩包下载后存储父目录。</li>\n<li><code>zipStorePath</code>：<code>zipStoreBase</code>指定目录的子目录。<code>zipStoreBase+zipStorePath</code>就是 Gradle 压缩包的存放位置。</li>\n</ul>\n<h3>更新 Gradle Wrapper</h3>\n<p>更新 Gradle Wrapper 有 2 种方式：</p>\n<ol>\n<li>接修改<code>distributionUrl</code>字段，然后执行 Gradle 命令。</li>\n<li>执行 gradlew 命令<code>gradlew wrapper –-gradle-version [version]</code>。</li>\n</ol>\n<p>下面的命令会将 Gradle 版本升级为 7.6。</p>\n<div class=\"language-shell line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"shell\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-shell\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">gradlew</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> wrapper</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> --gradle-version</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 7.6</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p><code>gradle-wrapper.properties</code> 文件中的 <code>distributionUrl</code> 属性也发生了改变。</p>\n<div class=\"language-properties line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"properties\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-properties\"><span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#C678DD\">distributionUrl</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#98C379\">https\\://services.gradle.org/distributions/gradle-7.6-all.zip</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><h3>自定义 Gradle Wrapper</h3>\n<p>Gradle 已经内置了 Wrapper Task，因此构建 Gradle Wrapper 会生成 Gradle Wrapper 的属性文件，这个属性文件可以通过自定义 Wrapper Task 来设置。比如我们想要修改要下载的 Gralde 版本为 7.6，可以这么设置：</p>\n<div class=\"language-javascript line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"javascript\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-javascript\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">task</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> wrapper</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">type</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">Wrapper</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    gradleVersion</span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '7.6'</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>也可以设置 Gradle 发行版压缩包的下载地址和 Gradle 解包后的本地存储路径等配置。</p>\n<div class=\"language-groovy line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"groovy\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-groovy\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">task </span><span style=\"--shiki-light:#383A42;--shiki-dark:#61AFEF\">wrapper</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">type</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">Wrapper</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    gradleVersion </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '7.6'</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    distributionUrl </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '../../gradle-7.6-bin.zip'</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    distributionPath</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">wrapper</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">dists</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>distributionUrl</code> 属性可以设置为本地的项目目录，你也可以设置为网络地址。</p>\n<h2>Gradle 任务</h2>\n<p>在 Gradle 中，任务(Task)是构建执行的单个工作单元。</p>\n<p>Gradle 的构建是基于 Task 进行的，当你运行项目的时候，实际就是在执行了一系列的 Task 比如编译 Java 源码的 Task、生成 jar 文件的 Task。</p>\n<p>Task 的声明方式如下（还有其他几种声明方式）：</p>\n<div class=\"language-groovy line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"groovy\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-groovy\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 声明一个名字为 helloTask 的 Task</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">task helloTask{</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">     doLast{</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">       println</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Hello\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">     }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>创建一个 Task 后，可以根据需要给 Task 添加不同的 Action，上面的“doLast”就是给队列尾增加一个 Action。</p>\n<div class=\"language-groovy line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"groovy\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-groovy\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> //在Action 队列头部添加Action</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> Task</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> doFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">Action&#x3C;? super Task></span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> action</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> Task</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> doFirst</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">Closure</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> action</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> //在Action 队列尾部添加Action</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> Task</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> doLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">Action&#x3C;? super Task></span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> action</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> Task</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> doLast</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">Closure</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> action</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> //删除所有的Action</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> Task</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> deleteAllActions</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>一个 Task 中可以有多个 Acton，从队列头部开始向队列尾部执行 Acton。</p>\n<p>Action 代表的是一个个函数、方法，每个 Task 都是一堆 Action 按序组成的执行图。</p>\n<p>Task 声明依赖的关键字是<code>dependsOn</code>，支持声明一个或多个依赖：</p>\n<div class=\"language-groovy line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"groovy\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-groovy\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">task first {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> doLast {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">        println</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"+++++first+++++\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">task second {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> doLast {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">        println</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"+++++second+++++\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 指定多个 task 依赖</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">task </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">print</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">dependsOn</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> :[second,first]) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> doLast {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      logger.quiet </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"指定多个task依赖\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 指定一个 task 依赖</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">task </span><span style=\"--shiki-light:#383A42;--shiki-dark:#61AFEF\">third</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">dependsOn</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> : </span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">print</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> doLast {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">      println</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> '+++++third+++++'</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>执行 Task 之前，会先执行它的依赖 Task。</p>\n<p>我们还可以设置默认 Task，脚本中我们不调用默认 Task ，也会执行。</p>\n<div class=\"language-groovy line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"groovy\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-groovy\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">defaultTasks </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'clean'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'run'</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">task clean {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    doLast {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">        println</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 'Default Cleaning!'</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">task run {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    doLast {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">        println</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 'Default Running!'</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>Gradle 本身也内置了很多 Task 比如 copy（复制文件）、delete（删除文件）。</p>\n<div class=\"language-groovy line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"groovy\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-groovy\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">task </span><span style=\"--shiki-light:#383A42;--shiki-dark:#61AFEF\">deleteFile</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">type</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">Delete</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    delete </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"C:</span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\\\</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">Users</span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\\\</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">guide</span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\\\</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">Desktop</span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\\\</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">test\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>Gradle 插件</h2>\n<p>Gradle 提供的是一套核心的构建机制，而 Gradle 插件则是运行在这套机制上的一些具体构建逻辑，其本质上和 <code>.gradle</code> 文件是相同。你可以将 Gradle 插件看作是封装了一系列 Task 并执行的工具。</p>\n<p>Gradle 插件主要分为两类：</p>\n<ul>\n<li>脚本插件：脚本插件就是一个普通的脚本文件，它可以被导入都其他构建脚本中。</li>\n<li>二进制插件 / 对象插件：在一个单独的插件模块中定义，其他模块通过 Plugin ID 应用插件。因为这种方式发布和复用更加友好，我们一般接触到的 Gradle 插件都是指二进制插件的形式。</li>\n</ul>\n<p>虽然 Gradle 插件与 .gradle 文件本质上没有区别，<code>.gradle</code> 文件也能实现 Gradle 插件类似的功能。但是，Gradle 插件使用了独立模块封装构建逻辑，无论是从开发开始使用来看，Gradle 插件的整体体验都更友好。</p>\n<ul>\n<li><strong>逻辑复用：</strong> 将相同的逻辑提供给多个相似项目复用，减少重复维护类似逻辑开销。当然 .gradle 文件也能做到逻辑复用，但 Gradle 插件的封装性更好；</li>\n<li><strong>组件发布：</strong> 可以将插件发布到 Maven 仓库进行管理，其他项目可以使用插件 ID 依赖。当然 .gradle 文件也可以放到一个远程路径被其他项目引用；</li>\n<li><strong>构建配置：</strong> Gradle 插件可以声明插件扩展来暴露可配置的属性，提供定制化能力。当然 .gradle 文件也可以做到，但实现会麻烦些。</li>\n</ul>\n<h2>Gradle 构建生命周期</h2>\n<p>Gradle 构建的生命周期有三个阶段：<strong>初始化阶段，配置阶段</strong>和<strong>运行阶段</strong>。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/csdn/dadbdf59fccd9a2ebf60a2d018541e52.png\" alt></p>\n<p>在初始化阶段与配置阶段之间、配置阶段结束之后、执行阶段结束之后，我们都可以加一些定制化的 Hook。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/csdn/5c297ccc4dac83229ff3e19caee9d1d2.png\" alt></p>\n<h3>初始化阶段</h3>\n<p>Gradle 支持单项目和多项目构建。在初始化阶段，Gradle 确定哪些项目将参与构建，并为每个项目创建一个 <a href=\"https://docs.gradle.org/current/dsl/org.gradle.api.Project.html\" target=\"_blank\" rel=\"noopener noreferrer\">Project 实例</a> 。本质上也就是执行 <code>settings.gradle</code> 脚本，从而读取整个项目中有多少个 Project 实例。</p>\n<h3>配置阶段</h3>\n<p>在配置阶段，Gradle 会解析每个工程的 <code>build.gradle</code> 文件，创建要执行的任务子集和确定各种任务之间的关系，以供执行阶段按照顺序执行，并对任务的做一些初始化配置。</p>\n<p>每个 <code>build.gradle</code> 对应一个 Project 对象，配置阶段执行的代码包括 <code>build.gradle</code> 中的各种语句、闭包以及 Task 中的配置语句。</p>\n<p>在配置阶段结束后，Gradle 会根据 Task 的依赖关系会创建一个 <strong>有向无环图</strong> 。</p>\n<h3>运行阶段</h3>\n<p>在运行阶段，Gradle 根据配置阶段创建和配置的要执行的任务子集，执行任务。</p>\n<h2>参考</h2>\n<ul>\n<li>Gradle 官方文档：<a href=\"https://docs.gradle.org/current/userguide/userguide.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://docs.gradle.org/current/userguide/userguide.html</a></li>\n<li>Gradle 入门教程：<a href=\"https://www.imooc.com/wiki/gradlebase\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.imooc.com/wiki/gradlebase</a></li>\n<li>Groovy 快速入门看这篇就够了：<a href=\"https://cloud.tencent.com/developer/article/1358357\" target=\"_blank\" rel=\"noopener noreferrer\">https://cloud.tencent.com/developer/article/1358357</a></li>\n<li>【Gradle】Gradle 的生命周期详解：<a href=\"https://juejin.cn/post/7067719629874921508\" target=\"_blank\" rel=\"noopener noreferrer\">https://juejin.cn/post/7067719629874921508</a></li>\n<li>手把手带你自定义 Gradle 插件 —— Gradle 系列(2)：<a href=\"https://www.cnblogs.com/pengxurui/p/16281537.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.cnblogs.com/pengxurui/p/16281537.html</a></li>\n<li>Gradle 爬坑指南 -- 理解 Plugin、Task、构建流程：<a href=\"https://juejin.cn/post/6889090530593112077\" target=\"_blank\" rel=\"noopener noreferrer\">https://juejin.cn/post/6889090530593112077</a></li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/csdn/efa7a0006b04051e2b84cd116c6ccdfc.png",
      "date_published": "2023-02-02T10:43:22.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "开发工具"
      ]
    },
    {
      "title": "MySQL自增主键一定是连续的吗？",
      "url": "https://javaguide.cn/database/mysql/mysql-auto-increment-primary-key-continuous.html",
      "id": "https://javaguide.cn/database/mysql/mysql-auto-increment-primary-key-continuous.html",
      "summary": "详解MySQL自增主键不连续的原因，分析唯一键冲突、事务回滚、批量插入等场景下自增值的分配机制，以及InnoDB自增锁模式的配置与影响。",
      "content_html": "<blockquote>\n<p>作者：飞天小牛肉</p>\n<p>原文：<a href=\"https://mp.weixin.qq.com/s/qci10h9rJx_COZbHV3aygQ\" target=\"_blank\" rel=\"noopener noreferrer\">https://mp.weixin.qq.com/s/qci10h9rJx_COZbHV3aygQ</a></p>\n</blockquote>\n<p>众所周知，自增主键可以让聚集索引尽量地保持递增顺序插入，避免了随机查询，从而提高了查询效率。</p>\n<p>但实际上，MySQL 的自增主键并不能保证一定是连续递增的。</p>\n<p>下面举个例子来看下，如下所示创建一张表：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/3e6b80ba50cb425386b80924e3da0d23~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<h2>自增值保存在哪里？</h2>\n<p>使用 <code>insert into test_pk values(null, 1, 1)</code> 插入一行数据，再执行 <code>show create table</code> 命令来看一下表的结构定义：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/c17e46230bd34150966f0d86b2ad5e91~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>上述表的结构定义存放在后缀名为 <code>.frm</code> 的本地文件中，在 MySQL 安装目录下的 data 文件夹下可以找到这个 <code>.frm</code> 文件：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/3ec0514dd7be423d80b9e7f2d52f5902~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>从上述表结构可以看到，表定义里面出现了一个 <code>AUTO_INCREMENT=2</code>，表示下一次插入数据时，如果需要自动生成自增值，会生成 id = 2。</p>\n<p>但需要注意的是，自增值并不会保存在这个表结构也就是 <code>.frm</code> 文件中，不同的引擎对于自增值的保存策略不同：</p>\n<p>1）MyISAM 引擎的自增值保存在数据文件中</p>\n<p>2）InnoDB 引擎的自增值，其实是保存在了内存里，并没有持久化。第一次打开表的时候，都会去找自增值的最大值 <code>max(id)</code>，然后将 <code>max(id)+1</code> 作为这个表当前的自增值。</p>\n<p>举个例子：我们现在表里当前数据行里最大的 id 是 1，AUTO_INCREMENT=2，对吧。这时候，我们删除 id=1 的行，AUTO_INCREMENT 还是 2。</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/61b8dc9155624044a86d91c368b20059~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>但如果马上重启 MySQL 实例，重启后这个表的 AUTO_INCREMENT 就会变成 1。﻿ 也就是说，MySQL 重启可能会修改一个表的 AUTO_INCREMENT 的值。</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/27fdb15375664249a31f88b64e6e5e66~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/dee15f93e65d44d384345a03404f3481~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>以上，是在我本地 MySQL 5.x 版本的实验，实际上，<strong>到了 MySQL 8.0 版本后，自增值的变更记录被放在了 redo log 中，提供了自增值持久化的能力</strong> ，也就是实现了“如果发生重启，表的自增值可以根据 redo log 恢复为 MySQL 重启前的值”</p>\n<p>也就是说对于上面这个例子来说，重启实例后这个表的 AUTO_INCREMENT 仍然是 2。</p>\n<p>理解了 MySQL 自增值到底保存在哪里以后，我们再来看看自增值的修改机制，并以此引出第一种自增值不连续的场景。</p>\n<h2>自增值不连续的场景</h2>\n<h3>自增值不连续场景 1</h3>\n<p>在 MySQL 里面，如果字段 id 被定义为 AUTO_INCREMENT，在插入一行数据的时候，自增值的行为如下：</p>\n<ul>\n<li>如果插入数据时 id 字段指定为 0、null 或未指定值，那么就把这个表当前的 AUTO_INCREMENT 值填到自增字段；</li>\n<li>如果插入数据时 id 字段指定了具体的值，就直接使用语句里指定的值。</li>\n</ul>\n<p>根据要插入的值和当前自增值的大小关系，自增值的变更结果也会有所不同。假设某次要插入的值是 <code>insert_num</code>，当前的自增值是 <code>autoIncrement_num</code>：</p>\n<ul>\n<li>如果 <code>insert_num &lt; autoIncrement_num</code>，那么这个表的自增值不变</li>\n<li>如果 <code>insert_num &gt;= autoIncrement_num</code>，就需要把当前自增值修改为新的自增值</li>\n</ul>\n<p>也就是说，如果插入的 id 是 100，当前的自增值是 90，<code>insert_num &gt;= autoIncrement_num</code>，那么自增值就会被修改为新的自增值即 101</p>\n<p>一定是这样吗？</p>\n<p>非也~</p>\n<p>了解过分布式 id 的小伙伴一定知道，为了避免两个库生成的主键发生冲突，我们可以让一个库的自增 id 都是奇数，另一个库的自增 id 都是偶数</p>\n<p>这个奇数偶数其实是通过 <code>auto_increment_offset</code> 和 <code>auto_increment_increment</code> 这两个参数来决定的，这俩分别用来表示自增的初始值和步长，默认值都是 1。</p>\n<p>所以，上面的例子中生成新的自增值的步骤实际是这样的：从 <code>auto_increment_offset</code> 开始，以 <code>auto_increment_increment</code> 为步长，持续叠加，直到找到第一个大于 100 的值，作为新的自增值。</p>\n<p>所以，这种情况下，自增值可能会是 102，103 等等之类的，就会导致不连续的主键 id。</p>\n<p>更遗憾的是，即使在自增初始值和步长这两个参数都设置为 1 的时候，自增主键 id 也不一定能保证主键是连续的</p>\n<h3>自增值不连续场景 2</h3>\n<p>举个例子，我们现在往表里插入一条 (null,1,1) 的记录，生成的主键是 1，AUTO_INCREMENT= 2，对吧</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/c22c4f2cea234c7ea496025eb826c3bc~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>这时我再执行一条插入 <code>(null,1,1)</code> 的命令，很显然会报错 <code>Duplicate entry</code>，因为我们设置了一个唯一索引字段 <code>a</code>：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/c0325e31398d4fa6bb1cbe08ef797b7f~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>但是，你会惊奇的发现，虽然插入失败了，但自增值仍然从 2 增加到了 3！</p>\n<p>这是为啥？</p>\n<p>我们来分析下这个 insert 语句的执行流程：</p>\n<ol>\n<li>执行器调用 InnoDB 引擎接口准备插入一行记录 (null,1,1);</li>\n<li>InnoDB 发现用户没有指定自增 id 的值，则获取表 <code>test_pk</code> 当前的自增值 2；</li>\n<li>将传入的记录改成 (2,1,1);</li>\n<li>将表的自增值改成 3；</li>\n<li>继续执行插入数据操作，由于已经存在 a=1 的记录，所以报 Duplicate key error，语句返回</li>\n</ol>\n<p>可以看到，自增值修改的这个操作，是在真正执行插入数据的操作之前。</p>\n<p>这个语句真正执行的时候，因为碰到唯一键 a 冲突，所以 id = 2 这一行并没有插入成功，但也没有将自增值再改回去。所以，在这之后，再插入新的数据行时，拿到的自增 id 就是 3。也就是说，出现了自增主键不连续的情况。</p>\n<p>至此，我们已经罗列了两种自增主键不连续的情况：</p>\n<ol>\n<li>自增初始值和自增步长设置不为 1</li>\n<li>唯一键冲突</li>\n</ol>\n<p>除此之外，事务回滚也会导致这种情况</p>\n<h3>自增值不连续场景 3</h3>\n<p>我们现在表里有一行 <code>(1,1,1)</code> 的记录，AUTO_INCREMENT = 3：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/6220fcf7dac54299863e43b6fb97de3e~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>我们先插入一行数据 <code>(null, 2, 2)</code>，也就是 (3, 2, 2) 嘛，并且 AUTO_INCREMENT 变为 4：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/3f02d46437d643c3b3d9f44a004ab269~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>再去执行这样一段 SQL：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/faf5ce4a2920469cae697f845be717f5~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>虽然我们插入了一条 (null, 3, 3) 记录，但是使用 rollback 进行回滚了，所以数据库中是没有这条记录的：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/6cb4c02722674dd399939d3d03a431c1~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>在这种事务回滚的情况下，自增值并没有同样发生回滚！如下图所示，自增值仍然固执地从 4 增加到了 5：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/e6eea1c927424ac7bda34a511ca521ae~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>所以这时候我们再去插入一条数据（null, 3, 3）的时候，主键 id 就会被自动赋为 <code>5</code> 了：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/80da69dd13b543c4a32d6ed832a3c568~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>那么，为什么在出现唯一键冲突或者回滚的时候，MySQL 没有把表的自增值改回去呢？回退回去的话不就不会发生自增 id 不连续了吗？</p>\n<p>事实上，这么做的主要原因是为了提高性能。</p>\n<p>我们直接用反证法来验证：假设 MySQL 在事务回滚的时候会把自增值改回去，会发生什么？</p>\n<p>现在有两个并行执行的事务 A 和 B，在申请自增值的时候，为了避免两个事务申请到相同的自增 id，肯定要加锁，然后顺序申请，对吧。</p>\n<ol>\n<li>假设事务 A 申请到了 id = 1， 事务 B 申请到 id=2，那么这时候表 t 的自增值是 3，之后继续执行。</li>\n<li>事务 B 正确提交了，但事务 A 出现了唯一键冲突，也就是 id = 1 的那行记录插入失败了，那如果允许事务 A 把自增 id 回退，也就是把表的当前自增值改回 1，那么就会出现这样的情况：表里面已经有 id = 2 的行，而当前的自增 id 值是 1。</li>\n<li>接下来，继续执行的其他事务就会申请到 id=2。这时，就会出现插入语句报错“主键冲突”。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/5f26f02e60f643c9a7cab88a9f1bdce9~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>而为了解决这个主键冲突，有两种方法：</p>\n<ol>\n<li>每次申请 id 之前，先判断表里面是否已经存在这个 id，如果存在，就跳过这个 id</li>\n<li>把自增 id 的锁范围扩大，必须等到一个事务执行完成并提交，下一个事务才能再申请自增 id</li>\n</ol>\n<p>很显然，上述两个方法的成本都比较高，会导致性能问题。而究其原因呢，是我们假设的这个 “允许自增 id 回退”。</p>\n<p>因此，InnoDB 放弃了这个设计，语句执行失败也不回退自增 id。也正是因为这样，所以才只保证了自增 id 是递增的，但不保证是连续的。</p>\n<p>综上，已经分析了三种自增值不连续的场景，还有第四种场景：批量插入数据。</p>\n<h3>自增值不连续场景 4</h3>\n<p>对于批量插入数据的语句，MySQL 有一个批量申请自增 id 的策略：</p>\n<ol>\n<li>语句执行过程中，第一次申请自增 id，会分配 1 个；</li>\n<li>1 个用完以后，这个语句第二次申请自增 id，会分配 2 个；</li>\n<li>2 个用完以后，还是这个语句，第三次申请自增 id，会分配 4 个；</li>\n<li>依此类推，同一个语句去申请自增 id，每次申请到的自增 id 个数都是上一次的两倍。</li>\n</ol>\n<p>注意，这里说的批量插入数据，不是在普通的 insert 语句里面包含多个 value 值！！！，因为这类语句在申请自增 id 的时候，是可以精确计算出需要多少个 id 的，然后一次性申请，申请完成后锁就可以释放了。</p>\n<p>而对于 <code>insert … select</code>、replace …… select 和 load data 这种类型的语句来说，MySQL 并不知道到底需要申请多少 id，所以就采用了这种批量申请的策略，毕竟一个一个申请的话实在太慢了。</p>\n<p>举个例子，假设我们现在这个表有下面这些数据：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/6453cfc107f94e3bb86c95072d443472~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>我们创建一个和当前表 <code>test_pk</code> 有相同结构定义的表 <code>test_pk2</code>：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/45248a6dc34f431bba14d434bee2c79e~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>然后使用 <code>insert...select</code> 往 <code>teset_pk2</code> 表中批量插入数据：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/c1b061e86bae484694d15ceb703b10ca~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>可以看到，成功导入了数据。</p>\n<p>再来看下 <code>test_pk2</code> 的自增值是多少：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/0ff9039366154c738331d64ebaf88d3b~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<p>如上分析，是 8 而不是 6</p>\n<p>具体来说，insert……select 实际上往表中插入了 5 行数据 （1 1）（2 2）（3 3）（4 4）（5 5）。但是，这五行数据是分三次申请的自增 id，结合批量申请策略，每次申请到的自增 id 个数都是上一次的两倍，所以：</p>\n<ul>\n<li>第一次申请到了一个 id：id=1</li>\n<li>第二次被分配了两个 id：id=2 和 id=3</li>\n<li>第三次被分配到了 4 个 id：id=4、id = 5、id = 6、id=7</li>\n</ul>\n<p>由于这条语句实际只用上了 5 个 id，所以 id=6 和 id=7 就被浪费掉了。之后，再执行 <code>insert into test_pk2 values(null,6,6)</code>，实际上插入的数据就是（8,6,6)：</p>\n<p><img src=\"https://oss.javaguide.cn/p3-juejin/51612fbac3804cff8c5157df21d6e355~tplv-k3u1fbpfcp-zoom-1.png\" alt></p>\n<h2>小结</h2>\n<p>本文总结下自增值不连续的 4 个场景：</p>\n<ol>\n<li>自增初始值和自增步长设置不为 1</li>\n<li>唯一键冲突</li>\n<li>事务回滚</li>\n<li>批量插入（如 <code>insert...select</code> 语句）</li>\n</ol>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/p3-juejin/3e6b80ba50cb425386b80924e3da0d23~tplv-k3u1fbpfcp-zoom-1.png",
      "date_published": "2023-02-02T09:54:04.000Z",
      "date_modified": "2026-03-23T08:33:10.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "乐观锁和悲观锁详解",
      "url": "https://javaguide.cn/java/concurrent/optimistic-lock-and-pessimistic-lock.html",
      "id": "https://javaguide.cn/java/concurrent/optimistic-lock-and-pessimistic-lock.html",
      "summary": "乐观锁与悲观锁深度对比：详解synchronized/ReentrantLock悲观锁实现、CAS/版本号乐观锁机制、适用场景分析、性能对比与选型建议。",
      "content_html": "<p>如果将悲观锁（Pessimistic Lock）和乐观锁（Optimistic Lock）对应到现实生活中来。悲观锁有点像是一位比较悲观（也可以说是未雨绸缪）的人，总是会假设最坏的情况，避免出现问题。乐观锁有点像是一位比较乐观的人，总是会假设最好的情况，在要出现问题之前快速解决问题。</p>\n<h2>什么是悲观锁？</h2>\n<p>悲观锁总是假设最坏的情况，认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改)，所以每次在获取资源操作的时候都会上锁，这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。也就是说，<strong>共享资源每次只给一个线程使用，其它线程阻塞，用完后再把资源转让给其它线程</strong>。</p>\n<p>像 Java 中<code>synchronized</code>和<code>ReentrantLock</code>等独占锁就是悲观锁思想的实现。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> performSynchronisedTask</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    synchronized</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 需要同步的操作</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Lock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> ReentrantLock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">   // 需要同步的操作</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">} </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>高并发的场景下，激烈的锁竞争会造成线程阻塞，大量阻塞线程会导致系统的上下文切换，增加系统的性能开销。并且，悲观锁还可能会存在死锁问题（线程获得锁的顺序不当时），影响代码的正常运行。</p>\n<h2>什么是乐观锁？</h2>\n<p>乐观锁总是假设最好的情况，认为共享资源每次被访问的时候不会出现问题，线程可以不停地执行，无需加锁也无需等待，只是在提交修改的时候去验证对应的资源（也就是数据）是否被其它线程修改了（具体方法可以使用版本号机制或 CAS 算法）。</p>\n<p>在 Java 中<code>java.util.concurrent.atomic</code>包下面的原子变量类（比如<code>AtomicInteger</code>、<code>LongAdder</code>）就是使用了乐观锁的一种实现方式 <strong>CAS</strong> 实现的。<br>\n<img src=\"https://oss.javaguide.cn/github/javaguide/java/JUC原子类概览-20230814005211968.png\" alt=\"JUC原子类概览\"></p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// LongAdder 在高并发场景下会比 AtomicInteger 和 AtomicLong 的性能更好</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 代价就是会消耗更多的内存空间（空间换时间）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">LongAdder</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> sum </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> LongAdder</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">sum</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">increment</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>高并发的场景下，乐观锁相比悲观锁来说，不存在锁竞争造成线程阻塞，也不会有死锁问题，在性能上往往会更胜一筹。但是，如果冲突频繁发生（写占比非常多的情况），会频繁失败并重试，这样同样会非常影响性能，导致 CPU 飙升。</p>\n<p>不过，大量失败重试的问题也是可以解决的，像我们前面提到的 <code>LongAdder</code>以空间换时间的方式就解决了这个问题。</p>\n<p>理论上来说：</p>\n<ul>\n<li>悲观锁通常多用于写比较多的情况（多写场景，竞争激烈），这样可以避免频繁失败和重试影响性能，悲观锁的开销是固定的。不过，如果乐观锁解决了频繁失败和重试这个问题的话（比如<code>LongAdder</code>），也是可以考虑使用乐观锁的，要视实际情况而定。</li>\n<li>乐观锁通常多用于写比较少的情况（多读场景，竞争较少），这样可以避免频繁加锁影响性能。不过，乐观锁主要针对的对象是单个共享变量（参考<code>java.util.concurrent.atomic</code>包下面的原子变量类）。</li>\n</ul>\n<h2>如何实现乐观锁？</h2>\n<p>乐观锁一般会使用版本号机制或 CAS 算法实现，CAS 算法相对来说更多一些，这里需要格外注意。</p>\n<h3>版本号机制</h3>\n<p>一般是在数据表中加上一个数据版本号 <code>version</code> 字段，表示数据被修改的次数。当数据被修改时，<code>version</code> 值会加一。当线程 A 要更新数据值时，在读取数据的同时也会读取 <code>version</code> 值，在提交更新时，若刚才读取到的 version 值为当前数据库中的 <code>version</code> 值相等时才更新，否则重试更新操作，直到更新成功。</p>\n<p><strong>举一个简单的例子</strong>：假设数据库中帐户信息表中有一个 version 字段，当前值为 1 ；而当前帐户余额字段（ <code>balance</code> ）为 $100 。</p>\n<ol>\n<li>操作员 A 此时将其读出（ <code>version</code>=1 ），并从其帐户余额中扣除 $50（ $100-$50 ）。</li>\n<li>在操作员 A 操作的过程中，操作员 B 也读入此用户信息（ <code>version</code>=1 ），并从其帐户余额中扣除 $20 （ $100-$20 ）。</li>\n<li>操作员 A 完成了修改工作，将数据版本号（ <code>version</code>=1 ），连同帐户扣除后余额（ <code>balance</code>=$50 ），提交至数据库更新，此时由于提交数据版本等于数据库记录当前版本，数据被更新，数据库记录 <code>version</code> 更新为 2 。</li>\n<li>操作员 B 完成了操作，也将版本号（ <code>version</code>=1 ）试图向数据库提交数据（ <code>balance</code>=$80 ），但此时比对数据库记录版本时发现，操作员 B 提交的数据版本号为 1 ，而数据库记录当前版本为 2 ，不满足 “ 提交版本必须等于当前版本才能执行更新 “ 的乐观锁策略，因此，操作员 B 的提交被驳回。</li>\n</ol>\n<p>这样就避免了操作员 B 用基于 <code>version</code>=1 的旧数据修改的结果覆盖操作员 A 的操作结果的可能。</p>\n<h3>CAS 算法</h3>\n<p>CAS 的全称是 <strong>Compare And Swap（比较与交换）</strong> ，用于实现乐观锁，被广泛应用于各大框架中。CAS 的思想很简单，就是用一个预期值和要更新的变量值进行比较，两值相等才会进行更新。</p>\n<p>CAS 是一个原子操作，底层依赖于一条 CPU 的原子指令。</p>\n<blockquote>\n<p><strong>原子操作</strong> 即最小不可拆分的操作，也就是说操作一旦开始，就不能被打断，直到操作完成。</p>\n</blockquote>\n<p>CAS 涉及到三个操作数：</p>\n<ul>\n<li><strong>V</strong>：要更新的变量值(Var)</li>\n<li><strong>E</strong>：预期值(Expected)</li>\n<li><strong>N</strong>：拟写入的新值(New)</li>\n</ul>\n<p>当且仅当 V 的值等于 E 时，CAS 通过原子方式用新值 N 来更新 V 的值。如果不等，说明已经有其它线程更新了 V，则当前线程放弃更新。</p>\n<p><strong>举一个简单的例子</strong>：线程 A 要修改变量 i 的值为 6，i 原值为 1（V = 1，E=1，N=6，假设不存在 ABA 问题）。</p>\n<ol>\n<li>i 与 1 进行比较，如果相等， 则说明没被其他线程修改，可以被设置为 6 。</li>\n<li>i 与 1 进行比较，如果不相等，则说明被其他线程修改，当前线程放弃更新，CAS 操作失败。</li>\n</ol>\n<p>当多个线程同时使用 CAS 操作一个变量时，只有一个会胜出，并成功更新，其余均会失败，但失败的线程并不会被挂起，仅是被告知失败，并且允许再次尝试，当然也允许失败的线程放弃操作。</p>\n<p>关于 CAS 的进一步介绍，可以阅读读者写的这篇文章：<a href=\"/java/concurrent/cas.html\" target=\"_blank\">CAS 详解</a>，其中详细提到了 Java 中 CAS 的实现以及 CAS 存在的一些问题。</p>\n<h2>总结</h2>\n<p>本文详细介绍了乐观锁和悲观锁的概念以及乐观锁常见实现方式：</p>\n<ul>\n<li>悲观锁基于悲观的假设，认为共享资源在每次访问时都会发生冲突，因此在每次操作时都会加锁。这种锁机制会导致其他线程阻塞，直到锁被释放。Java 中的 <code>synchronized</code> 和 <code>ReentrantLock</code> 是悲观锁的典型实现方式。虽然悲观锁能有效避免数据竞争，但在高并发场景下会导致线程阻塞、上下文切换频繁，从而影响系统性能，并且还可能引发死锁问题。</li>\n<li>乐观锁基于乐观的假设，认为共享资源在每次访问时不会发生冲突，因此无须加锁，只需在提交修改时验证数据是否被其他线程修改。Java 中的 <code>AtomicInteger</code> 和 <code>LongAdder</code> 等类通过 CAS（Compare-And-Swap）算法实现了乐观锁。乐观锁避免了线程阻塞和死锁问题，在读多写少的场景中性能优越。但在写操作频繁的情况下，可能会导致大量重试和失败，从而影响性能。</li>\n<li>乐观锁主要通过版本号机制或 CAS 算法实现。版本号机制通过比较版本号确保数据一致性，而 CAS 通过硬件指令实现原子操作，直接比较和交换变量值。</li>\n</ul>\n<p>悲观锁和乐观锁各有优缺点，适用于不同的应用场景。在实际开发中，选择合适的锁机制能够有效提升系统的并发性能和稳定性。</p>\n<h2>参考</h2>\n<ul>\n<li>《Java 并发编程核心 78 讲》</li>\n<li>通俗易懂 悲观锁、乐观锁、可重入锁、自旋锁、偏向锁、轻量/重量级锁、读写锁、各种锁及其 Java 实现！：<a href=\"https://zhuanlan.zhihu.com/p/71156910\" target=\"_blank\" rel=\"noopener noreferrer\">https://zhuanlan.zhihu.com/p/71156910</a></li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/JUC%E5%8E%9F%E5%AD%90%E7%B1%BB%E6%A6%82%E8%A7%88-20230814005211968.png",
      "date_published": "2023-01-31T08:27:36.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "Elasticsearch常见面试题总结(付费)",
      "url": "https://javaguide.cn/database/elasticsearch/elasticsearch-questions-01.html",
      "id": "https://javaguide.cn/database/elasticsearch/elasticsearch-questions-01.html",
      "summary": "Elasticsearch常见面试题总结，涵盖ES核心概念、倒排索引原理、分片与副本机制、查询DSL、聚合分析、集群调优等高频面试知识点。",
      "content_html": "<p><strong>Elasticsearch</strong> 相关的面试题为我的<a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\">知识星球</a>（点击链接即可查看详细介绍以及加入方法）专属内容，已经整理到了<a href=\"/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\">《Java 面试指北》</a>中。</p>\n<p><img src=\"https://oss.javaguide.cn/javamianshizhibei/elasticsearch-questions.png\" alt></p>\n<p>这本<a href=\"/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\">《Java 面试指北》</a>（后端面试通用）的内容经过反复打磨，质量极高，旨在帮助每一位 Java/后端求职者从容应对面试挑战。</p>\n<p><strong>用数据说话：</strong> 截至目前，专栏累计阅读量已突破 <strong>477.1W</strong>，收获点赞 <strong>5,118</strong> 个，评论互动 <strong>1,657</strong> 条。值得一提的是，评论区不仅仅是留言板，更是答疑区——几乎每一条提问，我都会用心回复，确保无疑问遗留。</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/java-interview-guide-statistics-2025.png\" alt></p>\n<p><a href=\"/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\">《Java 面试指北》</a>（点击链接即可查看详细介绍）的部分内容展示如下，你可以将其看作是 <a href=\"https://javaguide.cn/#/\" target=\"_blank\" rel=\"noopener noreferrer\">JavaGuide</a> 的补充完善，两者可以配合使用。</p>\n<p><img src=\"https://oss.javaguide.cn/javamianshizhibei/javamianshizhibei-content-overview.png\" alt=\"《Java 面试指北》内容概览\"></p>\n<p>下面是<a href=\"/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\">《Java 面试指北》</a>收到的部分球友的真实反馈：</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/praise-that-the-mianshizhibei-received.png\" alt=\"《Java 面试指北》 收到的部分球友的真实反馈\"></p>\n<p>如果需要面试辅导（比如简历优化、一对一模拟问答、高频考点突击资料等），欢迎了解我的<a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\">知识星球</a>。已经坚持维护六年，内容持续更新，虽然是白菜价（0.4 元/天），但质量很高、服务也很全面，主打一个良心！</p>\n<p>下面是星球提供的部分服务（点击下方图片即可获取知识星球的详细介绍）：</p>\n<p><a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/xingqiufuwu.png\" alt=\"星球服务\"></a></p>\n<p>下面是今年收到了部分好评，每一条都是真实存在的。我看到很多培训班或者机构通过虚构一些不存在的好评来欺骗他人购买高价服务（行业内非常常见），真的很难理解。</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/praise-that-the-planet-received.png\" alt=\"球友对星球的真实评价\"></p>\n<p><strong>我有自己的原则，不割韭菜，用心做内容，真心希望帮助到你！</strong> 如果你感兴趣的话，不妨花 3 分钟左右看看星球的详细介绍：<a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\">JavaGuide 知识星球详细介绍</a> 。</p>\n<p>这里再送一张 <strong>30</strong> 元的星球专属优惠券，数量有限（价格即将上调。老用户续费半价 ，微信扫码即可续费）！</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuan-30.jpg\" alt=\"知识星球30元优惠卷\"></p>\n<p>🚀 <strong>入圈必做</strong>（干货满满，一定要看！）：</p>\n<ol>\n<li><a href=\"https://t.zsxq.com/0d18KSarv\" target=\"_blank\" rel=\"noopener noreferrer\">星球使用指南</a></li>\n<li><a href=\"https://t.zsxq.com/12uSKgTIm\" target=\"_blank\" rel=\"noopener noreferrer\">优质主题汇总</a></li>\n</ol>\n<p><strong>无任何套路，无任何潜在收费项。用心做内容，不割韭菜！</strong></p>\n<p>不过， <strong>一定要确定需要再进</strong> 。并且， <strong>三天之内觉得内容不满意可以全额退款</strong> 。</p>\n",
      "image": "https://oss.javaguide.cn/javamianshizhibei/elasticsearch-questions.png",
      "date_published": "2023-01-29T03:31:13.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "MySQL执行计划分析",
      "url": "https://javaguide.cn/database/mysql/mysql-query-execution-plan.html",
      "id": "https://javaguide.cn/database/mysql/mysql-query-execution-plan.html",
      "summary": "详解MySQL EXPLAIN执行计划的各列含义，包括id、select_type、type、key、rows、Extra等关键字段解读，帮助你分析SQL性能瓶颈并进行针对性优化。",
      "content_html": "<p>优化 SQL 的第一步应该是读懂 SQL 的执行计划。本篇文章，我们一起来学习下 MySQL <code>EXPLAIN</code> 执行计划相关知识。</p>\n<blockquote>\n<p><strong>版本说明</strong>：本文内容基于 MySQL 5.7+ 和 8.0+ 版本。<code>filtered</code> 和 <code>partitions</code> 列在 MySQL 5.7+ 可用，<code>EXPLAIN ANALYZE</code> 和 Hash Join 特性需要 MySQL 8.0.18+ 和 8.0.20+。</p>\n</blockquote>\n<h2>什么是执行计划？</h2>\n<p><strong>执行计划</strong> 是指一条 SQL 语句在经过 <strong>MySQL 查询优化器</strong> 的优化后，具体的执行方式。</p>\n<p>执行计划通常用于 SQL 性能分析、优化等场景。通过 <code>EXPLAIN</code> 的结果，可以了解到如数据表的查询顺序、数据查询操作的操作类型、哪些索引可以被命中、哪些索引实际会命中、每个数据表有多少行记录被查询等信息。</p>\n<h2>如何获取执行计划？</h2>\n<p>MySQL 为我们提供了 <code>EXPLAIN</code> 命令，来获取执行计划的相关信息。</p>\n<p>需要注意的是，标准 <code>EXPLAIN</code> 语句并不会真的去执行相关的语句，而是通过查询优化器对语句进行分析，找出最优的查询方案，并显示对应的信息。</p>\n<p>MySQL 8.0.18 引入了 <code>EXPLAIN ANALYZE</code>，它会<strong>真正执行</strong>查询并输出每个步骤的实际耗时与行数，比标准 <code>EXPLAIN</code> 的估算数据更可靠，适合在测试环境深度排查慢查询：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">mysql</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> EXPLAIN ANALYZE </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> users </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> age </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 25</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\\G</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">*************************** </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">. </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">row</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ***************************</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">EXPLAIN: -</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> Covering </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">index</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> lookup </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">on</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> users </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">using</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> idx_age_score_name (age</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">25</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(cost</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">52</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> rows</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">12</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) (actual </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">time</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0272</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">..</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0344</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> rows</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">12</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> loops</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>此外，<code>EXPLAIN FORMAT=JSON</code> 可以输出优化器的成本模型数据（<code>query_cost</code>），比表格形式更能反映各步骤的实际代价，在多表 JOIN 或子查询调优时尤为有用：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">mysql</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> EXPLAIN FORMAT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">JSON</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> users </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> age </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 25</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">\\G</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">*************************** </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">. </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">row</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> ***************************</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">EXPLAIN: {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">  \"query_block\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">    \"select_id\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">    \"cost_info\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">      \"query_cost\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"1.52\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    },</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">    \"table\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">      \"table_name\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"users\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">      \"access_type\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"ref\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">      \"key\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"idx_age_score_name\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">      \"rows_examined_per_scan\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">12</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">      \"filtered\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"100.00\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">      \"using_index\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: true</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>EXPLAIN</code> 执行计划支持 <code>SELECT</code>、<code>DELETE</code>、<code>INSERT</code>、<code>REPLACE</code> 以及 <code>UPDATE</code> 语句。我们一般多用于分析 <code>SELECT</code> 查询语句，使用起来非常简单，语法如下：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">EXPLAIN </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> 查询语句；</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>我们简单来看下一条查询语句的执行计划：</p>\n<p><strong>示例 1：单表查询（使用索引）</strong></p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">-- 表结构：users(id, age, score, name, address)，联合索引 idx_age_score_name(age, score, name)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">mysql</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> EXPLAIN </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">SELECT</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> * </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">FROM</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> users </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">WHERE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> age </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 25</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">+</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div>",
      "date_published": "2023-01-14T10:33:11.000Z",
      "date_modified": "2026-03-10T15:38:18.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "NoSQL基础知识总结",
      "url": "https://javaguide.cn/database/nosql.html",
      "id": "https://javaguide.cn/database/nosql.html",
      "summary": "NoSQL数据库基础知识总结，包括NoSQL与SQL的区别、NoSQL的优势、四种NoSQL数据库类型（键值、文档、图形、宽列）及其代表产品Redis、MongoDB、Neo4j等的应用场景。",
      "content_html": "<h2>NoSQL 是什么？</h2>\n<p>NoSQL（Not Only SQL 的缩写）泛指非关系型的数据库，主要针对的是键值、文档以及图形类型数据存储。并且，NoSQL 数据库天生支持分布式，数据冗余和数据分片等特性，旨在提供可扩展的高可用高性能数据存储解决方案。</p>\n<p>一个常见的误解是 NoSQL 数据库或非关系型数据库不能很好地存储关系型数据。NoSQL 数据库可以存储关系型数据—它们与关系型数据库的存储方式不同。</p>\n<p>NoSQL 数据库代表：HBase、Cassandra、MongoDB、Redis。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/mongodb/sql-nosql-tushi.png\" alt></p>\n<h2>SQL 和 NoSQL 有什么区别？</h2>\n<p>|              | SQL 数据库                                                                 | NoSQL 数据库                                                                                                                            |<br>\n| :</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/database/mongodb/sql-nosql-tushi.png",
      "date_published": "2023-01-12T09:46:41.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "MongoDB常见面试题总结（上）",
      "url": "https://javaguide.cn/database/mongodb/mongodb-questions-01.html",
      "id": "https://javaguide.cn/database/mongodb/mongodb-questions-01.html",
      "summary": "MongoDB常见面试题总结上篇，详解MongoDB基础概念、存储结构、数据类型、副本集高可用、分片集群水平扩展等核心知识点，助力后端面试准备。",
      "content_html": "<blockquote>\n<p>少部分内容参考了 MongoDB 官方文档的描述，在此说明一下。</p>\n</blockquote>\n<h2>MongoDB 基础</h2>\n<h3>MongoDB 是什么？</h3>\n<p>MongoDB 是一个基于 <strong>分布式文件存储</strong> 的开源 NoSQL 数据库系统，由 <strong>C++</strong> 编写的。MongoDB 提供了 <strong>面向文档</strong> 的存储方式，操作起来比较简单和容易，支持“<strong>无模式</strong>”的数据建模，可以存储比较复杂的数据类型，是一款非常流行的 <strong>文档类型数据库</strong> 。</p>\n<p>在高负载的情况下，MongoDB 天然支持水平扩展和高可用，可以很方便地添加更多的节点/实例，以保证服务性能和可用性。在许多场景下，MongoDB 可以用于代替传统的关系型数据库或键/值存储方式，皆在为 Web 应用提供可扩展的高可用高性能数据存储解决方案。</p>\n<h3>MongoDB 的存储结构是什么？</h3>\n<p>MongoDB 的存储结构区别于传统的关系型数据库，主要由如下三个单元组成：</p>\n<ul>\n<li><strong>文档（Document）</strong>：MongoDB 中最基本的单元，由 BSON 键值对（key-value）组成，类似于关系型数据库中的行（Row）。</li>\n<li><strong>集合（Collection）</strong>：一个集合可以包含多个文档，类似于关系型数据库中的表（Table）。</li>\n<li><strong>数据库（Database）</strong>：一个数据库中可以包含多个集合，可以在 MongoDB 中创建多个数据库，类似于关系型数据库中的数据库（Database）。</li>\n</ul>\n<p>也就是说，MongoDB 将数据记录存储为文档 （更具体来说是<a href=\"https://www.mongodb.com/docs/manual/core/document/#std-label-bson-document-format\" target=\"_blank\" rel=\"noopener noreferrer\">BSON 文档</a>），这些文档在集合中聚集在一起，数据库中存储一个或多个文档集合。</p>\n<p><strong>SQL 与 MongoDB 常见术语对比</strong>：</p>\n<p>| SQL                      | MongoDB                         |<br>\n|</p>\n",
      "image": "https://oss.javaguide.cn/github/javaguide/database/mongodb/crud-annotated-document..png",
      "date_published": "2023-01-12T09:46:41.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "MongoDB常见面试题总结（下）",
      "url": "https://javaguide.cn/database/mongodb/mongodb-questions-02.html",
      "id": "https://javaguide.cn/database/mongodb/mongodb-questions-02.html",
      "summary": "MongoDB常见面试题总结下篇，深入讲解MongoDB各类索引（单字段、复合、多键、文本、地理位置、TTL）的原理、使用场景和查询优化技巧。",
      "content_html": "<h2>MongoDB 索引</h2>\n<h3>MongoDB 索引有什么用?</h3>\n<p>和关系型数据库类似，MongoDB 中也有索引。索引的目的主要是用来提高查询效率，如果没有索引的话，MongoDB 必须执行 <strong>集合扫描</strong> ，即扫描集合中的每个文档，以选择与查询语句匹配的文档。如果查询存在合适的索引，MongoDB 可以使用该索引来限制它必须检查的文档数量。并且，MongoDB 可以使用索引中的排序返回排序后的结果。</p>\n<p>虽然索引可以显著缩短查询时间，但是使用索引、维护索引是有代价的。在执行写入操作时，除了要更新文档之外，还必须更新索引，这必然会影响写入的性能。因此，当有大量写操作而读操作少时，或者不考虑读操作的性能时，都不推荐建立索引。</p>\n<h3>MongoDB 支持哪些类型的索引？</h3>\n<p><strong>MongoDB 支持多种类型的索引，包括单字段索引、复合索引、多键索引、哈希索引、文本索引、 地理位置索引等，每种类型的索引有不同的使用场合。</strong></p>\n<ul>\n<li><strong>单字段索引：</strong> 建立在单个字段上的索引，索引创建的排序顺序无所谓，MongoDB 可以头/尾开始遍历。</li>\n<li><strong>复合索引：</strong> 建立在多个字段上的索引，也可以称之为组合索引、联合索引。</li>\n<li><strong>多键索引</strong>：MongoDB 的一个字段可能是数组，在对这种字段创建索引时，就是多键索引。MongoDB 会为数组的每个值创建索引。就是说你可以按照数组里面的值做条件来查询，这个时候依然会走索引。</li>\n<li><strong>哈希索引</strong>：按数据的哈希值索引，用在哈希分片集群上。</li>\n<li><strong>文本索引：</strong> 支持对字符串内容的文本搜索查询。文本索引可以包含任何值为字符串或字符串元素数组的字段。一个集合只能有一个文本搜索索引，但该索引可以覆盖多个字段。MongoDB 虽然支持全文索引，但是性能低下，暂时不建议使用。</li>\n<li><strong>地理位置索引：</strong> 基于经纬度的索引，适合 2D 和 3D 的位置查询。</li>\n<li><strong>唯一索引</strong>：确保索引字段不会存储重复值。如果集合已经存在了违反索引的唯一约束的文档，则后台创建唯一索引会失败。</li>\n<li><strong>TTL 索引</strong>：TTL 索引提供了一个过期机制，允许为每一个文档设置一个过期时间，当一个文档达到预设的过期时间之后就会被删除。</li>\n<li>……</li>\n</ul>\n<h3>复合索引中字段的顺序有影响吗？</h3>\n<p>复合索引中字段的顺序非常重要，例如下图中的复合索引由<code>{userid:1, score:-1}</code>组成，则该复合索引首先按照<code>userid</code>升序排序；然后再每个<code>userid</code>的值内，再按照<code>score</code>降序排序。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/mongodb/mongodb-composite-index.png\" alt=\"复合索引\"></p>\n<p>在复合索引中，按照何种方式排序，决定了该索引在查询中是否能被应用到。</p>\n<p>走复合索引的排序：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">db</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">s2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.find().sort({</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"userid\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"score\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">})</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">db</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">s2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.find().sort({</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"userid\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"score\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">})</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>不走复合索引的排序：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">db</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">s2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.find().sort({</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"userid\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"score\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">})</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">db</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">s2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.find().sort({</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"userid\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"score\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">})</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">db</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">s2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.find().sort({</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"score\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"userid\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">})</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">db</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">s2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.find().sort({</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"score\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"userid\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">})</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">db</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">s2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.find().sort({</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"score\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"userid\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">})</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">db</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">s2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.find().sort({</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"score\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"userid\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">})</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>我们可以通过 explain 进行分析：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">db</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">s2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.find().sort({</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"score\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"userid\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}).explain()</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><h3>复合索引遵循左前缀原则吗？</h3>\n<p><strong>MongoDB 的复合索引遵循左前缀原则</strong>：拥有多个键的索引，可以同时得到所有这些键的前缀组成的索引，但不包括除左前缀之外的其他子集。比如说，有一个类似 <code>{a: 1, b: 1, c: 1, ..., z: 1}</code> 这样的索引，那么实际上也等于有了 <code>{a: 1}</code>、<code>{a: 1, b: 1}</code>、<code>{a: 1, b: 1, c: 1}</code> 等一系列索引，但是不会有 <code>{b: 1}</code> 这样的非左前缀的索引。</p>\n<h3>什么是 TTL 索引？</h3>\n<p>TTL 索引提供了一个过期机制，允许为每一个文档设置一个过期时间 <code>expireAfterSeconds</code> ，当一个文档达到预设的过期时间之后就会被删除。TTL 索引除了有 <code>expireAfterSeconds</code> 属性外，和普通索引一样。</p>\n<p>数据过期对于某些类型的信息很有用，比如机器生成的事件数据、日志和会话信息，这些信息只需要在数据库中保存有限的时间。</p>\n<p><strong>TTL 索引运行原理</strong>：</p>\n<ul>\n<li>MongoDB 会开启一个后台线程读取该 TTL 索引的值来判断文档是否过期，但不会保证已过期的数据会立马被删除，因后台线程每 60 秒触发一次删除任务，且如果删除的数据量较大，会存在上一次的删除未完成，而下一次的任务已经开启的情况，导致过期的数据也会出现超过了数据保留时间 60 秒以上的现象。</li>\n<li>对于副本集而言，TTL 索引的后台进程只会在 Primary 节点开启，在从节点会始终处于空闲状态，从节点的数据删除是由主库删除后产生的 oplog 来做同步。</li>\n</ul>\n<p><strong>TTL 索引限制</strong>：</p>\n<ul>\n<li>TTL 索引是单字段索引。复合索引不支持 TTL</li>\n<li><code>_id</code>字段不支持 TTL 索引。</li>\n<li>无法在上限集合(Capped Collection)上创建 TTL 索引，因为 MongoDB 无法从上限集合中删除文档。</li>\n<li>如果某个字段已经存在非 TTL 索引，那么在该字段上无法再创建 TTL 索引。</li>\n</ul>\n<h3>什么是覆盖索引查询？</h3>\n<p>根据官方文档介绍，覆盖查询是以下的查询：</p>\n<ul>\n<li>所有的查询字段是索引的一部分。</li>\n<li>结果中返回的所有字段都在同一索引中。</li>\n<li>查询中没有字段等于<code>null</code>。</li>\n</ul>\n<p>由于所有出现在查询中的字段是索引的一部分， MongoDB 无需在整个数据文档中检索匹配查询条件和返回使用相同索引的查询结果。因为索引存在于内存中，从索引中获取数据比通过扫描文档读取数据要快得多。</p>\n<p>举个例子：我们有如下 <code>users</code> 集合:</p>\n<div class=\"language-json line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"json\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-json\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">{</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">   \"_id\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#FFFFFF\">ObjectId(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"53402597d852426020000002\"</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#FFFFFF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">   \"contact\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"987654321\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">   \"dob\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"01-01-1991\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">   \"gender\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"M\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">   \"name\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Tom Benzamin\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">   \"user_name\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">: </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"tombenzamin\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>我们在 <code>users</code> 集合中创建联合索引，字段为 <code>gender</code> 和 <code>user_name</code> :</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">db</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">users</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.ensureIndex({gender:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,user_name:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">})</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>现在，该索引会覆盖以下查询：</p>\n<div class=\"language-sql line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"sql\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-sql\"><span class=\"line\"><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">db</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">users</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.find({gender:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"M\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">},{user_name:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,_id:</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">})</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><p>为了让指定的索引覆盖查询，必须显式地指定 <code>_id: 0</code> 来从结果中排除 <code>_id</code> 字段，因为索引不包括 <code>_id</code> 字段。</p>\n<h2>MongoDB 高可用</h2>\n<h3>复制集群</h3>\n<h4>什么是复制集群？</h4>\n<p>MongoDB 的复制集群又称为副本集群，是一组维护相同数据集合的 mongod 进程。</p>\n<p>客户端连接到整个 Mongodb 复制集群，主节点机负责整个复制集群的写，从节点可以进行读操作，但默认还是主节点负责整个复制集群的读。主节点发生故障时，自动从从节点中选举出一个新的主节点，确保集群的正常使用，这对于客户端来说是无感知的。</p>\n<p>通常来说，一个复制集群包含 1 个主节点（Primary），多个从节点（Secondary）以及零个或 1 个仲裁节点（Arbiter）。</p>\n<ul>\n<li><strong>主节点</strong>：整个集群的写操作入口，接收所有的写操作，并将集合所有的变化记录到操作日志中，即 oplog。主节点挂掉之后会自动选出新的主节点。</li>\n<li><strong>从节点</strong>：从主节点同步数据，在主节点挂掉之后选举新节点。不过，从节点可以配置成 0 优先级，阻止它在选举中成为主节点。</li>\n<li><strong>仲裁节点</strong>：这个是为了节约资源或者多机房容灾用，只负责主节点选举时投票不存数据，保证能有节点获得多数赞成票。</li>\n</ul>\n<p>下图是一个典型的三成员副本集群：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/mongodb/replica-set-read-write-operations-primary.png\" alt></p>\n<p>主节点与备节点之间是通过 <strong>oplog（操作日志）</strong> 来同步数据的。oplog 是 local 库下的一个特殊的 <strong>上限集合(Capped Collection)</strong> ，用来保存写操作所产生的增量日志，类似于 MySQL 中 的 Binlog。</p>\n<blockquote>\n<p>上限集合类似于定长的循环队列，数据顺序追加到集合的尾部，当集合空间达到上限时，它会覆盖集合中最旧的文档。上限集合的数据将会被顺序写入到磁盘的固定空间内，所以，I/O 速度非常快，如果不建立索引，性能更好。</p>\n</blockquote>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/mongodb/replica-set-primary-with-two-secondaries.png\" alt></p>\n<p>当主节点上的一个写操作完成后，会向 oplog 集合写入一条对应的日志，而从节点则通过这个 oplog 不断拉取到新的日志，在本地进行回放以达到数据同步的目的。</p>\n<p>副本集最多有一个主节点。 如果当前主节点不可用，一个选举会抉择出新的主节点。MongoDB 的节点选举规则能够保证在 Primary 挂掉之后选取的新节点一定是集群中数据最全的一个。</p>\n<h4>为什么要用复制集群？</h4>\n<ul>\n<li><strong>实现 failover</strong>：提供自动故障恢复的功能，主节点发生故障时，自动从从节点中选举出一个新的主节点，确保集群的正常使用，这对于客户端来说是无感知的。</li>\n<li><strong>实现读写分离</strong>：我们可以设置从节点上可以读取数据，主节点负责写入数据，这样的话就实现了读写分离，减轻了主节点读写压力过大的问题。MongoDB 4.0 之前版本如果主库压力不大,不建议读写分离，因为写会阻塞读，除非业务对响应时间不是非常关注以及读取历史数据接受一定时间延迟。</li>\n</ul>\n<h3>分片集群</h3>\n<h4>什么是分片集群？</h4>\n<p>分片集群是 MongoDB 的分布式版本，相较副本集，分片集群数据被均衡的分布在不同分片中， 不仅大幅提升了整个集群的数据容量上限，也将读写的压力分散到不同分片，以解决副本集性能瓶颈的难题。</p>\n<p>MongoDB 的分片集群由如下三个部分组成（下图来源于<a href=\"https://www.mongodb.com/docs/manual/sharding/\" target=\"_blank\" rel=\"noopener noreferrer\">官方文档对分片集群的介绍</a>）：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/mongodb/sharded-cluster-production-architecture.png\" alt></p>\n<ul>\n<li><strong>Config Servers</strong>：配置服务器，本质上是一个 MongoDB 的副本集，负责存储集群的各种元数据和配置，如分片地址、Chunks 等</li>\n<li><strong>Mongos</strong>：路由服务，不存具体数据，从 Config 获取集群配置讲请求转发到特定的分片，并且整合分片结果返回给客户端。</li>\n<li><strong>Shard</strong>：每个分片是整体数据的一部分子集，从 MongoDB3.6 版本开始，每个 Shard 必须部署为副本集（replica set）架构</li>\n</ul>\n<h4>为什么要用分片集群？</h4>\n<p>随着系统数据量以及吞吐量的增长，常见的解决办法有两种：垂直扩展和水平扩展。</p>\n<p>垂直扩展通过增加单个服务器的能力来实现，比如磁盘空间、内存容量、CPU 数量等；水平扩展则通过将数据存储到多个服务器上来实现，根据需要添加额外的服务器以增加容量。</p>\n<p>类似于 Redis Cluster，MongoDB 也可以通过分片实现 <strong>水平扩展</strong> 。水平扩展这种方式更灵活，可以满足更大数据量的存储需求，支持更高吞吐量。并且，水平扩展所需的整体成本更低，仅仅需要相对较低配置的单机服务器即可，代价是增加了部署的基础设施和维护的复杂性。</p>\n<p>也就是说当你遇到如下问题时，可以使用分片集群解决：</p>\n<ul>\n<li>存储容量受单机限制，即磁盘资源遭遇瓶颈。</li>\n<li>读写能力受单机限制，可能是 CPU、内存或者网卡等资源遭遇瓶颈，导致读写能力无法扩展。</li>\n</ul>\n<h4>什么是分片键？</h4>\n<p><strong>分片键（Shard Key）</strong> 是数据分区的前提， 从而实现数据分发到不同服务器上，减轻服务器的负担。也就是说，分片键决定了集合内的文档如何在集群的多个分片间的分布状况。</p>\n<p>分片键就是文档里面的一个字段，但是这个字段不是普通的字段，有一定的要求：</p>\n<ul>\n<li>它必须在所有文档中都出现。</li>\n<li>它必须是集合的一个索引，可以是单索引或复合索引的前缀索引，不能是多索引、文本索引或地理空间位置索引。</li>\n<li>MongoDB 4.2 之前的版本，文档的分片键字段值不可变。MongoDB 4.2 版本开始，除非分片键字段是不可变的 <code>_id</code> 字段，否则您可以更新文档的分片键值。MongoDB 5.0 版本开始，实现了实时重新分片（live resharding），可以实现分片键的完全重新选择。</li>\n<li>它的大小不能超过 512 字节。</li>\n</ul>\n<h4>如何选择分片键？</h4>\n<p>选择合适的片键对 sharding 效率影响很大，主要基于如下四个因素（摘自<a href=\"https://cloud.tencent.com/document/product/240/44611\" target=\"_blank\" rel=\"noopener noreferrer\">分片集群使用注意事项 - - 腾讯云文档</a>）：</p>\n<ul>\n<li><strong>取值基数</strong> 取值基数建议尽可能大，如果用小基数的片键，因为备选值有限，那么块的总数量就有限，随着数据增多，块的大小会越来越大，导致水平扩展时移动块会非常困难。 例如：选择年龄做一个基数，范围最多只有 100 个，随着数据量增多，同一个值分布过多时，导致 chunck 的增长超出 chuncksize 的范围，引起 jumbo chunk，从而无法迁移，导致数据分布不均匀，性能瓶颈。</li>\n<li><strong>取值分布</strong> 取值分布建议尽量均匀，分布不均匀的片键会造成某些块的数据量非常大，同样有上面数据分布不均匀，性能瓶颈的问题。</li>\n<li><strong>查询带分片</strong> 查询时建议带上分片，使用分片键进行条件查询时，mongos 可以直接定位到具体分片，否则 mongos 需要将查询分发到所有分片，再等待响应返回。</li>\n<li><strong>避免单调递增或递减</strong> 单调递增的 sharding key，数据文件挪动小，但写入会集中，导致最后一篇的数据量持续增大，不断发生迁移，递减同理。</li>\n</ul>\n<p>综上，在选择片键时要考虑以上 4 个条件，尽可能满足更多的条件，才能降低 MoveChunks 对性能的影响，从而获得最优的性能体验。</p>\n<h4>分片策略有哪些？</h4>\n<p>MongoDB 支持两种分片算法来满足不同的查询需求（摘自<a href=\"https://help.aliyun.com/document_detail/64561.html?spm=a2c4g.11186623.0.0.3121565eQhUGGB#h2--shard-key-3\" target=\"_blank\" rel=\"noopener noreferrer\">MongoDB 分片集群介绍 - 阿里云文档</a>）：</p>\n<p><strong>1、基于范围的分片</strong>：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/mongodb/example-of-scope-based-sharding.png\" alt></p>\n<p>MongoDB 按照分片键（Shard Key）的值的范围将数据拆分为不同的块（Chunk），每个块包含了一段范围内的数据。当分片键的基数大、频率低且值非单调变更时，范围分片更高效。</p>\n<ul>\n<li>优点：Mongos 可以快速定位请求需要的数据，并将请求转发到相应的 Shard 节点中。</li>\n<li>缺点：可能导致数据在 Shard 节点上分布不均衡，容易造成读写热点，且不具备写分散性。</li>\n<li>适用场景：分片键的值不是单调递增或单调递减、分片键的值基数大且重复的频率低、需要范围查询等业务场景。</li>\n</ul>\n<p><strong>2、基于 Hash 值的分片</strong></p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/mongodb/example-of-hash-based-sharding.png\" alt></p>\n<p>MongoDB 计算单个字段的哈希值作为索引值，并以哈希值的范围将数据拆分为不同的块（Chunk）。</p>\n<ul>\n<li>优点：可以将数据更加均衡地分布在各 Shard 节点中，具备写分散性。</li>\n<li>缺点：不适合进行范围查询，进行范围查询时，需要将读请求分发到所有的 Shard 节点。</li>\n<li>适用场景：分片键的值存在单调递增或递减、片键的值基数大且重复的频率低、需要写入的数据随机分发、数据读取随机性较大等业务场景。</li>\n</ul>\n<p>除了上述两种分片策略，您还可以配置 <strong>复合片键</strong> ，例如由一个低基数的键和一个单调递增的键组成。</p>\n<h4>分片数据如何存储？</h4>\n<p><strong>Chunk（块）</strong> 是 MongoDB 分片集群的一个核心概念，其本质上就是由一组 Document 组成的逻辑数据单元。每个 Chunk 包含一定范围片键的数据，互不相交且并集为全部数据，即离散数学中<strong>划分</strong>的概念。</p>\n<p>分片集群不会记录每条数据在哪个分片上，而是记录 Chunk 在哪个分片上一级这个 Chunk 包含哪些数据。</p>\n<p>默认情况下，一个 Chunk 的最大值默认为 64MB（可调整，取值范围为 1~1024 MB。如无特殊需求，建议保持默认值），进行数据插入、更新、删除时，如果此时 Mongos 感知到了目标 Chunk 的大小或者其中的数据量超过上限，则会触发 <strong>Chunk 分裂</strong>。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/mongodb/chunk-splitting-shard-a.png\" alt=\"Chunk 分裂\"></p>\n<p>数据的增长会让 Chunk 分裂得越来越多。这个时候，各个分片上的 Chunk 数量可能会不平衡。Mongos 中的 <strong>均衡器(Balancer)</strong> 组件就会执行自动平衡，尝试使各个 Shard 上 Chunk 的数量保持均衡，这个过程就是 <strong>再平衡（Rebalance）</strong>。默认情况下，数据库和集合的 Rebalance 是开启的。</p>\n<p>如下图所示，随着数据插入，导致 Chunk 分裂，让 AB 两个分片有 3 个 Chunk，C 分片只有一个，这个时候就会把 B 分配的迁移一个到 C 分片实现集群数据均衡。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/database/mongodb/mongo-reblance-three-shards.png\" alt=\"Chunk 迁移\"></p>\n<blockquote>\n<p>Balancer 是 MongoDB 的一个运行在 Config Server 的 Primary 节点上(自 MongoDB 3.4 版本起)的后台进程，它监控每个分片上 Chunk 数量，并在某个分片上 Chunk 数量达到阈值进行迁移。</p>\n</blockquote>\n<p>Chunk 只会分裂，不会合并，即使 chunkSize 的值变大。</p>\n<p>Rebalance 操作是比较耗费系统资源的，我们可以通过在业务低峰期执行、预分片或者设置 Rebalance 时间窗等方式来减少其对 MongoDB 正常使用所带来的影响。</p>\n<h4>Chunk 迁移原理是什么？</h4>\n<p>关于 Chunk 迁移原理的详细介绍，推荐阅读 MongoDB 中文社区的<a href=\"https://mongoing.com/archives/77479\" target=\"_blank\" rel=\"noopener noreferrer\">一文读懂 MongoDB chunk 迁移</a>这篇文章。</p>\n<h2>学习资料推荐</h2>\n<ul>\n<li><a href=\"https://docs.mongoing.com/\" target=\"_blank\" rel=\"noopener noreferrer\">MongoDB 中文手册|官方文档中文版</a>（推荐）：基于 4.2 版本，不断与官方最新版保持同步。</li>\n<li><a href=\"https://mongoing.com/archives/docs/mongodb%e5%88%9d%e5%ad%a6%e8%80%85%e6%95%99%e7%a8%8b/mongodb%e5%a6%82%e4%bd%95%e5%88%9b%e5%bb%ba%e6%95%b0%e6%8d%ae%e5%ba%93%e5%92%8c%e9%9b%86%e5%90%88\" target=\"_blank\" rel=\"noopener noreferrer\">MongoDB 初学者教程——7 天学习 MongoDB</a>：快速入门。</li>\n<li><a href=\"https://www.cnblogs.com/dxflqm/p/16643981.html\" target=\"_blank\" rel=\"noopener noreferrer\">SpringBoot 整合 MongoDB 实战 - 2022</a>：很不错的一篇 MongoDB 入门文章，主要围绕 MongoDB 的 Java 客户端使用进行基本的增删改查操作介绍。</li>\n</ul>\n<h2>参考</h2>\n<ul>\n<li>MongoDB 官方文档（主要参考资料，以官方文档为准）：<a href=\"https://www.mongodb.com/docs/manual/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.mongodb.com/docs/manual/</a></li>\n<li>《MongoDB 权威指南》</li>\n<li>Indexes - MongoDB 官方文档：<a href=\"https://www.mongodb.com/docs/manual/indexes/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.mongodb.com/docs/manual/indexes/</a></li>\n<li>MongoDB - 索引知识 - 程序员翔仔 - 2022：<a href=\"https://fatedeity.cn/posts/database/mongodb-index-knowledge.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://fatedeity.cn/posts/database/mongodb-index-knowledge.html</a></li>\n<li>MongoDB - 索引: <a href=\"https://www.cnblogs.com/Neeo/articles/14325130.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.cnblogs.com/Neeo/articles/14325130.html</a></li>\n<li>Sharding - MongoDB 官方文档：<a href=\"https://www.mongodb.com/docs/manual/sharding/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.mongodb.com/docs/manual/sharding/</a></li>\n<li>MongoDB 分片集群介绍 - 阿里云文档：<a href=\"https://help.aliyun.com/document_detail/64561.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://help.aliyun.com/document_detail/64561.html</a></li>\n<li>分片集群使用注意事项 - - 腾讯云文档：<a href=\"https://cloud.tencent.com/document/product/240/44611\" target=\"_blank\" rel=\"noopener noreferrer\">https://cloud.tencent.com/document/product/240/44611</a></li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/database/mongodb/mongodb-composite-index.png",
      "date_published": "2023-01-12T09:46:41.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "数据库"
      ]
    },
    {
      "title": "软件工程简明教程",
      "url": "https://javaguide.cn/system-design/basis/software-engineering.html",
      "id": "https://javaguide.cn/system-design/basis/software-engineering.html",
      "summary": "软件工程基础知识详解，涵盖软件危机、软件开发过程模型、瀑布模型、敏捷开发等软件工程核心概念。",
      "content_html": "<p>大部分软件开发从业者，都会忽略软件开发中的一些最基础、最底层的一些概念。但是，这些软件开发的概念对于软件开发来说非常重要，就像是软件开发的基石一样。这也是我写这篇文章的原因。</p>\n<h2>何为软件工程？</h2>\n<p>1968 年 NATO（北大西洋公约组织）提出了<strong>软件危机</strong>（<strong>Software crisis</strong>）一词。同年，为了解决软件危机问题，“<strong>软件工程</strong>”的概念诞生了。一门叫做软件工程的学科也就应运而生。</p>\n<p>随着时间的推移，软件工程这门学科也经历了一轮又一轮的完善，其中的一些核心内容比如软件开发模型越来越丰富实用！</p>\n<p><strong>什么是软件危机呢？</strong></p>\n<p>简单来说，软件危机描述了当时软件开发的一个痛点：我们很难高效地开发出质量高的软件。</p>\n<p>Dijkstra（Dijkstra 算法的作者） 在 1972 年图灵奖获奖感言中也提到过软件危机，他是这样说的：“导致软件危机的主要原因是机器变得功能强大了几个数量级！坦率地说：只要没有机器，编程就完全没有问题。当我们有一些弱小的计算机时，编程成为一个温和的问题，而现在我们有了庞大的计算机，编程也同样成为一个巨大的问题”。</p>\n<p><strong>说了这么多，到底什么是软件工程呢？</strong></p>\n<p>工程是为了解决实际的问题将理论应用于实践。软件工程指的就是将工程思想应用于软件开发。</p>\n<p>上面是我对软件工程的定义，我们再来看看比较权威的定义。IEEE 软件工程汇刊给出的定义是这样的：　(1)将系统化的、规范的、可量化的方法应用到软件的开发、运行及维护中，即将工程化方法应用于软件。　(2)在(1)中所述方法的研究。</p>\n<p>总之，软件工程的终极目标就是：<strong>在更少资源消耗的情况下，创造出更好、更容易维护的软件。</strong></p>\n<h2>软件开发过程</h2>\n<p><a href=\"https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E8%BF%87%E7%A8%8B\" target=\"_blank\" rel=\"noopener noreferrer\">维基百科是这样定义软件开发过程</a>的：</p>\n<blockquote>\n<p>软件开发过程（英语：software development process），或软件过程（英语：software process），是软件开发的开发生命周期（software development life cycle），其各个阶段实现了软件的需求定义与分析、设计、实现、测试、交付和维护。软件过程是在开发与构建系统时应遵循的步骤，是软件开发的路线图。</p>\n</blockquote>\n<ul>\n<li>需求分析：分析用户的需求，建立逻辑模型。</li>\n<li>软件设计：根据需求分析的结果对软件架构进行设计。</li>\n<li>编码：编写程序运行的源代码。</li>\n<li>测试 : 确定测试用例，编写测试报告。</li>\n<li>交付：将做好的软件交付给客户。</li>\n<li>维护：对软件进行维护比如解决 bug，完善功能。</li>\n</ul>\n<p>软件开发过程只是比较笼统的层面上，定义了一个软件开发可能涉及到的一些流程。</p>\n<p>软件开发模型更具体地定义了软件开发过程，对开发过程提供了强有力的理论支持。</p>\n<h2>软件开发模型</h2>\n<p>软件开发模型有很多种，比如瀑布模型（Waterfall Model）、快速原型模型（Rapid Prototype Model）、V 模型（V-model）、W 模型（W-model）、敏捷开发模型。其中最具有代表性的还是 <strong>瀑布模型</strong> 和 <strong>敏捷开发</strong> 。</p>\n<p><strong>瀑布模型</strong> 定义了一套完整的软件开发周期，完整地展示了一个软件的生命周期。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/schedule-task/up-264f2750a3d30366e36c375ec3a30ec2775.png\" alt></p>\n<p><strong>敏捷开发模型</strong> 是目前使用的最多的一种软件开发模型。<a href=\"https://wiki.mbalib.com/wiki/%E6%95%8F%E6%8D%B7%E5%BC%80%E5%8F%91\" target=\"_blank\" rel=\"noopener noreferrer\">MBA 智库百科对敏捷开发的描述</a>是这样的:</p>\n<blockquote>\n<p><strong>敏捷开发</strong> 是一种以人为核心、迭代、循序渐进的开发方法。在敏捷开发中，软件项目的构建被切分成多个子项目，各个子项目的成果都经过测试，具备集成和可运行的特征。换言之，就是把一个大项目分为多个相互联系，但也可独立运行的小项目，并分别完成，在此过程中软件一直处于可使用状态。</p>\n</blockquote>\n<p>像现在比较常见的一些概念比如 <strong>持续集成</strong>、<strong>重构</strong>、<strong>小版本发布</strong>、<strong>低文档</strong>、<strong>站会</strong>、<strong>结对编程</strong>、<strong>测试驱动开发</strong> 都是敏捷开发的核心。</p>\n<h2>软件开发的基本策略</h2>\n<h3>软件复用</h3>\n<p>我们在构建一个新的软件的时候，不需要从零开始，通过复用已有的一些轮子（框架、第三方库等）、设计模式、设计原则等等现成的物料，我们可以更快地构建出一个满足要求的软件。</p>\n<p>像我们平时接触的开源项目就是最好的例子。我想，如果不是开源，我们构建出一个满足要求的软件，耗费的精力和时间要比现在多的多！</p>\n<h3>分而治之</h3>\n<p>构建软件的过程中，我们会遇到很多问题。我们可以将一些比较复杂的问题拆解为一些小问题，然后，一一攻克。</p>\n<p>我结合现在比较火的软件设计方法—领域驱动设计（Domain Driven Design，简称 DDD）来说说。</p>\n<p>在领域驱动设计中，很重要的一个概念就是<strong>领域（Domain）</strong>，它就是我们要解决的问题。在领域驱动设计中，我们要做的就是把比较大的领域（问题）拆解为若干的小领域（子域）。</p>\n<p>除此之外，分而治之也是一个比较常用的算法思想，对应的就是分治算法。如果你想了解分治算法的话，推荐你看一下北大的<a href=\"https://www.coursera.org/learn/algorithms\" target=\"_blank\" rel=\"noopener noreferrer\">《算法设计与分析 Design and Analysis of Algorithms》</a>。</p>\n<h3>逐步演进</h3>\n<p>软件开发是一个逐步演进的过程，我们需要不断进行迭代式增量开发，最终交付符合客户价值的产品。</p>\n<p>这里补充一个在软件开发领域，非常重要的概念：<strong>MVP（Minimum Viable Product，最小可行产品</strong>）。</p>\n<p>这个最小可行产品，可以理解为刚好能够满足客户需求的产品。下面这张图片把这个思想展示的非常精髓。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/system-design/schedule-task/up-a99961ff7725106c0592abca845d555568a.png\" alt></p>\n<p>利用最小可行产品，我们可以也可以提早进行市场分析，这对于我们在探索产品不确定性的道路上非常有帮助。可以非常有效地指导我们下一步该往哪里走。</p>\n<h3>优化折中</h3>\n<p>软件开发是一个不断优化改进的过程。任何软件都有很多可以优化的点，不可能完美。我们需要不断改进和提升软件的质量。</p>\n<p>但是，也不要陷入这个怪圈。要学会折中，在有限的投入内，以最有效的方式提高现有软件的质量。</p>\n<h2>参考</h2>\n<ul>\n<li>软件工程的基本概念-清华大学软件学院 刘强：<a href=\"https://www.xuetangx.com/course/THU08091000367\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.xuetangx.com/course/THU08091000367</a></li>\n<li>软件开发过程-维基百科：<a href=\"https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E8%BF%87%E7%A8%8B\" target=\"_blank\" rel=\"noopener noreferrer\">https://zh.wikipedia.org/wiki/软件开发过程</a></li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/system-design/schedule-task/up-264f2750a3d30366e36c375ec3a30ec2775.png",
      "date_published": "2022-12-30T05:45:11.000Z",
      "date_modified": "2026-01-16T13:41:03.000Z",
      "authors": [],
      "tags": [
        "系统设计"
      ]
    },
    {
      "title": "Maven核心概念总结",
      "url": "https://javaguide.cn/tools/maven/maven-core-concepts.html",
      "id": "https://javaguide.cn/tools/maven/maven-core-concepts.html",
      "summary": "Apache Maven 的本质是一个软件项目管理和理解工具。基于项目对象模型 (Project Object Model，POM) 的概念，Maven 可以从一条中心信息管理项目的构建、报告和文档。",
      "content_html": "<blockquote>\n<p>这部分内容主要根据 Maven 官方文档整理，做了对应的删减，主要保留比较重要的部分，不涉及实战，主要是一些重要概念的介绍。</p>\n</blockquote>\n<h2>Maven 介绍</h2>\n<p><a href=\"https://github.com/apache/maven\" target=\"_blank\" rel=\"noopener noreferrer\">Maven</a> 官方文档是这样介绍的 Maven 的：</p>\n<blockquote>\n<p>Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information.</p>\n<p>Apache Maven 的本质是一个软件项目管理和理解工具。基于项目对象模型 (Project Object Model，POM) 的概念，Maven 可以从一条中心信息管理项目的构建、报告和文档。</p>\n</blockquote>\n<p><strong>什么是 POM？</strong> 每一个 Maven 工程都有一个 <code>pom.xml</code> 文件，位于根目录中，包含项目构建生命周期的详细信息。通过 <code>pom.xml</code> 文件，我们可以定义项目的坐标、项目依赖、项目信息、插件信息等等配置。</p>\n<p>对于开发者来说，Maven 的主要作用主要有 3 个：</p>\n<ol>\n<li><strong>项目构建</strong>：提供标准的、跨平台的自动化项目构建方式。</li>\n<li><strong>依赖管理</strong>：方便快捷的管理项目依赖的资源（jar 包），避免资源间的版本冲突问题。</li>\n<li><strong>统一开发结构</strong>：提供标准的、统一的项目结构。</li>\n</ol>\n<p>关于 Maven 的基本使用这里就不介绍了，建议看看官网的 5 分钟上手 Maven 的教程：<a href=\"https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html\" target=\"_blank\" rel=\"noopener noreferrer\">Maven in 5 Minutes</a> 。</p>\n<h2>Maven 坐标</h2>\n<p>项目中依赖的第三方库以及插件可统称为构件。每一个构件都可以使用 Maven 坐标唯一标识，坐标元素包括：</p>\n<ul>\n<li><strong>groupId</strong>(必须): 定义了当前 Maven 项目隶属的组织或公司。groupId 一般分为多段，通常情况下，第一段为域，第二段为公司名称。域又分为 org、com、cn 等，其中 org 为非营利组织，com 为商业组织，cn 表示中国。以 apache 开源社区的 tomcat 项目为例，这个项目的 groupId 是 org.apache，它的域是 org（因为 tomcat 是非营利项目），公司名称是 apache，artifactId 是 tomcat。</li>\n<li><strong>artifactId</strong>(必须)：定义了当前 Maven 项目的名称，项目的唯一的标识符，对应项目根目录的名称。</li>\n<li><strong>version</strong>(必须)：定义了 Maven 项目当前所处版本。</li>\n<li><strong>packaging</strong>（可选）：定义了 Maven 项目的打包方式（比如 jar，war...），默认使用 jar。</li>\n<li><strong>classifier</strong>(可选)：常用于区分从同一 POM 构建的具有不同内容的构件，可以是任意的字符串，附加在版本号之后。</li>\n</ul>\n<p>只要你提供正确的坐标，就能从 Maven 仓库中找到相应的构件供我们使用。</p>\n<p>举个例子（引入阿里巴巴开源的 EasyExcel）：</p>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>com.alibaba&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>easyexcel&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>3.1.1&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>你可以在 <a href=\"https://mvnrepository.com/\" target=\"_blank\" rel=\"noopener noreferrer\">https://mvnrepository.com/</a> 这个网站上找到几乎所有可用的构件，如果你的项目使用的是 Maven 作为构建工具，那这个网站你一定会经常接触。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/tools/maven/mvnrepository.com.png\" alt=\"Maven 仓库\"></p>\n<h2>Maven 依赖</h2>\n<p>如果使用 Maven 构建产生的构件（例如 Jar 文件）被其他的项目引用，那么该构件就是其他项目的依赖。</p>\n<h3>依赖配置</h3>\n<p><strong>配置信息示例</strong>：</p>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">project</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependencies</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">type</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>...&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">type</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>...&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">optional</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>...&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">optional</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">exclusions</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">exclusion</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>...&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>...&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">exclusion</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">exclusions</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependencies</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">project</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><strong>配置说明</strong>：</p>\n<ul>\n<li>dependencies：一个 pom.xml 文件中只能存在一个这样的标签，是用来管理依赖的总标签。</li>\n<li>dependency：包含在 dependencies 标签中，可以有多个，每一个表示项目的一个依赖。</li>\n<li>groupId,artifactId,version(必要)：依赖的基本坐标，对于任何一个依赖来说，基本坐标是最重要的，Maven 根据坐标才能找到需要的依赖。我们在上面解释过这些元素的具体意思，这里就不重复提了。</li>\n<li>type(可选)：依赖的类型，对应于项目坐标定义的 packaging。大部分情况下，该元素不必声明，其默认值是 jar。</li>\n<li>scope(可选)：依赖的范围，默认值是 compile。</li>\n<li>optional(可选)：标记依赖是否可选</li>\n<li>exclusions(可选)：用来排除传递性依赖,例如 jar 包冲突</li>\n</ul>\n<h3>依赖范围</h3>\n<p><strong>classpath</strong> 用于指定 <code>.class</code> 文件存放的位置，类加载器会从该路径中加载所需的 <code>.class</code> 文件到内存中。</p>\n<p>Maven 在编译、执行测试、实际运行有着三套不同的 classpath：</p>\n<ul>\n<li><strong>编译 classpath</strong>：编译主代码有效</li>\n<li><strong>测试 classpath</strong>：编译、运行测试代码有效</li>\n<li><strong>运行 classpath</strong>：项目运行时有效</li>\n</ul>\n<p>Maven 的依赖范围如下：</p>\n<ul>\n<li><strong>compile</strong>：编译依赖范围（默认），使用此依赖范围对于编译、测试、运行三种都有效，即在编译、测试和运行的时候都要使用该依赖 Jar 包。</li>\n<li><strong>test</strong>：测试依赖范围，从字面意思就可以知道此依赖范围只能用于测试，而在编译和运行项目时无法使用此类依赖，典型的是 JUnit，它只用于编译测试代码和运行测试代码的时候才需要。</li>\n<li><strong>provided</strong>：此依赖范围，对于编译和测试有效，而对运行时无效。比如 <code>servlet-api.jar</code> 在 Tomcat 中已经提供了，我们只需要的是编译期提供而已。</li>\n<li><strong>runtime</strong>：运行时依赖范围，对于测试和运行有效，但是在编译主代码时无效，典型的就是 JDBC 驱动实现。</li>\n<li><strong>system</strong>：系统依赖范围，使用 system 范围的依赖时必须通过 systemPath 元素显示地指定依赖文件的路径，不依赖 Maven 仓库解析，所以可能会造成建构的不可移植。</li>\n</ul>\n<h3>传递依赖性</h3>\n<h3>依赖冲突</h3>\n<p><strong>1、对于 Maven 而言，同一个 groupId 同一个 artifactId 下，只能使用一个 version。</strong></p>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>in.hocg.boot&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>mybatis-plus-spring-boot-starter&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>1.0.48&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">&#x3C;!-- 只会使用 1.0.49 这个版本的依赖 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>in.hocg.boot&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>mybatis-plus-spring-boot-starter&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>1.0.49&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>若相同类型但版本不同的依赖存在于同一个 pom 文件，只会引入后一个声明的依赖。</p>\n<p><strong>2、项目的两个依赖同时引入了某个依赖。</strong></p>\n<p>举个例子，项目存在下面这样的依赖关系：</p>\n<div class=\"language-plain line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"plain\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-plain\"><span class=\"line\"><span>依赖链路一：A -> B -> C -> X(1.0)</span></span>\n<span class=\"line\"><span>依赖链路二：A -> D -> X(2.0)</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这两条依赖路径上有两个版本的 X，为了避免依赖重复，Maven 只会选择其中的一个进行解析。</p>\n<p><strong>哪个版本的 X 会被 Maven 解析使用呢?</strong></p>\n<p>Maven 在遇到这种问题的时候，会遵循 <strong>路径最短优先</strong> 和 <strong>声明顺序优先</strong> 两大原则。解决这个问题的过程也被称为 <strong>Maven 依赖调解</strong> 。</p>\n<p><strong>路径最短优先</strong></p>\n<div class=\"language-plain line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"plain\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-plain\"><span class=\"line\"><span>依赖链路一：A -> B -> C -> X(1.0) // dist = 3</span></span>\n<span class=\"line\"><span>依赖链路二：A -> D -> X(2.0) // dist = 2</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>依赖链路二的路径最短，因此，X(2.0)会被解析使用。</p>\n<p>不过，你也可以发现。路径最短优先原则并不是通用的，像下面这种路径长度相等的情况就不能单单通过其解决了：</p>\n<div class=\"language-plain line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"plain\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-plain\"><span class=\"line\"><span>依赖链路一：A -> B -> X(1.0) // dist = 2</span></span>\n<span class=\"line\"><span>依赖链路二：A -> D -> X(2.0) // dist = 2</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>因此，Maven 又定义了声明顺序优先原则。</p>\n<p>依赖调解第一原则不能解决所有问题，比如这样的依赖关系：A-&gt;B-&gt;Y(1.0)、A-&gt; C-&gt;Y(2.0)，Y(1.0)和 Y(2.0)的依赖路径长度是一样的，都为 2。Maven 定义了依赖调解的第二原则：</p>\n<p><strong>声明顺序优先</strong></p>\n<p>在依赖路径长度相等的前提下，在 <code>pom.xml</code> 中依赖声明的顺序决定了谁会被解析使用，顺序最前的那个依赖优胜。该例中，如果 B 的依赖声明在 D 之前，那么 X (1.0)就会被解析使用。</p>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">&#x3C;!-- A pom.xml --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependencies</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    dependency B</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    ...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    dependency D</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependencies</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>排除依赖</h3>\n<p>单纯依赖 Maven 来进行依赖调解，在很多情况下是不适用的，需要我们手动排除依赖。</p>\n<p>举个例子，当前项目存在下面这样的依赖关系：</p>\n<div class=\"language-plain line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"plain\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-plain\"><span class=\"line\"><span>依赖链路一：A -> B -> C -> X(1.5) // dist = 3</span></span>\n<span class=\"line\"><span>依赖链路二：A -> D -> X(1.0) // dist = 2</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>根据路径最短优先原则，X(1.0) 会被解析使用，也就是说实际用的是 1.0 版本的 X。</p>\n<p>但是！！！这会一些问题：如果 C 依赖用到了 1.5 版本的 X 中才有的一个类，运行项目就会报<code>NoClassDefFoundError</code>错误。如果 C 依赖用到了 1.5 版本的 X 中才有的一个方法，运行项目就会报<code>NoSuchMethodError</code>错误。</p>\n<p>现在知道为什么你的 Maven 项目总是会报<code>NoClassDefFoundError</code>和<code>NoSuchMethodError</code>错误了吧？</p>\n<p><strong>如何解决呢？</strong> 我们可以通过<code>exclusion</code>标签手动将 X(1.0) 给排除。</p>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    ......</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">exclusions</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">exclusion</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>x&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>org.apache.x&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">exclusion</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">exclusions</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">dependency</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>一般我们在解决依赖冲突的时候，都会优先保留版本较高的。这是因为大部分 jar 在升级的时候都会做到向下兼容。</p>\n<p>如果高版本修改了低版本的一些类或者方法的话，这个时候就不能直接保留高版本了，而是应该考虑优化上层依赖，比如升级上层依赖的版本。</p>\n<p>还是上面的例子：</p>\n<div class=\"language-plain line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"plain\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-plain\"><span class=\"line\"><span>依赖链路一：A -> B -> C -> X(1.5) // dist = 3</span></span>\n<span class=\"line\"><span>依赖链路二：A -> D -> X(1.0) // dist = 2</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>我们保留了 1.5 版本的 X，但是这个版本的 X 删除了 1.0 版本中的某些类。这个时候，我们可以考虑升级 D 的版本到一个 X 兼容的版本。</p>\n<h2>Maven 仓库</h2>\n<p>在 Maven 世界中，任何一个依赖、插件或者项目构建的输出，都可以称为 <strong>构件</strong> 。</p>\n<p>坐标和依赖是构件在 Maven 世界中的逻辑表示方式，构件的物理表示方式是文件，Maven 通过仓库来统一管理这些文件。 任何一个构件都有一组坐标唯一标识。有了仓库之后，无需手动引入构件，我们直接给定构件的坐标即可在 Maven 仓库中找到该构件。</p>\n<p>Maven 仓库分为：</p>\n<ul>\n<li><strong>本地仓库</strong>：运行 Maven 的计算机上的一个目录，它缓存远程下载的构件并包含尚未发布的临时构件。<code>settings.xml</code> 文件中可以看到 Maven 的本地仓库路径配置，默认本地仓库路径是在 <code>${user.home}/.m2/repository</code>。</li>\n<li><strong>远程仓库</strong>：官方或者其他组织维护的 Maven 仓库。</li>\n</ul>\n<p>Maven 远程仓库可以分为：</p>\n<ul>\n<li><strong>中央仓库</strong>：这个仓库是由 Maven 社区来维护的，里面存放了绝大多数开源软件的包，并且是作为 Maven 的默认配置，不需要开发者额外配置。另外为了方便查询，还提供了一个<a href=\"https://search.maven.org/\" target=\"_blank\" rel=\"noopener noreferrer\">查询地址</a>，开发者可以通过这个地址更快的搜索需要构件的坐标。</li>\n<li><strong>私服</strong>：私服是一种特殊的远程 Maven 仓库，它是架设在局域网内的仓库服务，私服一般被配置为互联网远程仓库的镜像，供局域网内的 Maven 用户使用。</li>\n<li><strong>其他的公共仓库</strong>：有一些公共仓库是为了加速访问（比如阿里云 Maven 镜像仓库）或者部分构件不存在于中央仓库中。</li>\n</ul>\n<p>Maven 依赖包寻找顺序：</p>\n<ol>\n<li>先去本地仓库找寻，有的话，直接使用。</li>\n<li>本地仓库没有找到的话，会去远程仓库找寻，下载包到本地仓库。</li>\n<li>远程仓库没有找到的话，会报错。</li>\n</ol>\n<h2>Maven 生命周期</h2>\n<p>Maven 的生命周期就是为了对所有的构建过程进行抽象和统一，包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建步骤。</p>\n<p>Maven 定义了 3 个生命周期<code>META-INF/plexus/components.xml</code>：</p>\n<ul>\n<li><code>default</code> 生命周期</li>\n<li><code>clean</code>生命周期</li>\n<li><code>site</code>生命周期</li>\n</ul>\n<p>这些生命周期是相互独立的，每个生命周期包含多个阶段(phase)。并且，这些阶段是有序的，也就是说，后面的阶段依赖于前面的阶段。当执行某个阶段的时候，会先执行它前面的阶段。</p>\n<p>执行 Maven 生命周期的命令格式如下：</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">mvn</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> 阶段</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> [阶段2] ...[阶段n]</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div></div></div><h3>default 生命周期</h3>\n<p><code>default</code>生命周期是在没有任何关联插件的情况下定义的，是 Maven 的主要生命周期，用于构建应用程序，共包含 23 个阶段。</p>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phases</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 验证项目是否正确，并且所有必要的信息可用于完成构建过程 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>validate&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 建立初始化状态，例如设置属性 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>initialize&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 生成要包含在编译阶段的源代码 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>generate-sources&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 处理源代码 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>process-sources&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 生成要包含在包中的资源 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>generate-resources&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 将资源复制并处理到目标目录中，为打包阶段做好准备。 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>process-resources&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 编译项目的源代码  --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>compile&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 对编译生成的文件进行后处理，例如对 Java 类进行字节码增强/优化 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>process-classes&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 生成要包含在编译阶段的任何测试源代码 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>generate-test-sources&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 处理测试源代码 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>process-test-sources&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 生成要包含在编译阶段的测试源代码 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>generate-test-resources&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 处理从测试代码文件编译生成的文件 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>process-test-resources&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 编译测试源代码 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>test-compile&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 处理从测试代码文件编译生成的文件 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>process-test-classes&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 使用合适的单元测试框架（Junit 就是其中之一）运行测试 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>test&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 在实际打包之前，执行任何的必要的操作为打包做准备 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>prepare-package&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 获取已编译的代码并将其打包成可分发的格式，例如 JAR、WAR 或 EAR 文件 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>package&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 在执行集成测试之前执行所需的操作。 例如，设置所需的环境 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>pre-integration-test&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 处理并在必要时部署软件包到集成测试可以运行的环境 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>integration-test&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 执行集成测试后执行所需的操作。 例如，清理环境  --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>post-integration-test&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 运行任何检查以验证打的包是否有效并符合质量标准。 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>verify&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- \t将包安装到本地仓库中，可以作为本地其他项目的依赖 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>install&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!-- 将最终的项目包复制到远程仓库中与其他开发者和项目共享 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>deploy&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phases</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>根据前面提到的阶段间依赖关系理论，当我们执行 <code>mvn test</code>命令的时候，会执行从 validate 到 test 的所有阶段，这也就解释了为什么执行测试的时候，项目的代码能够自动编译。</p>\n<h3>clean 生命周期</h3>\n<p>clean 生命周期的目的是清理项目，共包含 3 个阶段：</p>\n<ol>\n<li>pre-clean</li>\n<li>clean</li>\n<li>post-clean</li>\n</ol>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phases</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!--  执行一些需要在clean之前完成的工作 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>pre-clean&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!--  移除所有上一次构建生成的文件 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>clean&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!--  执行一些需要在clean之后立刻完成的工作 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>post-clean&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phases</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">default-phases</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">clean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    org.apache.maven.plugins:maven-clean-plugin:2.5:clean</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">clean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">default-phases</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>根据前面提到的阶段间依赖关系理论，当我们执行 <code>mvn clean</code> 的时候，会执行 clean 生命周期中的 pre-clean 和 clean 阶段。</p>\n<h3>site 生命周期</h3>\n<p>site 生命周期的目的是建立和发布项目站点，共包含 4 个阶段：</p>\n<ol>\n<li>pre-site</li>\n<li>site</li>\n<li>post-site</li>\n<li>site-deploy</li>\n</ol>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phases</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!--  执行一些需要在生成站点文档之前完成的工作 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>pre-site&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!--  生成项目的站点文档作 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>site&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!--  执行一些需要在生成站点文档之后完成的工作，并且为部署做准备 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>post-site&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  &#x3C;!--  将生成的站点文档部署到特定的服务器上 --></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>site-deploy&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phases</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">default-phases</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">site</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    org.apache.maven.plugins:maven-site-plugin:3.3:site</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">site</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">site-deploy</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    org.apache.maven.plugins:maven-site-plugin:3.3:deploy</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">site-deploy</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">default-phases</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>Maven 能够基于 <code>pom.xml</code> 所包含的信息，自动生成一个友好的站点，方便团队交流和发布项目信息。</p>\n<h2>Maven 插件</h2>\n<p>Maven 本质上是一个插件执行框架，所有的执行过程，都是由一个一个插件独立完成的。像咱们日常使用到的 install、clean、deploy 等命令，其实底层都是一个一个的 Maven 插件。关于 Maven 的核心插件可以参考官方的这篇文档：<a href=\"https://maven.apache.org/plugins/index.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://maven.apache.org/plugins/index.html</a> 。</p>\n<p>本地默认插件路径: <code>${user.home}/.m2/repository/org/apache/maven/plugins</code></p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/tools/maven/maven-plugins.png\" alt></p>\n<p>除了 Maven 自带的插件之外，还有一些三方提供的插件比如单测覆盖率插件 jacoco-maven-plugin、帮助开发检测代码中不合规范的地方的插件 maven-checkstyle-plugin、分析代码质量的 sonar-maven-plugin。并且，我们还可以自定义插件来满足自己的需求。</p>\n<p>jacoco-maven-plugin 使用示例：</p>\n<div class=\"language-xml line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"xml\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-xml\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">build</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">plugins</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">plugin</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>org.jacoco&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">groupId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>jacoco-maven-plugin&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">artifactId</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>0.8.8&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">version</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">executions</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">execution</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>prepare-agent&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">execution</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">execution</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">id</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>generate-code-coverage-report&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">id</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>test&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">phase</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            &#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">>report&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">          &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">goals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">execution</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">      &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">executions</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">plugin</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">  &#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">plugins</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;/</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">build</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>你可以将 Maven 插件理解为一组任务的集合，用户可以通过命令行直接运行指定插件的任务，也可以将插件任务挂载到构建生命周期，随着生命周期运行。</p>\n<p>Maven 插件被分为下面两种类型：</p>\n<ul>\n<li><strong>Build plugins</strong>：在构建时执行。</li>\n<li><strong>Reporting plugins</strong>：在网站生成过程中执行。</li>\n</ul>\n<h2>Maven 多模块管理</h2>\n<p>多模块管理简单地来说就是将一个项目分为多个模块，每个模块只负责单一的功能实现。直观的表现就是一个 Maven 项目中不止有一个 <code>pom.xml</code> 文件，会在不同的目录中有多个 <code>pom.xml</code> 文件，进而实现多模块管理。</p>\n<p>多模块管理除了可以更加便于项目开发和管理，还有如下好处：</p>\n<ol>\n<li>降低代码之间的耦合性（从类级别的耦合提升到 jar 包级别的耦合）；</li>\n<li>减少重复，提升复用性；</li>\n<li>每个模块都可以是自解释的（通过模块名或者模块文档）；</li>\n<li>模块还规范了代码边界的划分，开发者很容易通过模块确定自己所负责的内容。</li>\n</ol>\n<p>多模块管理下，会有一个父模块，其他的都是子模块。父模块通常只有一个 <code>pom.xml</code>，没有其他内容。父模块的 <code>pom.xml</code> 一般只定义了各个依赖的版本号、包含哪些子模块以及插件有哪些。不过，要注意的是，如果依赖只在某个子项目中使用，则可以在子项目的 pom.xml 中直接引入，防止父 pom 的过于臃肿。</p>\n<p>如下图所示，Dubbo 项目就被分成了多个子模块比如 dubbo-common（公共逻辑模块）、dubbo-remoting（远程通讯模块）、dubbo-rpc（远程调用模块）。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/tools/maven/dubbo-maven-multi-module.png\" alt></p>\n<h2>文章推荐</h2>\n<ul>\n<li><a href=\"https://mp.weixin.qq.com/s/flniMiP-eu3JSBnswfd_Ew\" target=\"_blank\" rel=\"noopener noreferrer\">安全同学讲 Maven 间接依赖场景的仲裁机制 - 阿里开发者 - 2022</a></li>\n<li><a href=\"https://mp.weixin.qq.com/s/Wvq7t2FC58jaCh4UFJ6GGQ\" target=\"_blank\" rel=\"noopener noreferrer\">高效使用 Java 构建工具｜ Maven 篇 - 阿里开发者 - 2022</a></li>\n<li><a href=\"https://mp.weixin.qq.com/s/xsJkB0onUkakrVH0wejcIg\" target=\"_blank\" rel=\"noopener noreferrer\">安全同学讲 Maven 重打包的故事 - 阿里开发者 - 2022</a></li>\n</ul>\n<h2>参考</h2>\n<ul>\n<li>《Maven 实战》</li>\n<li>Introduction to Repositories - Maven 官方文档：<a href=\"https://maven.apache.org/guides/introduction/introduction-to-repositories.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://maven.apache.org/guides/introduction/introduction-to-repositories.html</a></li>\n<li>Introduction to the Build Lifecycle - Maven 官方文档：<a href=\"https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference\" target=\"_blank\" rel=\"noopener noreferrer\">https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference</a></li>\n<li>Maven 依赖范围：<a href=\"http://www.mvnbook.com/maven-dependency.html\" target=\"_blank\" rel=\"noopener noreferrer\">http://www.mvnbook.com/maven-dependency.html</a></li>\n<li>解决 maven 依赖冲突，这篇就够了！：<a href=\"https://www.cnblogs.com/qdhxhz/p/16363532.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.cnblogs.com/qdhxhz/p/16363532.html</a></li>\n<li>Multi-Module Project with Maven：<a href=\"https://www.baeldung.com/maven-multi-module\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.baeldung.com/maven-multi-module</a></li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/tools/maven/mvnrepository.com.png",
      "date_published": "2022-12-16T14:32:28.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "开发工具"
      ]
    },
    {
      "title": "项目经验指南",
      "url": "https://javaguide.cn/interview-preparation/project-experience-guide.html",
      "id": "https://javaguide.cn/interview-preparation/project-experience-guide.html",
      "summary": "项目经验指南：针对没有项目/项目平淡的求职者，给出获取实战项目经验的方法与选择建议，并讲清如何做出项目亮点、如何复盘与表达，提升简历与面试竞争力。",
      "content_html": "<div class=\"hint-container tip\">\n<p class=\"hint-container-title\">友情提示</p>\n<p>本文节选自 <strong><a href=\"/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\">《Java 面试指北》</a></strong>。这是一份教你如何更高效地准备面试的专栏，内容和 JavaGuide 互补，涵盖常见八股文（系统设计、常见框架、分布式、高并发 ……）、优质面经等内容。</p>\n</div>\n<h2>没有项目经验怎么办?</h2>\n<p>没有项目经验是大部分应届生会碰到的一个问题。甚至说，有很多有工作经验的程序员，对自己在公司做的项目不满意，也想找一个比较有技术含量的项目来做。</p>\n<p>说几种我觉得比较靠谱的获取项目经验的方式，希望能够对你有启发。</p>\n<h3>实战项目视频/专栏</h3>\n<p>在网上找一个符合自己能力与找工作需求的实战项目视频或者专栏，跟着老师一起做。</p>\n<p>你可以通过慕课网、哔哩哔哩、拉勾、极客时间、培训机构（比如黑马、尚硅谷）等渠道获取到适合自己的实战项目视频/专栏。</p>\n<p><img src=\"https://oss.javaguide.cn/javamianshizhibei/mukewangzhiazhanke.png\" alt=\"慕课网实战课\"></p>\n<p>尽量选择一个适合自己的项目，没必要必须做分布式/微服务项目，对于绝大部分同学来说，能把一个单机项目做好就已经很不错了。</p>\n<p>我面试过很多求职者，简历上看着有微服务的项目经验，结果随便问两个问题就知道根本不是自己做的或者说做的时候压根没认真思考。这种情况会给我留下非常不好的印象。</p>\n<p>我在 <strong><a href=\"/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\">《Java 面试指北》</a></strong> 的「面试准备篇」中也说过：</p>\n<blockquote>\n<p>个人认为也没必要非要去做微服务或者分布式项目，不一定对你面试有利。微服务或者分布式项目涉及的知识点太多，一般人很难吃透。并且，这类项目其实对于校招生来说稍微有一点超标了。即使你做出来，很多面试官也会认为不是你独立完成的。</p>\n<p>其实，你能把一个单体项目做到极致也很好，对于个人能力提升不比做微服务或者分布式项目差。如何做到极致？代码质量这里就不提了，更重要的是你要尽量让自己的项目有一些亮点（比如你是如何提升项目性能的、如何解决项目中存在的一个痛点的），项目经历取得的成果尽量要量化一下比如我使用 xxx 技术解决了 xxx 问题，系统 qps 从 xxx 提高到了 xxx。</p>\n</blockquote>\n<p>跟着老师做的过程中，你一定要有自己的思考，不要浅尝辄止。对于很多知识点，别人的讲解可能只是满足项目就够了，你自己想多点知识的话，对于重要的知识点就要自己学会去深入学习。</p>\n<h3>实战类开源项目</h3>\n<p>GitHub 或者码云上面有很多实战类别项目，你可以选择一个来研究，为了让自己对这个项目更加理解，在理解原有代码的基础上，你可以对原有项目进行改进或者增加功能。</p>\n<p>你可以参考 <a href=\"https://javaguide.cn/open-source-project/practical-project.html\" title=\"Java 优质开源实战项目\" target=\"_blank\" rel=\"noopener noreferrer\">Java 优质开源实战项目</a> 上面推荐的实战类开源项目，质量都很高，项目类型也比较全面，涵盖博客/论坛系统、考试/刷题系统、商城系统、权限管理系统、快速开发脚手架以及各种轮子。</p>\n<p><img src=\"https://oss.javaguide.cn/javamianshizhibei/javaguide-practical-project.png\" alt=\"Java 优质开源实战项目\"></p>\n<p>一定要记住：<strong>不光要做，还要改进，改善。不论是实战项目视频或者专栏还是实战类开源项目，都一定会有很多可以完善改进的地方。</strong></p>\n<h3>从头开始做</h3>\n<p>自己动手去做一个自己想完成的东西，遇到不会的东西就临时去学，现学现卖。</p>\n<p>这个要求比较高，我建议你已经有了一个项目经验之后，再采用这个方法。如果你没有做过项目的话，还是老老实实采用上面两个方法比较好。</p>\n<h3>参加各种大公司组织的各种大赛</h3>\n<p>如果参加这种赛事能获奖的话，项目含金量非常高。即使没获奖也没啥，也可以写简历上。</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/up-673f598477242691900a1e72c5d8b26df2c.png\" alt=\"阿里云天池大赛\"></p>\n<h3>参与实际项目</h3>\n<p>通常情况下，你有如下途径接触到企业实际项目的开发：</p>\n<ol>\n<li>老师接的项目；</li>\n<li>自己接的私活；</li>\n<li>实习/工作接触到的项目；</li>\n</ol>\n<p>老师接的项目和自己接的私活通常都是一些偏业务的项目，很少会涉及到性能优化。这种情况下，你可以考虑对项目进行改进，别怕花时间，某个时间用心做好一件事情就好比如你对项目的数据模型进行改进、引入缓存提高访问速度等等。</p>\n<p>实习/工作接触到的项目类似，如果遇到一些偏业务的项目，也是要自己私下对项目进行改进优化。</p>\n<p>尽量是真的对项目进行了优化，这本身也是对个人能力的提升。如果你实在是没时间去实践的话，也没关系，吃透这个项目优化手段就好，把一些面试可能会遇到的问题提前准备一下。</p>\n<h2>有没有还不错的项目推荐？</h2>\n<p><strong><a href=\"/zhuanlan/java-mian-shi-zhi-bei.html\" target=\"_blank\">《Java 面试指北》</a></strong> 的「面试准备篇」中有一篇文章专门整理了一些比较高质量的实战项目，包含业务项目、轮子项目、国外公开课 Lab 和视频类实战项目教程推荐，非常适合用来学习或者作为项目经验。</p>\n<p><img src=\"https://oss.javaguide.cn/javamianshizhibei/project-experience-guide.png\" alt=\"优质 Java 实战项目推荐\"></p>\n<p>这篇文章一共推荐了 15+ 个实战项目，有业务类的，也有轮子类的，有开源项目、也有视频教程。对于参加校招的小伙伴，我更建议做一个业务类项目加上一个轮子类的项目。</p>\n<h2>我跟着视频做的项目会被面试官嫌弃不？</h2>\n<p>很多应届生都是跟着视频做的项目，这个大部分面试官都心知肚明。</p>\n<p>不排除确实有些面试官不吃这一套，这个也看人。不过我相信大多数面试官都是能理解的，毕竟你在学校的时候实际上是没有什么获得实际项目经验的途径的。</p>\n<p>大部分应届生的项目经验都是自己在网上找的或者像你一样买的付费课程跟着做的，极少部分是比较真实的项目。 从你能想着做一个实战项目来说，我觉得初衷是好的，确实也能真正学到东西。 但是，究竟有多少是自己掌握了很重要。看视频最忌讳的是被动接受，自己多改进一下，多思考一下！就算是你跟着视频做的项目，也是可以优化的！</p>\n<p><strong>如果你想真正学到东西的话，建议不光要把项目单纯完成跑起来，还要去自己尝试着优化！</strong></p>\n<p>简单说几个比较容易的优化点：</p>\n<ol>\n<li><strong>全局异常处理</strong>：很多项目这方面都做的不是很好，可以参考我的这篇文章：<a href=\"https://mp.weixin.qq.com/s/Y4Q4yWRqKG_lw0GLUsY2qw\" target=\"_blank\" rel=\"noopener noreferrer\">《使用枚举简单封装一个优雅的 Spring Boot 全局异常处理！》</a> 来做优化。</li>\n<li><strong>项目的技术选型优化</strong>：比如使用 Guava 做本地缓存的地方可以换成 <strong>Caffeine</strong> 。Caffeine 的各方面的表现要更加好！再比如 Controller 层是否放了太多的业务逻辑。</li>\n<li><strong>数据库方面</strong>：数据库设计可否优化？索引是否使用使用正确？SQL 语句是否可以优化？是否需要进行读写分离？</li>\n<li><strong>缓存</strong>：项目有没有哪些数据是经常被访问的？是否引入缓存来提高响应速度？</li>\n<li><strong>安全</strong>：项目是否存在安全问题？</li>\n<li>……</li>\n</ol>\n<p>另外，我在星球分享过常见的性能优化方向实践案例，涉及到多线程、异步、索引、缓存等方向，强烈推荐你看看：<a href=\"https://t.zsxq.com/06EqfeMZZ\" target=\"_blank\" rel=\"noopener noreferrer\">https://t.zsxq.com/06EqfeMZZ</a> 。</p>\n<p>最后，<strong>再给大家推荐一个 IDEA 优化代码的小技巧，超级实用！</strong></p>\n<p>分析你的代码：右键项目-&gt; Analyze-&gt;Inspect Code</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/up-651672bce128025a135c1536cd5dc00532e.png\" alt></p>\n<p>扫描完成之后，IDEA 会给出一些可能存在的代码坏味道比如命名问题。</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/up-05c83b319941995b07c8020fddc57f26037.png\" alt></p>\n<p>并且，你还可以自定义检查规则。</p>\n<p><img src=\"https://oss.javaguide.cn/xingqiu/up-6b618ad3bad0bc3f76e6066d90c8cd2f255.png\" alt></p>\n",
      "image": "https://oss.javaguide.cn/javamianshizhibei/mukewangzhiazhanke.png",
      "date_published": "2022-11-03T15:33:32.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "面试准备"
      ]
    },
    {
      "title": "Java 17 新特性概览（重要）",
      "url": "https://javaguide.cn/java/new-features/java17.html",
      "id": "https://javaguide.cn/java/new-features/java17.html",
      "summary": "总结 JDK 17 的重要更新与 JEP，涵盖密封类、记录类与模式匹配等特性。",
      "content_html": "<p>Java 17 在 2021 年 9 月 14 日正式发布，是一个长期支持（LTS）版本。</p>\n<p>下面这张图是 Oracle 官方给出的 Oracle JDK 支持的时间线。可以看得到，Java 17 最多可以支持到 2029 年 9 月份。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/4c1611fad59449edbbd6e233690e9fa7.png\" alt></p>\n<p>Java 17 将是继 Java 8 以来最重要的长期支持（LTS）版本，是 Java 社区八年努力的成果。Spring 6.x 和 Spring Boot 3.x 最低支持的就是 Java 17。</p>\n<p>JDK 17 共有 14 个新特性，这篇文章会挑选其中较为重要的一些新特性进行详细介绍：</p>\n<ul>\n<li><a href=\"https://openjdk.java.net/jeps/356\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 356: Enhanced Pseudo-Random Number Generators（增强的伪随机数生成器）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/398\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 398: Deprecate the Applet API for Removal（标记弃用 Applet API 以便移除）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/406\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 406: Pattern Matching for switch (Preview)（switch 模式匹配，预览）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/407\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 407: Remove RMI Activation（移除 RMI 激活机制）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/409\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 409: Sealed Classes（密封类，转正）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/410\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 410: Remove the Experimental AOT and JIT Compiler（移除实验性的 AOT 和 JIT 编译器）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/411\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 411: Deprecate the Security Manager for Removal（标记弃用安全管理器以便移除）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/412\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 412: Foreign Function &amp; Memory API (Incubator)（外部函数和内存 API，第一次孵化）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/414\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 414: Vector API (Second Incubator)（向量 API，第二次孵化）</a></li>\n</ul>\n<p>下图是从 JDK 8 到 JDK 16 每个版本的更新带来的新特性数量和更新时间：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png\" alt></p>\n<p>相关阅读：<a href=\"https://openjdk.java.net/projects/jdk/17/\" target=\"_blank\" rel=\"noopener noreferrer\">OpenJDK Java 17 文档</a> 。</p>\n<h2>JEP 356: Enhanced Pseudo-Random Number Generators（增强的伪随机数生成器）</h2>\n<p>JDK 17 之前，我们可以借助 <code>Random</code>、<code>ThreadLocalRandom</code>和<code>SplittableRandom</code>来生成随机数。不过，这 3 个类都各有缺陷，且缺少常见的伪随机算法支持。</p>\n<p>Java 17 为伪随机数生成器 （pseudorandom number generator，PRNG，又称为确定性随机位生成器）增加了新的接口类型和实现，使得开发者更容易在应用程序中互换使用各种 PRNG 算法。</p>\n<blockquote>\n<p><a href=\"https://ctf-wiki.org/crypto/streamcipher/prng/intro/\" target=\"_blank\" rel=\"noopener noreferrer\">PRNG</a> 用来生成接近于绝对随机数序列的数字序列。一般来说，PRNG 会依赖于一个初始值，也称为种子，来生成对应的伪随机数序列。只要种子确定了，PRNG 所生成的随机数就是完全确定的，因此其生成的随机数序列并不是真正随机的。</p>\n</blockquote>\n<p>使用示例：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">RandomGeneratorFactory</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">RandomGenerator</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> l128X256MixRandom </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> RandomGeneratorFactory</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">of</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"L128X256MixRandom\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 使用时间戳作为随机数种子</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">RandomGenerator</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> randomGenerator </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> l128X256MixRandom</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">create</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentTimeMillis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 生成随机数</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">randomGenerator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">nextInt</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JEP 398: Deprecate the Applet API for Removal（标记弃用 Applet API 以便移除）</h2>\n<p>Applet API 用于编写在 Web 浏览器端运行的 Java 小程序，很多年前就已经被淘汰了，已经没有理由使用了。</p>\n<p>Applet API 在 Java 9 时被标记弃用（<a href=\"https://openjdk.java.net/jeps/289\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 289</a>），但不是为了删除。</p>\n<h2>JEP 406: Pattern Matching for switch（switch 模式匹配，预览）</h2>\n<p>正如 <code>instanceof</code> 一样， <code>switch</code> 也紧跟着增加了类型匹配自动转换功能。</p>\n<p><code>instanceof</code> 代码示例：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// Old code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> String) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> s </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (String)o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    ...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> use</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> s </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// New code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> s) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    ...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> use</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> s </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>switch</code> 代码示例：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// Old code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> formatter</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> o) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> formatted </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"unknown\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        formatted </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"int %d\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> l) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        formatted </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"long %d\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, l);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Double</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> d) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        formatted </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"double %f\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, d);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">instanceof</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> s) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        formatted </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"String %s\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, s);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> formatted</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// New code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> formatterPatternSwitch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> o) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> switch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (o) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"int %d\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> l    </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"long %d\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, l);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Double</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> d  </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"double %f\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, d);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> s  </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">format</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"String %s\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, s);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        default</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        -></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> o</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">toString</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>对于 <code>null</code> 值的判断也进行了优化。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// Old code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> testFooBar</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> s) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (s </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">==</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"oops!\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    switch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (s) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Foo\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Bar\"</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> -></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Great\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        default</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">           -></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Ok\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// New code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> testFooBar</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> s) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    switch</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (s) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">         -></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Oops\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        case</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Foo\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"Bar\"</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> -></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Great\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">        default</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">           -></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Ok\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JEP 407: Remove RMI Activation（移除 RMI 激活机制）</h2>\n<p>删除远程方法调用 (RMI) 激活机制，同时保留 RMI 的其余部分。RMI 激活机制已过时且不再使用。</p>\n<h2>JEP 409: Sealed Classes（密封类）</h2>\n<p>密封类由 <a href=\"https://openjdk.java.net/jeps/360\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 360</a> 提出预览，集成到了 Java 15 中。在 JDK 16 中， 密封类得到了改进（更加严格的引用检查和密封类的继承关系），由 <a href=\"https://openjdk.java.net/jeps/397\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 397</a> 提出了再次预览。</p>\n<p>在 <a href=\"/java/new-features/java14-15.html\" target=\"_blank\">Java 14 &amp; 15 新特性概览</a> 中，我有详细介绍到密封类，这里就不再做额外的介绍了。</p>\n<h2>JEP 410: Remove the Experimental AOT and JIT Compiler（移除实验性的 AOT 和 JIT 编译器）</h2>\n<p>在 Java 9 的 <a href=\"https://openjdk.java.net/jeps/295\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 295</a> ,引入了实验性的提前 (AOT) 编译器，在启动虚拟机之前将 Java 类编译为本机代码。</p>\n<p>Java 17，删除实验性的提前 (AOT) 和即时 (JIT) 编译器，因为该编译器自推出以来很少使用，维护它所需的工作量很大。保留实验性的 Java 级 JVM 编译器接口 (JVMCI)，以便开发人员可以继续使用外部构建的编译器版本进行 JIT 编译。</p>\n<h2>JEP 411: Deprecate the Security Manager for Removal（标记弃用安全管理器以便移除）</h2>\n<p>弃用安全管理器以便在将来的版本中删除。</p>\n<p>安全管理器可追溯到 Java 1.0，多年来，它一直不是保护客户端 Java 代码的主要方法，也很少用于保护服务器端代码。为了推动 Java 向前发展，Java 17 弃用安全管理器，以便与旧版 Applet API ( <a href=\"https://openjdk.java.net/jeps/398\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 398</a> ) 一起移除。</p>\n<h2>JEP 412: Foreign Function &amp; Memory API（外部函数和内存 API，孵化）</h2>\n<p>Java 程序可以通过该 API 与 Java 运行时之外的代码和数据进行互操作。通过高效地调用外部函数（即 JVM 之外的代码）和安全地访问外部内存（即不受 JVM 管理的内存），该 API 使 Java 程序能够调用本机库并处理本机数据，而不会像 JNI 那样危险和脆弱。</p>\n<p>外部函数和内存 API 在 Java 17 中进行了第一轮孵化，由 <a href=\"https://openjdk.java.net/jeps/412\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 412</a> 提出。第二轮孵化由<a href=\"https://openjdk.org/jeps/419\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 419</a> 提出并集成到了 Java 18 中，预览由 <a href=\"https://openjdk.org/jeps/424\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 424</a> 提出并集成到了 Java 19 中。</p>\n<p>在 <a href=\"/java/new-features/java19.html\" target=\"_blank\">Java 19 新特性概览</a> 中，我有详细介绍到外部函数和内存 API，这里就不再做额外的介绍了。</p>\n<h2>JEP 414: Vector API（向量 API，第二次孵化）</h2>\n<p>向量（Vector） API 最初由 <a href=\"https://openjdk.java.net/jeps/338\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 338</a> 提出，并作为<a href=\"http://openjdk.java.net/jeps/11\" target=\"_blank\" rel=\"noopener noreferrer\">孵化 API</a>集成到 Java 16 中。第二轮孵化由 <a href=\"https://openjdk.java.net/jeps/414\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 414</a> 提出并集成到 Java 17 中，第三轮孵化由 <a href=\"https://openjdk.java.net/jeps/417\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 417</a> 提出并集成到 Java 18 中，第四轮由 <a href=\"https://openjdk.java.net/jeps/426\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 426</a> 提出并集成到了 Java 19 中。</p>\n<p>该孵化器 API 提供了一个 API 的初始迭代以表达一些向量计算，这些计算在运行时可靠地编译为支持的 CPU 架构上的最佳向量硬件指令，从而获得优于同等标量计算的性能，充分利用单指令多数据（SIMD）技术（大多数现代 CPU 上都可以使用的一种指令）。尽管 HotSpot 支持自动向量化，但是可转换的标量操作集有限且易受代码更改的影响。该 API 将使开发人员能够轻松地用 Java 编写可移植的高性能向量算法。</p>\n<p>在 <a href=\"/java/new-features/java18.html\" target=\"_blank\">Java 18 新特性概览</a> 中，我有详细介绍到向量 API，这里就不再做额外的介绍了。</p>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/new-features/4c1611fad59449edbbd6e233690e9fa7.png",
      "date_published": "2022-09-28T12:35:46.000Z",
      "date_modified": "2026-01-26T14:09:42.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "Java 18 新特性概览",
      "url": "https://javaguide.cn/java/new-features/java18.html",
      "id": "https://javaguide.cn/java/new-features/java18.html",
      "summary": "概览 JDK 18 的更新与预览特性，理解新 API 带来的改进。",
      "content_html": "<p>Java 18 在 2022 年 3 月 22 日正式发布，非长期支持版本。</p>\n<p>JDK 18 共有 8 个新特性，这篇文章会挑选其中较为重要的一些新特性进行详细介绍：</p>\n<ul>\n<li><a href=\"https://openjdk.java.net/jeps/400\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 400: UTF-8 by Default（UTF-8 作为默认字符集）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/408\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 408: Simple Web Server（简单 Web 服务器）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/413\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 413: Code Snippets in Java API Documentation（API 文档代码片段）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/416\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 416: Reimplement Core Reflection with Method Handles（方法句柄重构核心反射）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/417\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 417: Vector API (Third Incubator)（向量 API，第三次孵化）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/418\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 418: Internet-Address Resolution SPI（互联网地址解析 SPI）</a></li>\n<li><a href=\"https://openjdk.java.net/jeps/419\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 419: Foreign Function &amp; Memory API (Second Incubator)（外部函数和内存 API，第二次孵化）</a></li>\n</ul>\n<p>下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png\" alt=\" JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间\"></p>\n<p>相关阅读：</p>\n<ul>\n<li><a href=\"https://openjdk.java.net/projects/jdk/18/\" target=\"_blank\" rel=\"noopener noreferrer\">OpenJDK Java 18 文档</a></li>\n<li><a href=\"https://mp.weixin.qq.com/s/PocFKR9z9u7-YCZHsrA5kQ\" target=\"_blank\" rel=\"noopener noreferrer\">IntelliJ IDEA | Java 18 功能支持</a></li>\n</ul>\n<h2>JEP 400: UTF-8 by Default（UTF-8 作为默认字符集，转正）</h2>\n<p>JDK 终于将 UTF-8 设置为默认字符集。</p>\n<p>在 Java 17 及更早版本中，默认字符集是在 Java 虚拟机运行时才确定的，取决于不同的操作系统、区域设置等因素，因此存在潜在的风险。就比如说你在 Mac 上运行正常的一段打印文字到控制台的 Java 程序到了 Windows 上就会出现乱码，如果你不手动更改字符集的话。</p>\n<h2>JEP 408: Simple Web Server（简单 Web 服务器，转正）</h2>\n<p>Java 18 之后，你可以使用 <code>jwebserver</code> 命令启动一个简易的静态 Web 服务器。</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">$</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> jwebserver</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">Binding</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> to</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> loopback</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> by</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> default.</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> For</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> all</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> interfaces</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> use</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"-b 0.0.0.0\"</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> or</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"-b ::\".</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">Serving</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> /cwd</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> and</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> subdirectories</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> on</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 127.0.0.1</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> port</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 8000</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">URL:</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> http://127.0.0.1:8000/</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这个服务器不支持 CGI 和 Servlet，只限于静态文件。</p>\n<h2>JEP 413: Code Snippets in Java API Documentation（API 文档代码片段，转正）</h2>\n<p>在 Java 18 之前，如果我们想要在 Javadoc 中引入代码片段可以使用 <code>&lt;pre&gt;{@code ...}&lt;/pre&gt;</code> 。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">pre</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">{</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    lines of source code</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;/</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">pre</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>&lt;pre&gt;{@code ...}&lt;/pre&gt;</code> 这种方式生成的效果比较一般。</p>\n<p>在 Java 18 之后，可以通过 <code>@snippet</code> 标签来做这件事情。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">/**</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * The following code shows how to use {@code Optional.isPresent}:</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * {@snippet :</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * if (v.isPresent()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> *     System.out.println(\"v: \" + v.get());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> * }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\"> */</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>@snippet</code> 这种方式生成的效果更好且使用起来更方便一些。</p>\n<h2>JEP 416: Reimplement Core Reflection with Method Handles（方法句柄重构核心反射，转正）</h2>\n<p>Java 18 改进了 <code>java.lang.reflect.Method</code>、<code>Constructor</code> 的实现逻辑，使之性能更好，速度更快。这项改动不会改动相关 API ，这意味着开发中不需要改动反射相关代码，就可以体验到性能更好的反射。</p>\n<p>OpenJDK 官方给出了新老实现的反射性能基准测试结果。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/JEP416Benchmark.png\" alt=\"新老实现的反射性能基准测试结果\"></p>\n<h2>JEP 417: Vector API（向量 API，第三次孵化）</h2>\n<p>向量（Vector） API 最初由 <a href=\"https://openjdk.java.net/jeps/338\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 338</a> 提出，并作为<a href=\"http://openjdk.java.net/jeps/11\" target=\"_blank\" rel=\"noopener noreferrer\">孵化 API</a>集成到 Java 16 中。第二轮孵化由 <a href=\"https://openjdk.java.net/jeps/414\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 414</a> 提出并集成到 Java 17 中，第三轮孵化由 <a href=\"https://openjdk.java.net/jeps/417\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 417</a> 提出并集成到 Java 18 中，第四轮由 <a href=\"https://openjdk.java.net/jeps/426\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 426</a> 提出并集成到了 Java 19 中。</p>\n<p>向量计算由对向量的一系列操作组成。向量 API 用来表达向量计算，该计算可以在运行时可靠地编译为支持的 CPU 架构上的最佳向量指令，从而实现优于等效标量计算的性能。</p>\n<p>向量 API 的目标是为用户提供简洁易用且与平台无关的表达范围广泛的向量计算。</p>\n<p>这是对数组元素的简单标量计算：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> scalarComputation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">   for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        c[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i]) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1.0f</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">   }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>这是使用 Vector API 进行的等效向量计算：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> VectorSpecies</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Float</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> SPECIES </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> FloatVector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">SPECIES_PREFERRED</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> vectorComputation</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] b</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\"> float</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">[] c) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> upperBound </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> SPECIES</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">loopBound</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> upperBound</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> SPECIES</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // FloatVector va, vb, vc;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> va </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> FloatVector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fromArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(SPECIES, a, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> vb </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> FloatVector</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fromArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(SPECIES, b, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">        var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> vc </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> va</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">mul</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(va)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                   .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">add</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">vb</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">mul</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(vb))</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                   .</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">neg</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        vc</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">intoArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(c, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> a</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">        c[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> a[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> b[i]) </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">*</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> -</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1.0f</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>在 JDK 18 中，向量 API 的性能得到了进一步的优化。</p>\n<h2>JEP 418: Internet-Address Resolution SPI（互联网地址解析 SPI，转正）</h2>\n<p>Java 18 定义了一个全新的 SPI（service-provider interface），用于主要名称和地址的解析，以便 <code>java.net.InetAddress</code> 可以使用平台之外的第三方解析器。</p>\n<h2>JEP 419: Foreign Function &amp; Memory API（外部函数和内存 API，第二次孵化）</h2>\n<p>Java 程序可以通过该 API 与 Java 运行时之外的代码和数据进行互操作。通过高效地调用外部函数（即 JVM 之外的代码）和安全地访问外部内存（即不受 JVM 管理的内存），该 API 使 Java 程序能够调用本机库并处理本机数据，而不会像 JNI 那样危险和脆弱。</p>\n<p>外部函数和内存 API 在 Java 17 中进行了第一轮孵化，由 <a href=\"https://openjdk.java.net/jeps/412\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 412</a> 提出。第二轮孵化由<a href=\"https://openjdk.org/jeps/419\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 419</a> 提出并集成到了 Java 18 中，预览由 <a href=\"https://openjdk.org/jeps/424\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 424</a> 提出并集成到了 Java 19 中。</p>\n<p>在 <a href=\"/java/new-features/java19.html\" target=\"_blank\">Java 19 新特性概览</a> 中，我有详细介绍到外部函数和内存 API，这里就不再做额外的介绍了。</p>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png",
      "date_published": "2022-09-13T12:49:20.000Z",
      "date_modified": "2026-01-27T07:16:00.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "Java 19 新特性概览",
      "url": "https://javaguide.cn/java/new-features/java19.html",
      "id": "https://javaguide.cn/java/new-features/java19.html",
      "summary": "介绍 JDK 19 的预览特性与并发相关更新，为后续虚拟线程铺垫。",
      "content_html": "<p>JDK 19 于 2022 年 9 月 20 日正式发布，非长期支持版本。</p>\n<p>JDK 19 共有 7 个新特性，这篇文章会挑选其中较为重要的一些新特性进行详细介绍：</p>\n<ul>\n<li><a href=\"https://openjdk.org/jeps/424\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 424: Foreign Function &amp; Memory API（外部函数和内存 API）</a>（预览）</li>\n<li><a href=\"https://openjdk.org/jeps/425\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 425: Virtual Threads（虚拟线程）</a>（预览）</li>\n<li><a href=\"https://openjdk.java.net/jeps/426\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 426: Vector API（向量 API）</a>（第四次孵化）</li>\n<li><a href=\"https://openjdk.org/jeps/428\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 428: Structured Concurrency（结构化并发）</a>（孵化）</li>\n</ul>\n<p>下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间：</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png\" alt=\" JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间\"></p>\n<h2>JEP 424: 外部函数和内存 API（预览）</h2>\n<p>Java 程序可以通过该 API 与 Java 运行时之外的代码和数据进行互操作。通过高效地调用外部函数（即 JVM 之外的代码）和安全地访问外部内存（即不受 JVM 管理的内存），该 API 使 Java 程序能够调用本机库并处理本机数据，而不会像 JNI 那样危险和脆弱。</p>\n<p>外部函数和内存 API 在 Java 17 中进行了第一轮孵化，由 <a href=\"https://openjdk.java.net/jeps/412\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 412</a> 提出。第二轮孵化由<a href=\"https://openjdk.org/jeps/419\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 419</a> 提出并集成到了 Java 18 中，预览由 <a href=\"https://openjdk.org/jeps/424\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 424</a> 提出并集成到了 Java 19 中。</p>\n<p>在没有外部函数和内存 API 之前：</p>\n<ul>\n<li>Java 通过 <a href=\"https://hg.openjdk.java.net/jdk/jdk/file/tip/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java\" target=\"_blank\" rel=\"noopener noreferrer\"><code>sun.misc.Unsafe</code></a> 提供一些执行低级别、不安全操作的方法（如直接访问系统内存资源、自主管理内存资源等），<code>Unsafe</code> 类让 Java 语言拥有了类似 C 语言指针一样操作内存空间的能力的同时，也增加了 Java 语言的不安全性，不正确使用 <code>Unsafe</code> 类会使得程序出错的概率变大。</li>\n<li>Java 1.1 就已通过 Java 原生接口（JNI）支持了原生方法调用，但并不好用。JNI 实现起来过于复杂，步骤繁琐（具体的步骤可以参考这篇文章：<a href=\"https://www.baeldung.com/jni\" target=\"_blank\" rel=\"noopener noreferrer\">Guide to JNI (Java Native Interface)</a>），不受 JVM 的语言安全机制控制，影响 Java 语言的跨平台特性。并且，JNI 的性能也不行，因为 JNI 方法调用不能从许多常见的 JIT 优化（如内联）中受益。虽然 <a href=\"https://github.com/java-native-access/jna\" target=\"_blank\" rel=\"noopener noreferrer\">JNA</a>、<a href=\"https://github.com/jnr/jnr-ffi\" target=\"_blank\" rel=\"noopener noreferrer\">JNR</a> 和 <a href=\"https://github.com/bytedeco/javacpp\" target=\"_blank\" rel=\"noopener noreferrer\">JavaCPP</a> 等框架对 JNI 进行了改进，但效果还是不太理想。</li>\n</ul>\n<p>引入外部函数和内存 API 就是为了解决 Java 访问外部函数和外部内存存在的一些痛点。</p>\n<p>Foreign Function &amp; Memory API (FFM API) 定义了类和接口：</p>\n<ul>\n<li>分配外部内存：<code>MemorySegment</code>、<code>MemoryAddress</code> 和 <code>SegmentAllocator</code></li>\n<li>操作和访问结构化的外部内存：<code>MemoryLayout</code>、<code>VarHandle</code></li>\n<li>控制外部内存的分配和释放：<code>MemorySession</code></li>\n<li>调用外部函数：<code>Linker</code>、<code>FunctionDescriptor</code> 和 <code>SymbolLookup</code></li>\n</ul>\n<p>下面是 FFM API 使用示例，这段代码获取了 C 库函数的 <code>radixsort</code> 方法句柄，然后使用它对 Java 数组中的四个字符串进行排序。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 1. 在 C 库路径上查找外部函数</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Linker</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> linker </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Linker</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">nativeLinker</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">SymbolLookup</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> stdlib </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> linker</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">defaultLookup</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">MethodHandle</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> radixSort </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> linker</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">downcallHandle</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                             stdlib</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lookup</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"radixsort\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">), ...);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 2. 分配堆上内存以存储四个字符串</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">[] javaStrings   </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> { </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"mouse\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"cat\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"dog\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \"car\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> }</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 3. 分配堆外内存以存储四个指针</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">SegmentAllocator</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> allocator </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> implicitAllocator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">MemorySegment</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> offHeap  </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> allocator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">allocateArray</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ADDRESS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">javaStrings</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 4. 将字符串从堆上复制到堆外</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> javaStrings</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 在堆外分配一个字符串，然后存储指向它的指针</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    MemorySegment</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> cString </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> allocator</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">allocateUtf8String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(javaStrings[i]);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    offHeap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setAtIndex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ADDRESS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, i, cString);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 5. 通过调用外部函数对堆外数据进行排序</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">radixSort</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">invoke</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(offHeap, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">javaStrings</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">MemoryAddress</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">NULL</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'</span><span style=\"--shiki-light:#0184BC;--shiki-dark:#56B6C2\">\\0</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">'</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 6. 将（重新排序的）字符串从堆外复制到堆上</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">for</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">int</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> javaStrings</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">length</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> i</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">++</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    MemoryAddress</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> cStringPtr </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> offHeap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getAtIndex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ValueLayout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">ADDRESS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, i);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    javaStrings[i] </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> cStringPtr</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getUtf8String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">0</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">assert</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Arrays</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">equals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(javaStrings, </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[] {</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"car\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"cat\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"dog\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"mouse\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">});</span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // true</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h2>JEP 425: 虚拟线程（预览）</h2>\n<p>虚拟线程（Virtual Thread）是 JDK 而不是 OS 实现的轻量级线程（Lightweight Process，LWP），许多虚拟线程共享同一个操作系统线程，虚拟线程的数量可以远大于操作系统线程的数量。</p>\n<p>虚拟线程在其他多线程语言中已经被证实是十分有用的，比如 Go 中的 Goroutine、Erlang 中的进程。</p>\n<p>虚拟线程避免了上下文切换的额外耗费，兼顾了多线程的优点，简化了高并发程序的复杂，可以有效减少编写、维护和观察高吞吐量并发应用程序的工作量。</p>\n<p>知乎有一个关于 Java 19 虚拟线程的讨论，感兴趣的可以去看看：<a href=\"https://www.zhihu.com/question/536743167\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.zhihu.com/question/536743167</a> 。</p>\n<p>Java 虚拟线程的详细解读和原理可以看下面这两篇文章：</p>\n<ul>\n<li><a href=\"https://mp.weixin.qq.com/s/vdLXhZdWyxc6K-D3Aj03LA\" target=\"_blank\" rel=\"noopener noreferrer\">虚拟线程原理及性能分析｜得物技术</a></li>\n<li><a href=\"https://mp.weixin.qq.com/s/yyApBXxpXxVwttr01Hld6Q\" target=\"_blank\" rel=\"noopener noreferrer\">Java19 正式 GA！看虚拟线程如何大幅提高系统吞吐量</a></li>\n<li><a href=\"https://www.cnblogs.com/throwable/p/16758997.html\" target=\"_blank\" rel=\"noopener noreferrer\">虚拟线程 - VirtualThread 源码透视</a></li>\n</ul>\n<h2>JEP 426: 向量 API（第四次孵化）</h2>\n<p>向量（Vector） API 最初由 <a href=\"https://openjdk.java.net/jeps/338\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 338</a> 提出，并作为<a href=\"http://openjdk.java.net/jeps/11\" target=\"_blank\" rel=\"noopener noreferrer\">孵化 API</a>集成到 Java 16 中。第二轮孵化由 <a href=\"https://openjdk.java.net/jeps/414\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 414</a> 提出并集成到 Java 17 中，第三轮孵化由 <a href=\"https://openjdk.java.net/jeps/417\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 417</a> 提出并集成到 Java 18 中，第四轮由 <a href=\"https://openjdk.java.net/jeps/426\" target=\"_blank\" rel=\"noopener noreferrer\">JEP 426</a> 提出并集成到了 Java 19 中。</p>\n<p>在 <a href=\"/java/new-features/java18.html\" target=\"_blank\">Java 18 新特性概览</a> 中，我有详细介绍到向量 API，这里就不再做额外的介绍了。</p>\n<h2>JEP 428: 结构化并发(孵化)</h2>\n<p>JDK 19 引入了结构化并发，一种多线程编程方法，目的是为了通过结构化并发 API 来简化多线程编程，并不是为了取代<code>java.util.concurrent</code>，目前处于孵化器阶段。</p>\n<p>结构化并发将不同线程中运行的多个任务视为单个工作单元，从而简化错误处理、提高可靠性并增强可观察性。也就是说，结构化并发保留了单线程代码的可读性、可维护性和可观察性。</p>\n<p>结构化并发的基本 API 是<a href=\"https://download.java.net/java/early_access/loom/docs/api/jdk.incubator.concurrent/jdk/incubator/concurrent/StructuredTaskScope.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code>StructuredTaskScope</code></a>。<code>StructuredTaskScope</code> 支持将任务拆分为多个并发子任务，在它们自己的线程中执行，并且子任务必须在主任务继续之前完成。</p>\n<p><code>StructuredTaskScope</code> 的基本用法如下：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">var</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> scope </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> StructuredTaskScope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Object</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 使用fork方法派生线程来执行子任务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Future</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Integer</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> future1 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fork</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task1);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Future</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> future2 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">fork</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task2);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 等待线程完成</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        scope</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">join</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">        // 结果的处理可能包括处理或重新抛出异常</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        ...</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> process</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> results</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">exceptions </span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    } </span><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// close</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>结构化并发非常适合虚拟线程，虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程，从而允许非常多的虚拟线程。</p>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png",
      "date_published": "2022-09-13T01:11:51.000Z",
      "date_modified": "2026-01-27T07:16:00.000Z",
      "authors": [],
      "tags": [
        "Java"
      ]
    },
    {
      "title": "ARP 协议详解(网络层)",
      "url": "https://javaguide.cn/cs-basics/network/arp.html",
      "id": "https://javaguide.cn/cs-basics/network/arp.html",
      "summary": "讲解 ARP 的地址解析机制与报文流程，结合 ARP 表与广播/单播详解常见攻击与防御策略。",
      "content_html": "<p>每当我们学习一个新的网络协议的时候，都要把他结合到 OSI 七层模型中，或者是 TCP/IP 协议栈中来学习，一是要学习该协议在整个网络协议栈中的位置，二是要学习该协议解决了什么问题，地位如何？三是要学习该协议的工作原理，以及一些更深入的细节。</p>\n<p><strong>ARP 协议</strong>，可以说是在协议栈中属于一个<strong>偏底层的、非常重要的、又非常简单的</strong>通信协议。</p>\n<p>开始阅读这篇文章之前，你可以先看看下面几个问题：</p>\n<ol>\n<li><strong>ARP 协议在协议栈中的位置？</strong> ARP 协议在协议栈中的位置非常重要，在理解了它的工作原理之后，也很难说它到底是网络层协议，还是链路层协议，因为它恰恰串联起了网络层和链路层。国外的大部分教程通常将 ARP 协议放在网络层。</li>\n<li><strong>ARP 协议解决了什么问题，地位如何？</strong> ARP 协议，全称 <strong>地址解析协议（Address Resolution Protocol）</strong>，它解决的是网络层地址和链路层地址之间的转换问题。因为一个 IP 数据报在物理上传输的过程中，总是需要知道下一跳（物理上的下一个目的地）该去往何处，但 IP 地址属于逻辑地址，而 MAC 地址才是物理地址，ARP 协议解决了 IP 地址转 MAC 地址的一些问题。</li>\n<li><strong>ARP 工作原理？</strong> 只希望大家记住几个关键词：<strong>ARP 表、广播问询、单播响应</strong>。</li>\n</ol>\n<h2>MAC 地址</h2>\n<p>在介绍 ARP 协议之前，有必要介绍一下 MAC 地址。</p>\n<p>MAC 地址的全称是 <strong>媒体访问控制地址（Media Access Control Address）</strong>。如果说，互联网中每一个资源都由 IP 地址唯一标识（IP 协议内容），那么一切网络设备都由 MAC 地址唯一标识。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/cs-basics/network/router-back-will-indicate-mac-address.png\" alt=\"路由器的背面就会注明 MAC 位址\"></p>\n<p>可以理解为，MAC 地址是一个网络设备真正的身份证号，IP 地址只是一种不重复的定位方式（比如说住在某省某市某街道的张三，这种逻辑定位是 IP 地址，他的身份证号才是他的 MAC 地址），也可以理解为 MAC 地址是身份证号，IP 地址是邮政地址。MAC 地址也有一些别称，如 LAN 地址、物理地址、以太网地址等。</p>\n<blockquote>\n<p>还有一点要知道的是，不仅仅是网络资源才有 IP 地址，网络设备也有 IP 地址，比如路由器。但从结构上说，路由器等网络设备的作用是组成一个网络，而且通常是内网，所以它们使用的 IP 地址通常是内网 IP，内网的设备在与内网以外的设备进行通信时，需要用到 NAT 协议。</p>\n</blockquote>\n<p>MAC 地址的长度为 6 字节（48 比特），地址空间大小有 280 万亿之多（$2^{48}$），MAC 地址由 IEEE 统一管理与分配，理论上，一个网络设备中的网卡上的 MAC 地址是永久的。不同的网卡生产商从 IEEE 那里购买自己的 MAC 地址空间（MAC 的前 24 比特），也就是前 24 比特由 IEEE 统一管理，保证不会重复。而后 24 比特，由各家生产商自己管理，同样保证生产的两块网卡的 MAC 地址不会重复。</p>\n<p>MAC 地址具有可携带性、永久性，身份证号永久地标识一个人的身份，不论他到哪里都不会改变。而 IP 地址不具有这些性质，当一台设备更换了网络，它的 IP 地址也就可能发生改变，也就是它在互联网中的定位发生了变化。</p>\n<p>最后，记住，MAC 地址有一个特殊地址：FF-FF-FF-FF-FF-FF（全 1 地址），该地址表示广播地址。</p>\n<h2>ARP 协议工作原理</h2>\n<p>ARP 协议工作时有一个大前提，那就是 <strong>ARP 表</strong>。</p>\n<p>在一个局域网内，每个网络设备都自己维护了一个 ARP 表，ARP 表记录了某些其他网络设备的 IP 地址-MAC 地址映射关系，该映射关系以 <code>&lt;IP, MAC, TTL&gt;</code> 三元组的形式存储。其中，TTL 为该映射关系的生存周期，典型值为 20 分钟，超过该时间，该条目将被丢弃。</p>\n<p>ARP 的工作原理将分两种场景讨论：</p>\n<ol>\n<li><strong>同一局域网内的 MAC 寻址</strong>；</li>\n<li><strong>从一个局域网到另一个局域网中的网络设备的寻址</strong>。</li>\n</ol>\n<h3>同一局域网内的 MAC 寻址</h3>\n<p>假设当前有如下场景：IP 地址为<code>137.196.7.23</code>的主机 A，想要给同一局域网内的 IP 地址为<code>137.196.7.14</code>主机 B，发送 IP 数据报文。</p>\n<blockquote>\n<p>再次强调，当主机发送 IP 数据报文时（网络层），仅知道目的地的 IP 地址，并不清楚目的地的 MAC 地址，而 ARP 协议就是解决这一问题的。</p>\n</blockquote>\n<p>为了达成这一目标，主机 A 将不得不通过 ARP 协议来获取主机 B 的 MAC 地址，并将 IP 报文封装成链路层帧，发送到下一跳上。在该局域网内，关于此将按照时间顺序，依次发生如下事件：</p>\n<ol>\n<li>\n<p>主机 A 检索自己的 ARP 表，发现 ARP 表中并无主机 B 的 IP 地址对应的映射条目，也就无从知道主机 B 的 MAC 地址。</p>\n</li>\n<li>\n<p>主机 A 将构造一个 ARP 查询分组，并将其广播到所在的局域网中。</p>\n<p>ARP 分组是一种特殊报文，ARP 分组有两类，一种是查询分组，另一种是响应分组，它们具有相同的格式，均包含了发送和接收的 IP 地址、发送和接收的 MAC 地址。当然了，查询分组中，发送的 IP 地址，即为主机 A 的 IP 地址，接收的 IP 地址即为主机 B 的 IP 地址，发送的 MAC 地址也是主机 A 的 MAC 地址，但接收的 MAC 地址绝不会是主机 B 的 MAC 地址（因为这正是我们要问询的！），而是一个特殊值——<code>FF-FF-FF-FF-FF-FF</code>，之前说过，该 MAC 地址是广播地址，也就是说，查询分组将广播给该局域网内的所有设备。</p>\n</li>\n<li>\n<p>主机 A 构造的查询分组将在该局域网内广播，理论上，每一个设备都会收到该分组，并检查查询分组的接收 IP 地址是否为自己的 IP 地址，如果是，说明查询分组已经到达了主机 B，否则，该查询分组对当前设备无效，丢弃之。</p>\n</li>\n<li>\n<p>主机 B 收到了查询分组之后，验证是对自己的问询，接着构造一个 ARP 响应分组，该分组的目的地只有一个——主机 A，发送给主机 A。同时，主机 B 提取查询分组中的 IP 地址和 MAC 地址信息，在自己的 ARP 表中构造一条主机 A 的 IP-MAC 映射记录。</p>\n<p>ARP 响应分组具有和 ARP 查询分组相同的构造，不同的是，发送和接受的 IP 地址恰恰相反，发送的 MAC 地址为发送者本身，目标 MAC 地址为查询分组的发送者，也就是说，ARP 响应分组只有一个目的地，而非广播。</p>\n</li>\n<li>\n<p>主机 A 终将收到主机 B 的响应分组，提取出该分组中的 IP 地址和 MAC 地址后，构造映射信息，加入到自己的 ARP 表中。</p>\n</li>\n</ol>\n<p></p>\n<p>在整个过程中，有几点需要补充说明的是：</p>\n<ol>\n<li>主机 A 想要给主机 B 发送 IP 数据报，如果主机 B 的 IP-MAC 映射信息已经存在于主机 A 的 ARP 表中，那么主机 A 无需广播，只需提取 MAC 地址并构造链路层帧发送即可。</li>\n<li>ARP 表中的映射信息是有生存周期的，典型值为 20 分钟。</li>\n<li>目标主机接收到了问询主机构造的问询报文后，将先把问询主机的 IP-MAC 映射存进自己的 ARP 表中，这样才能获取到响应的目标 MAC 地址，顺利的发送响应分组。</li>\n</ol>\n<p>总结来说，ARP 协议是一个<strong>广播问询，单播响应</strong>协议。</p>\n<h3>不同局域网内的 MAC 寻址</h3>\n<p>更复杂的情况是，发送主机 A 和接收主机 B 不在同一个子网中，假设一个一般场景，两台主机所在的子网由一台路由器联通。这里需要注意的是，一般情况下，我们说网络设备都有一个 IP 地址和一个 MAC 地址，这里说的网络设备，更严谨的说法应该是一个接口。路由器作为互联设备，具有多个接口，每个接口同样也应该具备不重复的 IP 地址和 MAC 地址。因此，在讨论 ARP 表时，路由器的多个接口都各自维护一个 ARP 表，而非一个路由器只维护一个 ARP 表。</p>\n<p>接下来，回顾同一子网内的 MAC 寻址，如果主机 A 发送一个广播问询分组，那么 A 所在的子网内所有设备（接口）都将会捕获该分组，因为该分组的目的 IP 与发送主机 A 的 IP 在同一个子网中。但是当目的 IP 与 A 不在同一子网时，A 所在子网内将不会有设备成功接收该分组。那么，主机 A 应该发送怎样的查询分组呢？整个过程按照时间顺序发生的事件如下：</p>\n<ol>\n<li>\n<p>主机 A 查询 ARP 表，期望寻找到目标路由器的本子网接口的 MAC 地址。</p>\n<p>目标路由器指的是，根据目的主机 B 的 IP 地址，分析出 B 所在的子网，能够把报文转发到 B 所在子网的那个路由器。</p>\n</li>\n<li>\n<p>主机 A 未能找到目标路由器的本子网接口的 MAC 地址，将采用 ARP 协议，问询到该 MAC 地址，由于目标接口与主机 A 在同一个子网内，该过程与同一局域网内的 MAC 寻址相同。</p>\n</li>\n<li>\n<p>主机 A 获取到目标接口的 MAC 地址，先构造 IP 数据报，其中源 IP 是 A 的 IP 地址，目的 IP 地址是 B 的 IP 地址，再构造链路层帧，其中源 MAC 地址是 A 的 MAC 地址，目的 MAC 地址是<strong>本子网内与路由器连接的接口的 MAC 地址</strong>。主机 A 将把这个链路层帧，以单播的方式，发送给目标接口。</p>\n</li>\n<li>\n<p>目标接口接收到了主机 A 发过来的链路层帧，解析，根据目的 IP 地址，查询转发表，将该 IP 数据报转发到与主机 B 所在子网相连的接口上。</p>\n<p>到此，该帧已经从主机 A 所在的子网，转移到了主机 B 所在的子网了。</p>\n</li>\n<li>\n<p>路由器接口查询 ARP 表，期望寻找到主机 B 的 MAC 地址。</p>\n</li>\n<li>\n<p>路由器接口如未能找到主机 B 的 MAC 地址，将采用 ARP 协议，广播问询，单播响应，获取到主机 B 的 MAC 地址。</p>\n</li>\n<li>\n<p>路由器接口将对 IP 数据报重新封装成链路层帧，目标 MAC 地址为主机 B 的 MAC 地址，单播发送，直到目的地。</p>\n</li>\n</ol>\n<p></p>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/cs-basics/network/router-back-will-indicate-mac-address.png",
      "date_published": "2022-08-28T07:18:49.000Z",
      "date_modified": "2026-01-16T13:04:11.000Z",
      "authors": [],
      "tags": [
        "计算机基础"
      ]
    },
    {
      "title": "分布式锁常见实现方案总结",
      "url": "https://javaguide.cn/distributed-system/distributed-lock-implementations.html",
      "id": "https://javaguide.cn/distributed-system/distributed-lock-implementations.html",
      "summary": "分布式锁常见实现方案详解，包括基于Redis SETNX、Redlock、ZooKeeper临时节点实现分布式锁的原理、优缺点对比及最佳实践。",
      "content_html": "<p><a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/xingqiu.png\" alt=\"JavaGuide官方知识星球\"></a></p>\n<p>通常情况下，我们一般会选择基于 Redis 或者 ZooKeeper 实现分布式锁，Redis 用的要更多一点，我这里也先以 Redis 为例介绍分布式锁的实现。</p>\n<h2>基于 Redis 实现分布式锁</h2>\n<h3>如何基于 Redis 实现一个最简易的分布式锁？</h3>\n<p>不论是本地锁还是分布式锁，核心都在于“互斥”。</p>\n<p>在 Redis 中， <code>SETNX</code> 命令是可以帮助我们实现互斥。<code>SETNX</code> 即 <strong>SET</strong> if <strong>N</strong>ot e<strong>X</strong>ists (对应 Java 中的 <code>setIfAbsent</code> 方法)，如果 key 不存在的话，才会设置 key 的值。如果 key 已经存在， <code>SETNX</code> 啥也不做。</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> SETNX lockKey uniqueValue</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">1</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> SETNX lockKey uniqueValue</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">0</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>释放锁的话，直接通过 <code>DEL</code> 命令删除对应的 key 即可。</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> DEL lockKey</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">integer</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">1</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>为了防止误删到其他的锁，这里我们建议使用 Lua 脚本通过 key 对应的 value（唯一值）来判断。</p>\n<p>选用 Lua 脚本是为了保证解锁操作的原子性。因为 Redis 在执行 Lua 脚本时，可以以原子性的方式执行，从而保证了锁释放操作的原子性。</p>\n<div class=\"language-lua line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"lua\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-lua\"><span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">// 释放锁时，先比较锁对应的 </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">value</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> 值是否相等，避免锁的误释放</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">if</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> redis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">call</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"get\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">KEYS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">]) == </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">ARGV</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">] </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">then</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> redis</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">call</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"del\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">KEYS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">[</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">])</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 0</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">end</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/distributed-lock/distributed-lock-setnx.png\" alt=\"Redis 实现简易分布式锁\"></p>\n<p>这是一种最简易的 Redis 分布式锁实现，实现方式比较简单，性能也很高效。不过，这种方式实现分布式锁存在一些问题。就比如应用程序遇到一些问题比如释放锁的逻辑突然挂掉，可能会导致锁无法被释放，进而造成共享资源无法再被其他线程/进程访问。</p>\n<h3>为什么要给锁设置一个过期时间？</h3>\n<p>为了避免锁无法被释放，我们可以想到的一个解决办法就是：<strong>给这个 key（也就是锁） 设置一个过期时间</strong> 。</p>\n<div class=\"language-bash line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"bash\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-bash\"><span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">127.0.0.1:6379</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">SET</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> lockKey</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> uniqueValue</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> EX</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 3</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> NX</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">OK</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><ul>\n<li><strong>lockKey</strong>：加锁的锁名；</li>\n<li><strong>uniqueValue</strong>：能够唯一标识锁的随机字符串；</li>\n<li><strong>NX</strong>：只有当 lockKey 对应的 key 值不存在的时候才能 SET 成功；</li>\n<li><strong>EX</strong>：过期时间设置（秒为单位）EX 3 标示这个锁有一个 3 秒的自动过期时间。与 EX 对应的是 PX（毫秒为单位），这两个都是过期时间设置。</li>\n</ul>\n<p><strong>一定要保证设置指定 key 的值和过期时间是一个原子操作！！！</strong> 不然的话，依然可能会出现锁无法被释放的问题。</p>\n<p>这样确实可以解决问题，不过，这种解决办法同样存在漏洞：<strong>如果操作共享资源的时间大于过期时间，就会出现锁提前过期的问题，进而导致分布式锁直接失效。如果锁的超时时间设置过长，又会影响到性能。</strong></p>\n<p>你或许在想：<strong>如果操作共享资源的操作还未完成，锁过期时间能够自己续期就好了！</strong></p>\n<h3>如何实现锁的优雅续期？</h3>\n<p>对于 Java 开发的小伙伴来说，已经有了现成的解决方案：<strong><a href=\"https://github.com/redisson/redisson\" target=\"_blank\" rel=\"noopener noreferrer\">Redisson</a></strong> 。其他语言的解决方案，可以在 Redis 官方文档中找到，地址：<a href=\"https://redis.io/topics/distlock\" target=\"_blank\" rel=\"noopener noreferrer\">https://redis.io/topics/distlock</a> 。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/redis-distributed-lock.png\" alt=\"Distributed locks with Redis\"></p>\n<p>Redisson 是一个开源的 Java 语言 Redis 客户端，提供了很多开箱即用的功能，不仅仅包括多种分布式锁的实现。并且，Redisson 还支持 Redis 单机、Redis Sentinel、Redis Cluster 等多种部署架构。</p>\n<p>Redisson 中的分布式锁自带自动续期机制，使用起来非常简单，原理也比较简单，其提供了一个专门用来监控和续期锁的 <strong>Watch Dog（ 看门狗）</strong>，如果操作共享资源的线程还未执行完成的话，Watch Dog 会不断地延长锁的过期时间，进而保证锁不会因为超时而被释放。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/distributed-lock/distributed-lock-redisson-renew-expiration.png\" alt=\"Redisson 看门狗自动续期\"></p>\n<p>看门狗名字的由来于 <code>getLockWatchdogTimeout()</code> 方法，这个方法返回的是看门狗给锁续期的过期时间，默认为 30 秒（<a href=\"https://github.com/redisson/redisson/releases/tag/redisson-3.17.6\" target=\"_blank\" rel=\"noopener noreferrer\">redisson-3.17.6</a>）。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">//默认 30秒，支持修改</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lockWatchdogTimeout </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 30</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> *</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 1000</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Config</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> setLockWatchdogTimeout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> lockWatchdogTimeout) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lockWatchdogTimeout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> lockWatchdogTimeout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> long</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getLockWatchdogTimeout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">   return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> lockWatchdogTimeout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>renewExpiration()</code> 方法包含了看门狗的主要逻辑：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> renewExpiration</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">         //......</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">        Timeout</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> task </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> commandExecutor</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getConnectionManager</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">().</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newTimeout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> TimerTask</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            @</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">            public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> run</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Timeout</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> timeout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> throws</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Exception</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                //......</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                // 异步续期，基于 Lua 脚本</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">                CompletionStage</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Boolean</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">> </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\">future</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> renewExpirationAsync</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(threadId);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                future</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">whenComplete</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">((res, e) </span><span style=\"--shiki-light:#C18401;--shiki-dark:#C678DD\">-></span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (e </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                        // 无法续期</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                        log</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">error</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Can't update lock \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getRawName</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">() </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">+</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\"> \" expiration\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, e);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">                        EXPIRATION_RENEWAL_MAP</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">remove</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getEntryName</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                        return</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">                    if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> (res) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                        // 递归调用实现续期</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">                        renewExpiration</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                    } </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">else</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">                        // 取消续期</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">                        cancelExpirationRenewal</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">                });</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">            }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">         // 延迟 internalLockLeaseTime/3（默认 10s，也就是 30/3） 再调用</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">        }, internalLockLeaseTime </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">/</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> 3</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">MILLISECONDS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">        ee</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">setTimeout</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(task);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">    }</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>默认情况下，每过 10 秒，看门狗就会执行续期操作，将锁的超时时间设置为 30 秒。看门狗续期前也会先判断是否需要执行续期操作，需要才会执行续期，否则取消续期操作。</p>\n<p>Watch Dog 通过调用 <code>renewExpirationAsync()</code> 方法实现锁的异步续期：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">protected</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> CompletionStage</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">&#x3C;</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">Boolean</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">></span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> renewExpirationAsync</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> threadId) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> evalWriteAsync</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getRawName</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">()</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> LongCodec</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">INSTANCE</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> RedisCommands</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">EVAL_BOOLEAN</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">            // 判断是否为持锁线程，如果是就执行续期操作，就锁的过期时间设置为 30s（默认）</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">            \"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">                    \"redis.call('pexpire', KEYS[1], ARGV[1]); \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">                    \"return 1; \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">                    \"end; \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">                    \"return 0;\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">            Collections</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">singletonList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getRawName</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">()),</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">            internalLockLeaseTime</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> getLockName</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(threadId))</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>可以看出， <code>renewExpirationAsync</code> 方法其实是调用 Lua 脚本实现的续期，这样做主要是为了保证续期操作的原子性。</p>\n<p>我这里以 Redisson 的分布式可重入锁 <code>RLock</code> 为例来说明如何使用 Redisson 实现分布式锁：</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 1.获取指定的分布式锁对象</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">RLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> redisson</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getLock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"lock\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 2.拿锁且不设置锁超时时间，具备 Watch Dog 自动续期机制</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 3.执行业务</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">...</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 4.释放锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">unlock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>只有未指定锁超时时间，才会使用到 Watch Dog 自动续期机制。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 手动给锁设置过期时间，不具备 Watch Dog 自动续期机制</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">SECONDS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>如果使用 Redis 来实现分布式锁的话，还是比较推荐直接基于 Redisson 来做的。</p>\n<h3>如何实现可重入锁？</h3>\n<p>所谓可重入锁指的是在一个线程中可以多次获取同一把锁，比如一个线程在执行一个带锁的方法，该方法中又调用了另一个需要相同锁的方法，则该线程可以直接执行调用的方法即可重入 ，而无需重新获得锁。像 Java 中的 <code>synchronized</code> 和 <code>ReentrantLock</code> 都属于可重入锁。</p>\n<p><strong>不可重入的分布式锁基本可以满足绝大部分业务场景了，一些特殊的场景可能会需要使用可重入的分布式锁。</strong></p>\n<p>可重入分布式锁的实现核心思路是线程在获取锁的时候判断是否为自己的锁，如果是的话，就不用再重新获取了。为此，我们可以为每个锁关联一个可重入计数器和一个占有它的线程。当可重入计数器大于 0 时，则锁被占有，需要判断占有该锁的线程和请求获取锁的线程是否为同一个。</p>\n<p>实际项目中，我们不需要自己手动实现，推荐使用我们上面提到的 <strong>Redisson</strong> ，其内置了多种类型的锁比如可重入锁（Reentrant Lock）、自旋锁（Spin Lock）、公平锁（Fair Lock）、多重锁（MultiLock）、 红锁（RedLock）、 读写锁（ReadWriteLock）。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/distributed-lock/redisson-readme-locks.png\" alt></p>\n<h3>Redis 如何解决集群情况下分布式锁的可靠性？</h3>\n<p>为了避免单点故障，生产环境下的 Redis 服务通常是集群化部署的。</p>\n<p>Redis 集群下，上面介绍到的分布式锁的实现会存在一些问题。由于 Redis 集群数据同步到各个节点时是异步的，如果在 Redis 主节点获取到锁后，在没有同步到其他节点时，Redis 主节点宕机了，此时新的 Redis 主节点依然可以获取锁，所以多个应用服务就可以同时获取到锁。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/distributed-lock/redis-master-slave-distributed-lock.png\" alt></p>\n<p>针对这个问题，Redis 之父 antirez 设计了 <a href=\"https://redis.io/topics/distlock\" target=\"_blank\" rel=\"noopener noreferrer\">Redlock 算法</a> 来解决。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/distributed-lock/distributed-lock-redis.io-realock.png\" alt></p>\n<p>Redlock 算法的思想是让客户端向 Redis 集群中的多个独立的 Redis 实例依次请求申请加锁，如果客户端能够和半数以上的实例成功地完成加锁操作，那么我们就认为，客户端成功地获得分布式锁，否则加锁失败。</p>\n<p>即使部分 Redis 节点出现问题，只要保证 Redis 集群中有半数以上的 Redis 节点可用，分布式锁服务就是正常的。</p>\n<p>Redlock 是直接操作 Redis 节点的，并不是通过 Redis 集群操作的，这样才可以避免 Redis 集群主从切换导致的锁丢失问题。</p>\n<p>Redlock 实现比较复杂，性能比较差，发生时钟变迁的情况下还存在安全性隐患。《数据密集型应用系统设计》一书的作者 Martin Kleppmann 曾经专门发文（<a href=\"https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html\" target=\"_blank\" rel=\"noopener noreferrer\">How to do distributed locking - Martin Kleppmann - 2016</a>）怼过 Redlock，他认为这是一个很差的分布式锁实现。感兴趣的朋友可以看看<a href=\"https://mp.weixin.qq.com/s?__biz=Mzg3NjU3NTkwMQ==&amp;mid=2247505097&amp;idx=1&amp;sn=5c03cb769c4458350f4d4a321ad51f5a&amp;source=41#wechat_redirect\" target=\"_blank\" rel=\"noopener noreferrer\">Redis 锁从面试连环炮聊到神仙打架</a>这篇文章，有详细介绍到 antirez 和 Martin Kleppmann 关于 Redlock 的激烈辩论。</p>\n<p>实际项目中不建议使用 Redlock 算法，成本和收益不成正比，可以考虑基于 Redis 主从复制+哨兵模式实现分布式锁。</p>\n<h2>基于 ZooKeeper 实现分布式锁</h2>\n<p>ZooKeeper 相比于 Redis 实现分布式锁，除了提供相对更高的可靠性之外，在功能层面还有一个非常有用的特性：<strong>Watch 机制</strong>。这个机制可以用来实现公平的分布式锁。不过，使用 ZooKeeper 实现的分布式锁在性能方面相对较差，因此如果对性能要求比较高的话，ZooKeeper 可能就不太适合了。</p>\n<h3>如何基于 ZooKeeper 实现分布式锁？</h3>\n<p>ZooKeeper 分布式锁是基于 <strong>临时顺序节点</strong> 和 <strong>Watcher（事件监听器）</strong> 实现的。</p>\n<p>获取锁：</p>\n<ol>\n<li>首先我们要有一个持久节点<code>/locks</code>，客户端获取锁就是在<code>locks</code>下创建临时顺序节点。</li>\n<li>假设客户端 1 创建了<code>/locks/lock1</code>节点，创建成功之后，会判断 <code>lock1</code>是否是 <code>/locks</code> 下最小的子节点。</li>\n<li>如果 <code>lock1</code>是最小的子节点，则获取锁成功。否则，获取锁失败。</li>\n<li>如果获取锁失败，则说明有其他的客户端已经成功获取锁。客户端 1 并不会不停地循环去尝试加锁，而是在前一个节点比如<code>/locks/lock0</code>上注册一个事件监听器。这个监听器的作用是当前一个节点释放锁之后通知客户端 1（避免无效自旋），这样客户端 1 就加锁成功了。</li>\n</ol>\n<p>释放锁：</p>\n<ol>\n<li>成功获取锁的客户端在执行完业务流程之后，会将对应的子节点删除。</li>\n<li>成功获取锁的客户端在出现故障之后，对应的子节点由于是临时顺序节点，也会被自动删除，避免了锁无法被释放。</li>\n<li>我们前面说的事件监听器其实监听的就是这个子节点删除事件，子节点删除就意味着锁被释放。</li>\n</ol>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/distributed-lock/distributed-lock-zookeeper.png\" alt></p>\n<p>实际项目中，推荐使用 Curator 来实现 ZooKeeper 分布式锁。Curator 是 Netflix 公司开源的一套 ZooKeeper Java 客户端框架，相比于 ZooKeeper 自带的客户端 zookeeper 来说，Curator 的封装更加完善，各种 API 都可以比较方便地使用。</p>\n<p><code>Curator</code>主要实现了下面四种锁：</p>\n<ul>\n<li><code>InterProcessMutex</code>：分布式可重入排它锁</li>\n<li><code>InterProcessSemaphoreMutex</code>：分布式不可重入排它锁</li>\n<li><code>InterProcessReadWriteLock</code>：分布式读写锁</li>\n<li><code>InterProcessMultiLock</code>：将多个锁作为单个实体管理的容器，获取锁的时候获取所有锁，释放锁也会释放所有锁资源（忽略释放失败的锁）。</li>\n</ul>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">CuratorFramework</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> client </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> ZKUtils</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getClient</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">start</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 分布式可重入排它锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">InterProcessLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock1 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> InterProcessMutex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> lockPath1)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 分布式不可重入排它锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">InterProcessLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock2 </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> InterProcessSemaphoreMutex</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> lockPath2)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 将多个锁作为一个整体</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">InterProcessMultiLock</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lock </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> InterProcessMultiLock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">Arrays</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">asList</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(lock1, lock2)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">acquire</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">10</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">SECONDS</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">   throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> IllegalStateException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"不能获取多锁\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"已获取多锁\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"是否有第一个锁: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> lock1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isAcquiredInThisProcess</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"是否有第二个锁: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> lock2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isAcquiredInThisProcess</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">try</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 资源操作</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    resource</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">use</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">} </span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">finally</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"释放多个锁\"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    lock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">release</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"是否有第一个锁: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> lock1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isAcquiredInThisProcess</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">System</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">out</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">println</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"是否有第二个锁: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> lock2</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">isAcquiredInThisProcess</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">client</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">close</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><h3>为什么要用临时顺序节点？</h3>\n<p>每个数据节点在 ZooKeeper 中被称为 <strong>znode</strong>，它是 ZooKeeper 中数据的最小单元。</p>\n<p>我们通常是将 znode 分为 4 大类：</p>\n<ul>\n<li><strong>持久（PERSISTENT）节点</strong>：一旦创建就一直存在即使 ZooKeeper 集群宕机，直到将其删除。</li>\n<li><strong>临时（EPHEMERAL）节点</strong>：临时节点的生命周期是与 <strong>客户端会话（session）</strong> 绑定的，<strong>会话消失则节点消失</strong> 。并且，<strong>临时节点只能做叶子节点</strong> ，不能创建子节点。</li>\n<li><strong>持久顺序（PERSISTENT_SEQUENTIAL）节点</strong>：除了具有持久（PERSISTENT）节点的特性之外， 子节点的名称还具有顺序性。比如 <code>/node1/app0000000001</code>、<code>/node1/app0000000002</code> 。</li>\n<li><strong>临时顺序（EPHEMERAL_SEQUENTIAL）节点</strong>：除了具备临时（EPHEMERAL）节点的特性之外，子节点的名称还具有顺序性。</li>\n</ul>\n<p>可以看出，临时节点相比持久节点，最主要的是对会话失效的情况处理不一样，临时节点会话消失则对应的节点消失。这样的话，如果客户端发生异常导致没来得及释放锁也没关系，会话失效节点自动被删除，不会发生死锁的问题。</p>\n<p>使用 Redis 实现分布式锁的时候，我们是通过过期时间来避免锁无法被释放导致死锁问题的，而 ZooKeeper 直接利用临时节点的特性即可。</p>\n<p>假设不使用顺序节点的话，所有尝试获取锁的客户端都会对持有锁的子节点加监听器。当该锁被释放之后，势必会造成所有尝试获取锁的客户端来争夺锁，这样对性能不友好。使用顺序节点之后，只需要监听前一个节点就好了，对性能更友好。</p>\n<h3>为什么要设置对前一个节点的监听？</h3>\n<blockquote>\n<p>Watcher（事件监听器），是 ZooKeeper 中的一个很重要的特性。ZooKeeper 允许用户在指定节点上注册一些 Watcher，并且在一些特定事件触发的时候，ZooKeeper 服务端会将事件通知到感兴趣的客户端上去，该机制是 ZooKeeper 实现分布式协调服务的重要特性。</p>\n</blockquote>\n<p>同一时间段内，可能会有很多客户端同时获取锁，但只有一个可以获取成功。如果获取锁失败，则说明有其他的客户端已经成功获取锁。获取锁失败的客户端并不会不停地循环去尝试加锁，而是在前一个节点注册一个事件监听器。</p>\n<p>这个事件监听器的作用是：<strong>当前一个节点对应的客户端释放锁之后（也就是前一个节点被删除之后，监听的是删除事件），通知获取锁失败的客户端（唤醒等待的线程，Java 中的 <code>wait/notifyAll</code> ），让它尝试去获取锁，然后就成功获取锁了。</strong></p>\n<h3>如何实现可重入锁？</h3>\n<p>这里以 Curator 的 <code>InterProcessMutex</code> 对可重入锁的实现来介绍（源码地址：<a href=\"https://github.com/apache/curator/blob/master/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/InterProcessMutex.java\" target=\"_blank\" rel=\"noopener noreferrer\">InterProcessMutex.java</a>）。</p>\n<p>当我们调用 <code>InterProcessMutex#acquire</code>方法获取锁的时候，会调用<code>InterProcessMutex#internalLock</code>方法。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">// 获取可重入互斥锁，直到获取成功为止</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">@</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#E5C07B\">Override</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">public</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> void</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> acquire</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">() throws Exception {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">internalLock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">-</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    throw</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> IOException</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#50A14F;--shiki-dark:#98C379\">\"Lost connection while trying to acquire lock: \"</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> +</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> basePath)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>internalLock</code> 方法会先获取当前请求锁的线程，然后从 <code>threadData</code>( <code>ConcurrentMap&lt;Thread, LockData&gt;</code> 类型)中获取当前线程对应的 <code>lockData</code> 。 <code>lockData</code> 包含锁的信息和加锁的次数，是实现可重入锁的关键。</p>\n<p>第一次获取锁的时候，<code>lockData</code>为 <code>null</code>。获取锁成功之后，会将当前线程和对应的 <code>lockData</code> 放到 <code>threadData</code> 中</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> boolean</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> internalLock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">long</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> time</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> TimeUnit</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> unit) throws Exception {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 获取当前请求锁的线程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> currentThread </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Thread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">currentThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 拿对应的 lockData</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  LockData</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lockData </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> threadData</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">get</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(currentThread);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 第一次获取锁的话，lockData 为 null</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (lockData </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 当前线程获取过一次锁之后</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 因为当前线程的锁存在， lockCount 自增后返回，实现锁重入.</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    lockData</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lockCount</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">incrementAndGet</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">  }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">  // 尝试获取锁</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">  String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lockPath </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> internals</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">attemptLock</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(time, unit, </span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">getLockNodeBytes</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">());</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  if</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> (lockPath </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">!=</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> null</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">    LockData</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> newLockData </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> LockData</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(currentThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\"> lockPath)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">     // 获取锁成功之后，将当前线程和对应的 lockData 放到 threadData 中</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">    threadData</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">put</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(currentThread, newLockData);</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> true</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">  }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">  return</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\"> false</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p><code>LockData</code>是 <code>InterProcessMutex</code>中的一个静态内部类。</p>\n<div class=\"language-java line-numbers-mode\" data-highlighter=\"shiki\" data-ext=\"java\" style=\"--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34\"><pre class=\"shiki shiki-themes one-light one-dark-pro vp-code\"><code class=\"language-java\"><span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> ConcurrentMap</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">&#x3C;</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">,</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LockData</span><span style=\"--shiki-light:#E45649;--shiki-dark:#ABB2BF\">></span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> threadData </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\"> Maps</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\">newConcurrentMap</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">private</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> static</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> class</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> LockData</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">{</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 当前持有锁的线程</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> Thread</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> owningThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 锁对应的子节点</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> String</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lockPath</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">    // 加锁的次数</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    final</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\"> AtomicInteger</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E06C75\"> lockCount </span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\">=</span><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\"> new</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> AtomicInteger</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">(</span><span style=\"--shiki-light:#986801;--shiki-dark:#D19A66\">1</span><span style=\"--shiki-light:#383A42;--shiki-dark:#E06C75\">)</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#A626A4;--shiki-dark:#C678DD\">    private</span><span style=\"--shiki-light:#4078F2;--shiki-dark:#61AFEF\"> LockData</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">(</span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">Thread</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> owningThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">, </span><span style=\"--shiki-light:#C18401;--shiki-dark:#E5C07B\">String</span><span style=\"--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic\"> lockPath</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">      this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">owningThread</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> owningThread;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">      this</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">.</span><span style=\"--shiki-light:#E45649;--shiki-dark:#E5C07B\">lockPath</span><span style=\"--shiki-light:#383A42;--shiki-dark:#56B6C2\"> =</span><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\"> lockPath;</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#383A42;--shiki-dark:#ABB2BF\">}</span></span></code></pre>\n<div class=\"line-numbers\" aria-hidden=\"true\" style=\"counter-reset:line-number 0\"><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div><div class=\"line-number\"></div></div></div><p>如果已经获取过一次锁，后面再来获取锁的话，直接就会在 <code>if (lockData != null)</code> 这里被拦下了，然后就会执行<code>lockData.lockCount.incrementAndGet();</code> 将加锁次数加 1。</p>\n<p>整个可重入锁的实现逻辑非常简单，直接在客户端判断当前线程有没有获取锁，有的话直接将加锁次数加 1 就可以了。</p>\n<h2>总结</h2>\n<p>在这篇文章中，我介绍了实现分布式锁的两种常见方式：<strong>Redis</strong> 和 <strong>ZooKeeper</strong>。至于具体选择 Redis 还是 ZooKeeper 来实现分布式锁，还是要根据业务的具体需求来决定。</p>\n<ul>\n<li>如果对性能要求比较高的话，建议使用 Redis 实现分布式锁。推荐优先选择 <strong>Redisson</strong> 提供的现成分布式锁，而不是自己实现。实际项目中不建议使用 Redlock 算法，成本和收益不成正比，可以考虑基于 Redis 主从复制+哨兵模式实现分布式锁。</li>\n<li>如果对可靠性要求比较高，建议使用 ZooKeeper 实现分布式锁，推荐基于 <strong>Curator</strong> 框架来实现。不过，现在很多项目都不会用到 ZooKeeper，如果单纯是因为分布式锁而引入 ZooKeeper 的话，那是不太可取的，不建议这样做，为了一个小小的功能增加了系统的复杂度。</li>\n</ul>\n<p>需要注意的是，无论选择哪种方式实现分布式锁，包括 Redis、ZooKeeper 或 Etcd（本文没介绍，但也经常用来实现分布式锁），都无法保证 100% 的安全性，特别是在遇到进程垃圾回收（GC）、网络延迟等异常情况下。</p>\n<p>为了进一步提高系统的可靠性，建议引入一个兜底机制。例如，可以通过 <strong>版本号（Fencing Token）机制</strong> 来避免并发冲突。</p>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/distributed-system/distributed-lock/distributed-lock-setnx.png",
      "date_published": "2022-08-23T10:53:21.000Z",
      "date_modified": "2026-03-12T04:06:59.000Z",
      "authors": [],
      "tags": [
        "分布式"
      ]
    },
    {
      "title": "分布式锁入门介绍",
      "url": "https://javaguide.cn/distributed-system/distributed-lock.html",
      "id": "https://javaguide.cn/distributed-system/distributed-lock.html",
      "summary": "分布式锁基础概念详解，讲解为什么需要分布式锁、分布式锁的核心特性（互斥性、防死锁、可重入）、常见应用场景（秒杀、库存扣减）分析。",
      "content_html": "<p><a href=\"/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\"><img src=\"https://oss.javaguide.cn/xingqiu/xingqiu.png\" alt=\"JavaGuide官方知识星球\"></a></p>\n<p>网上有很多分布式锁相关的文章，写了一个相对简洁易懂的版本，针对面试和工作应该够用了。</p>\n<p>这篇文章我们先介绍一下分布式锁的基本概念。</p>\n<h2>为什么需要分布式锁？</h2>\n<p>在多线程环境中，如果多个线程同时访问共享资源（例如商品库存、外卖订单），会发生数据竞争，可能会导致出现脏数据或者系统问题，威胁到程序的正常运行。</p>\n<p>举个例子，假设现在有 100 个用户参与某个限时秒杀活动，每位用户限购 1 件商品，且商品的数量只有 3 个。如果不对共享资源进行互斥访问，就可能出现以下情况：</p>\n<ul>\n<li>线程 1、2、3 等多个线程同时进入抢购方法，每一个线程对应一个用户。</li>\n<li>线程 1 查询用户已经抢购的数量，发现当前用户尚未抢购且商品库存还有 1 个，因此认为可以继续执行抢购流程。</li>\n<li>线程 2 也执行查询用户已经抢购的数量，发现当前用户尚未抢购且商品库存还有 1 个，因此认为可以继续执行抢购流程。</li>\n<li>线程 1 继续执行，将库存数量减少 1 个，然后返回成功。</li>\n<li>线程 2 继续执行，将库存数量减少 1 个，然后返回成功。</li>\n<li>此时就发生了超卖问题，导致商品被多卖了一份。</li>\n</ul>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/distributed-lock/oversold-without-locking.png\" alt=\"共享资源未互斥访问导致出现问题\"></p>\n<p>为了保证共享资源被安全地访问，我们需要使用互斥操作对共享资源进行保护，即同一时刻只允许一个线程访问共享资源，其他线程需要等待当前线程释放后才能访问。这样可以避免数据竞争和脏数据问题，保证程序的正确性和稳定性。</p>\n<p><strong>如何才能实现共享资源的互斥访问呢？</strong> 锁是一个比较通用的解决方案，更准确点来说是悲观锁。</p>\n<p>悲观锁总是假设最坏的情况，认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改)，所以每次在获取资源操作的时候都会上锁，这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。也就是说，<strong>共享资源每次只给一个线程使用，其它线程阻塞，用完后再把资源转让给其它线程</strong>。</p>\n<p>对于单机多线程来说，在 Java 中，我们通常使用 <code>ReentrantLock</code> 类、<code>synchronized</code> 关键字这类 JDK 自带的 <strong>本地锁</strong> 来控制一个 JVM 进程内的多个线程对本地共享资源的访问。</p>\n<p>下面是我对本地锁画的一张示意图。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/distributed-lock/jvm-local-lock.png\" alt=\"本地锁\"></p>\n<p>从图中可以看出，这些线程访问共享资源是互斥的，同一时刻只有一个线程可以获取到本地锁访问共享资源。</p>\n<p>分布式系统下，不同的服务/客户端通常运行在独立的 JVM 进程上。如果多个 JVM 进程共享同一份资源的话，使用本地锁就没办法实现资源的互斥访问了。于是，<strong>分布式锁</strong> 就诞生了。</p>\n<p>举个例子：系统的订单服务一共部署了 3 份，都对外提供服务。用户下订单之前需要检查库存，为了防止超卖，这里需要加锁以实现对检查库存操作的同步访问。由于订单服务位于不同的 JVM 进程中，本地锁在这种情况下就没办法正常工作了。我们需要用到分布式锁，这样的话，即使多个线程不在同一个 JVM 进程中也能获取到同一把锁，进而实现共享资源的互斥访问。</p>\n<p>下面是我对分布式锁画的一张示意图。</p>\n<p><img src=\"https://oss.javaguide.cn/github/javaguide/distributed-system/distributed-lock/distributed-lock.png\" alt=\"分布式锁\"></p>\n<p>从图中可以看出，这些独立的进程中的线程访问共享资源是互斥的，同一时刻只有一个线程可以获取到分布式锁访问共享资源。</p>\n<h2>分布式锁应该具备哪些条件？</h2>\n<p>一个最基本的分布式锁需要满足：</p>\n<ul>\n<li><strong>互斥</strong>：任意一个时刻，锁只能被一个线程持有。</li>\n<li><strong>高可用</strong>：锁服务是高可用的，当一个锁服务出现问题，能够自动切换到另外一个锁服务。并且，即使客户端的释放锁的代码逻辑出现问题，锁最终一定还是会被释放，不会影响其他线程对共享资源的访问。这一般是通过超时机制实现的。</li>\n<li><strong>可重入</strong>：一个节点获取了锁之后，还可以再次获取锁。</li>\n</ul>\n<p>除了上面这三个基本条件之外，一个好的分布式锁还需要满足下面这些条件：</p>\n<ul>\n<li><strong>高性能</strong>：获取和释放锁的操作应该快速完成，并且不应该对整个系统的性能造成过大影响。</li>\n<li><strong>非阻塞</strong>：如果获取不到锁，不能无限期等待，避免对系统正常运行造成影响。</li>\n</ul>\n<h2>分布式锁的常见实现方式有哪些？</h2>\n<p>常见分布式锁实现方案如下：</p>\n<ul>\n<li>基于关系型数据库比如 MySQL 实现分布式锁。</li>\n<li>基于分布式协调服务 ZooKeeper 实现分布式锁。</li>\n<li>基于分布式键值存储系统比如 Redis 、Etcd 实现分布式锁。</li>\n</ul>\n<p>关系型数据库的方式一般是通过唯一索引或者排他锁实现。不过，一般不会使用这种方式，问题太多比如性能太差、不具备锁失效机制。</p>\n<p>基于 ZooKeeper 或者 Redis 实现分布式锁这两种实现方式要用的更多一些，我专门写了一篇文章来详细介绍这两种方案：<a href=\"/distributed-system/distributed-lock-implementations.html\" target=\"_blank\">分布式锁常见实现方案总结</a>。</p>\n<h2>总结</h2>\n<p>这篇文章我们主要介绍了：</p>\n<ul>\n<li>分布式锁的用途：分布式系统下，不同的服务/客户端通常运行在独立的 JVM 进程上。如果多个 JVM 进程共享同一份资源的话，使用本地锁就没办法实现资源的互斥访问了。</li>\n<li>分布式锁的应该具备的条件：互斥、高可用、可重入、高性能、非阻塞。</li>\n<li>分布式锁的常见实现方式：关系型数据库比如 MySQL、分布式协调服务 ZooKeeper、分布式键值存储系统比如 Redis 、Etcd 。</li>\n</ul>\n<h2>写在最后</h2>\n<p>感谢你能看到这里，也希望这篇文章对你有点用。</p>\n<p>JavaGuide 坚持更新 6 年多，近 6000 次提交、600+ 位贡献者一起打磨。如果这些内容对你有帮助，非常欢迎点个免费的 Star 支持下（完全自愿，觉得有收获再点就好）：<a href=\"https://github.com/Snailclimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> | <a href=\"https://gitee.com/SnailClimb/JavaGuide\" target=\"_blank\" rel=\"noopener noreferrer\">Gitee</a>。</p>\n<p>如果你想要付费支持/面试辅导（比如实战项目、简历优化、一对一提问、高频考点突击资料等）的话，欢迎了解我的<a href=\"https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html\" target=\"_blank\" rel=\"noopener noreferrer\">知识星球</a>。已经坚持维护六年，内容持续更新，虽白菜价（0.4元/天）但质量很高，主打一个良心！</p>\n<img src=\"https://oss.javaguide.cn/github/javaguide/gongzhonghao-javaguide.png\" alt=\"JavaGuide 公众号\" style=\"zoom: 43%; display: block; margin: 0 auto;\">\n",
      "image": "https://oss.javaguide.cn/github/javaguide/distributed-system/distributed-lock/oversold-without-locking.png",
      "date_published": "2022-08-23T10:53:21.000Z",
      "date_modified": "2026-03-12T04:06:59.000Z",
      "authors": [],
      "tags": [
        "分布式"
      ]
    }
  ]
}