<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>LiuGuang</title><description>AI与日常</description><link>https://www.liuguang.top/</link><templateTheme>Firefly</templateTheme><templateThemeVersion>6.8.7</templateThemeVersion><templateThemeUrl>https://github.com/CuteLeaf/Firefly</templateThemeUrl><lastBuildDate>2026年5月1日 19:44:24</lastBuildDate><item><title>Firefly 简单使用指南（liuguang）</title><link>https://www.liuguang.top/posts/guide/index2/</link><guid isPermaLink="true">https://www.liuguang.top/posts/guide/index2/</guid><description>如何使用 当前 博客模板。</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这个博客模板是基于 &lt;a href=&quot;https://astro.build/&quot; target=&quot;_blank&quot;&gt;Astro&lt;/a&gt; 构建的。对于本指南中未提及的内容，您可以在 &lt;a href=&quot;https://docs.astro.build/&quot; target=&quot;_blank&quot;&gt;Astro 文档&lt;/a&gt; 中找到答案。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;文章的 Front-matter&lt;a href=&quot;#文章的-front-matter&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;我的第一篇博客文章&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;published&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2023-09-09&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;这是我新 Astro 博客的第一篇文章。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;./cover.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;前端&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;开发&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;category&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;前端开发&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;draft&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
















































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;属性&lt;/th&gt;&lt;th&gt;描述&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;title&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章标题。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;published&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章发布日期。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;updated&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章更新日期。如果未设置，将默认使用发布日期。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pinned&lt;/code&gt;&lt;/td&gt;&lt;td&gt;是否将此文章置顶在文章列表顶部。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章的简短描述。显示在首页上。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章封面图片路径。&lt;br /&gt;1. 以 &lt;code&gt;http://&lt;/code&gt; 或 &lt;code&gt;https://&lt;/code&gt; 开头：使用网络图片&lt;br /&gt;2. 以 &lt;code&gt;/&lt;/code&gt; 开头：&lt;code&gt;public&lt;/code&gt; 目录中的图片&lt;br /&gt;3. 不带任何前缀：相对于 markdown 文件的路径&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;tags&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章标签。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;category&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章分类。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;lang&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章语言代码（如 &lt;code&gt;zh-CN&lt;/code&gt;）。仅当文章语言与站点默认语言不同时设置。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;licenseName&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章内容的许可证名称。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;licenseUrl&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章内容的许可证链接。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;author&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章作者。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;sourceLink&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章内容的来源链接或参考。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;draft&lt;/code&gt;&lt;/td&gt;&lt;td&gt;如果这篇文章仍是草稿，则不会显示。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;comment&lt;/code&gt;&lt;/td&gt;&lt;td&gt;是否启用此文章的评论功能。默认为 &lt;code&gt;true&lt;/code&gt;。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;slug&lt;/code&gt;&lt;/td&gt;&lt;td&gt;自定义文章 URL 路径。如果不设置，将使用文件名作为 URL。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;password&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章密码。设置后文章内容将被 AES-256-GCM 加密，访客需输入密码才能查看。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;passwordHint&lt;/code&gt;&lt;/td&gt;&lt;td&gt;密码提示。显示在密码输入框上方，帮助访客回忆密码，也可以不加。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;文章文件的放置位置&lt;a href=&quot;#文章文件的放置位置&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;您的文章文件应放置在 &lt;code&gt;src/content/posts/&lt;/code&gt; 目录中。您也可以创建子目录来更好地组织您的文章和资源。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/content/posts/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;├── post-1.md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└── post-2/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── cover.png&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── index.md&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;自定义文章 URL (Slug)&lt;a href=&quot;#自定义文章-url-slug&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;什么是 Slug？&lt;a href=&quot;#什么是-slug&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Slug 是文章 URL 路径的自定义部分。如果不设置 slug，系统将使用文件名作为 URL。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;Slug 使用示例&lt;a href=&quot;#slug-使用示例&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;示例 1：使用文件名作为 URL&lt;a href=&quot;#示例-1使用文件名作为-url&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;我的第一篇博客文章&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;published&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2023-09-09&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;文件：&lt;code&gt;src/content/posts/my-first-blog-post.md&lt;/code&gt;&lt;/p&gt;&lt;p&gt;URL：&lt;code&gt;/posts/my-first-blog-post&lt;/code&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;示例 2：自定义 Slug&lt;a href=&quot;#示例-2自定义-slug&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;我的第一篇博客文章&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;published&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2023-09-09&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;slug&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;hello-world&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;文件：&lt;code&gt;src/content/posts/my-first-blog-post.md&lt;/code&gt;&lt;/p&gt;&lt;p&gt;URL：&lt;code&gt;/posts/hello-world&lt;/code&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;示例 3：其他语言文件名使用Slug&lt;a href=&quot;#示例-3其他语言文件名使用slug&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;如何使用 Firefly 博客主题&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;published&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2023-09-09&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;slug&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;how-to-use-firefly-blog-theme&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;文件：&lt;code&gt;src/content/posts/如何使用Firefly博客主题.md&lt;/code&gt;&lt;/p&gt;&lt;p&gt;URL：&lt;code&gt;/posts/how-to-use-firefly-blog-theme&lt;/code&gt;&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;Slug 使用建议&lt;a href=&quot;#slug-使用建议&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;使用英文和连字符&lt;/strong&gt;：&lt;code&gt;my-awesome-post&lt;/code&gt; 而不是 &lt;code&gt;my awesome post&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保持简洁&lt;/strong&gt;：避免过长的 slug&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;具有描述性&lt;/strong&gt;：让 URL 能够反映文章内容&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免特殊字符&lt;/strong&gt;：只使用字母、数字和连字符&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保持一致性&lt;/strong&gt;：在整个博客中使用相似的命名模式&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;注意事项&lt;a href=&quot;#注意事项&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Slug 一旦设置并发布，建议不要随意更改，以免影响 SEO 和已存在的链接&lt;/li&gt;
&lt;li&gt;如果多个文章使用相同的 slug，后面的文章会覆盖前面的&lt;/li&gt;
&lt;li&gt;Slug 会自动转换为小写&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>Spring Cloud GateWay</title><link>https://www.liuguang.top/posts/microservices/article-20260501-spring-cloud-gateway/</link><guid isPermaLink="true">https://www.liuguang.top/posts/microservices/article-20260501-spring-cloud-gateway/</guid><description>网关比较基础的概念。</description><pubDate>Sat, 02 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h5&gt;[1.Spring Cloud Gateway 的核心组件及作用？](#####Spring Cloud Gateway 核心组件及作用)&lt;a href=&quot;#1spring-cloud-gateway-的核心组件及作用spring-cloud-gateway-核心组件及作用&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;section&gt;&lt;h5&gt;[2.Gateway 如何实现限流？Redis 限流的原理是什么？](#####Spring Cloud Gateway 实现限流的方式)&lt;a href=&quot;#2gateway-如何实现限流redis-限流的原理是什么spring-cloud-gateway-实现限流的方式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;section&gt;&lt;h5&gt;[3.Gateway 的 Filter 分为哪两类？执行顺序如何控制？](#####Gateway 的 Filter 核心分类)&lt;a href=&quot;#3gateway-的-filter-分为哪两类执行顺序如何控制gateway-的-filter-核心分类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;section&gt;&lt;h5&gt;[4.什么是 Predicate？常用的 Predicate 有哪些？](#####什么是 Gateway 的 Predicate？)&lt;a href=&quot;#4什么是-predicate常用的-predicate-有哪些什么是-gateway-的-predicate&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;section&gt;&lt;h5&gt;&lt;a href=&quot;#####%E5%8A%A8%E6%80%81%E8%B7%AF%E7%94%B1%E7%9A%84%E6%A0%B8%E5%BF%83%E5%8E%9F%E7%90%86&quot;&gt;5.Gateway 如何实现动态路由？&lt;/a&gt;&lt;a href=&quot;#5gateway-如何实现动态路由&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;section&gt;&lt;h5&gt;&lt;a&gt;6.Gateway 的负载均衡原理？如何自定义负载均衡策略？&lt;/a&gt;&lt;a href=&quot;#6gateway-的负载均衡原理如何自定义负载均衡策略&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;section&gt;&lt;h5&gt;&lt;a&gt;7.Gateway 如何实现权限校验&lt;/a&gt;&lt;a href=&quot;#7gateway-如何实现权限校验&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;section&gt;&lt;h5&gt;[8.Sentinel 和 Redis 限流在 Gateway 中的区别](#####先理清两种限流在 Gateway 中的基础实现)&lt;a href=&quot;#8sentinel-和-redis-限流在-gateway-中的区别先理清两种限流在-gateway-中的基础实现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;section&gt;&lt;h5&gt;[9.Gateway 如何避免服务雪崩？（熔断降级 + 限流）](#####Gateway 如何避免服务雪崩？（熔断降级 + 限流）)&lt;a href=&quot;#9gateway-如何避免服务雪崩熔断降级--限流gateway-如何避免服务雪崩熔断降级--限流&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;section&gt;&lt;h5&gt;Gateway 如何避免服务雪崩？（熔断降级 + 限流）&lt;a href=&quot;#gateway-如何避免服务雪崩熔断降级--限流&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;Spring Cloud Gateway 如何通过&lt;strong&gt;熔断降级 + 限流&lt;/strong&gt;组合手段避免服务雪崩，这是网关作为微服务流量入口的核心防护能力 —— 限流从 “入口” 控制总流量，避免后端服务被压垮；熔断降级则在后端服务故障时 “及时切断” 请求，防止故障扩散引发雪崩。我会先讲服务雪崩的成因，再给出基于 Sentinel（一体化支持限流 + 熔断）的完整实现方案，代码可直接落地。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;一、先理解：服务雪崩的成因&lt;a href=&quot;#一先理解服务雪崩的成因&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;当后端某个服务出现故障（如响应超时、异常率飙升、资源耗尽），Gateway 若仍持续向该服务转发请求，会导致：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;网关线程池被大量阻塞请求占满，无法处理其他正常服务的请求；&lt;/li&gt;
&lt;li&gt;故障服务的请求积压，进一步加剧服务不可用；&lt;/li&gt;
&lt;li&gt;故障扩散到上下游服务，最终引发整个微服务集群雪崩。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;Gateway 防护核心逻辑&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;限流&lt;/strong&gt;：前置防护，控制进入系统的总流量（如单接口 QPS、单 IP 请求数），避免流量超过后端服务承载能力；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;熔断降级&lt;/strong&gt;：故障隔离，当后端服务异常率 / 超时率达到阈值时，暂时 “熔断” 该服务，直接返回降级响应，待服务恢复后再恢复转发，防止故障扩散。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、完整实现方案：基于 Sentinel 实现限流 + 熔断降级&lt;a href=&quot;#二完整实现方案基于-sentinel-实现限流--熔断降级&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Sentinel 是阿里开源的流量治理组件，一站式支持 Gateway 的&lt;strong&gt;限流、熔断、降级&lt;/strong&gt;，是防雪崩的最优选择（无需整合多个组件），具体步骤如下：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;步骤 1：引入核心依赖&lt;a href=&quot;#步骤-1引入核心依赖&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- Gateway 核心依赖 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;org.springframework.cloud&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;spring-cloud-starter-gateway&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- Sentinel 适配 Gateway（核心） --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;com.alibaba.cloud&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;spring-cloud-alibaba-sentinel-gateway&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;version&amp;gt;2023.0.1.0&amp;lt;/version&amp;gt; &amp;lt;!-- 适配Spring Cloud 2023版本 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- Sentinel 核心依赖 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;com.alibaba.csp&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;sentinel-core&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- Sentinel 控制台依赖（可视化配置规则） --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;com.alibaba.csp&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;sentinel-transport-simple-http&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- 可选：Nacos 持久化规则（避免重启丢失） --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;com.alibaba.csp&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;sentinel-datasource-nacos&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 2：配置 Sentinel 控制台与基础参数&lt;a href=&quot;#步骤-2配置-sentinel-控制台与基础参数&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;在 &lt;code&gt;application.yml&lt;/code&gt; 中配置 Sentinel 控制台连接、Gateway 适配参数：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;spring:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cloud:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# Gateway 配置（示例路由）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gateway:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;routes:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- id: user-service-route&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;uri: lb://user-service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;predicates: [Path=/user/**]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- id: order-service-route&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;uri: lb://order-service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;predicates: [Path=/order/**]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# Sentinel 配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;alibaba:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sentinel:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# Sentinel 控制台地址&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transport:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;dashboard: localhost:8080  # 控制台端口（需先启动Sentinel控制台）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;port: 8719                  # 与控制台通信的端口&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# Gateway 适配配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gateway:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enabled: true               # 开启Sentinel对Gateway的支持&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scg:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fallback:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mode: response          # 降级响应模式（返回自定义JSON）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;response-body: &quot;{\&quot;code\&quot;:503,\&quot;msg\&quot;:\&quot;服务繁忙，请稍后再试\&quot;,\&quot;success\&quot;:false}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;response-status: 503    # 降级响应状态码&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;content-type: application/json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 可选：Nacos 持久化规则（避免重启网关丢失规则）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;datasource:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ds1:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;nacos:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server-addr: localhost:8848&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data-id: gateway-sentinel-rules&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;group-id: DEFAULT_GROUP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rule-type: gw_flow       # 网关限流规则&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ds2:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;nacos:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server-addr: localhost:8848&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data-id: gateway-sentinel-degrade-rules&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;group-id: DEFAULT_GROUP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rule-type: gw_degrade    # 网关熔断降级规则&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 3：启动 Sentinel 控制台&lt;a href=&quot;#步骤-3启动-sentinel-控制台&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;下载 Sentinel 控制台 jar 包：&lt;a href=&quot;https://github.com/alibaba/Sentinel/releases&quot; target=&quot;_blank&quot;&gt;Sentinel 官方下载&lt;/a&gt;；&lt;/li&gt;
&lt;li&gt;启动控制台：&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;java -jar sentinel-dashboard-1.8.7.jar --server.port=8080&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;访问控制台：&lt;code&gt;http://localhost:8080&lt;/code&gt;（默认账号 / 密码：sentinel/sentinel）。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 4：配置限流规则（前置防护）&lt;a href=&quot;#步骤-4配置限流规则前置防护&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;通过 Sentinel 控制台配置网关限流规则，控制单接口 / 单 IP 的请求量：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;进入控制台 → 网关流控 → 新增网关流控规则：&lt;/p&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;配置项&lt;/th&gt;&lt;th&gt;示例值&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;资源名&lt;/td&gt;&lt;td&gt;user-service-route&lt;/td&gt;&lt;td&gt;对应 Gateway 的路由 ID（或接口路径：/user/**）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;限流类型&lt;/td&gt;&lt;td&gt;QPS&lt;/td&gt;&lt;td&gt;按每秒请求数限流（也可选 “并发数”）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;阈值&lt;/td&gt;&lt;td&gt;100&lt;/td&gt;&lt;td&gt;每秒最多允许 100 个请求&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;流控模式&lt;/td&gt;&lt;td&gt;直接&lt;/td&gt;&lt;td&gt;直接限流当前资源&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;流控效果&lt;/td&gt;&lt;td&gt;快速失败&lt;/td&gt;&lt;td&gt;超出阈值直接返回降级响应（也可选 “Warm Up” 预热、“匀速排队”）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;限流粒度&lt;/td&gt;&lt;td&gt;IP&lt;/td&gt;&lt;td&gt;按客户端 IP 限流（也可选手动选择、参数等）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 5：配置熔断降级规则（故障隔离）&lt;a href=&quot;#步骤-5配置熔断降级规则故障隔离&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;当后端服务异常率 / 超时率达到阈值时，自动熔断该服务，避免持续转发请求：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;进入控制台 → 网关熔断 → 新增网关熔断规则：&lt;/p&gt;













































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;配置项&lt;/th&gt;&lt;th&gt;示例值&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;资源名&lt;/td&gt;&lt;td&gt;user-service-route&lt;/td&gt;&lt;td&gt;对应 Gateway 的路由 ID&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;熔断策略&lt;/td&gt;&lt;td&gt;异常比例&lt;/td&gt;&lt;td&gt;按异常请求比例熔断（也可选 “异常数”“慢调用比例”）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;阈值&lt;/td&gt;&lt;td&gt;0.5&lt;/td&gt;&lt;td&gt;异常比例达到 50% 时触发熔断&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;熔断时长&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;熔断后 10 秒内不再转发请求到该服务&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;最小请求数&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;触发熔断的最小请求数（避免少量请求误触发）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;统计时长&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;统计异常比例的时间窗口（10 秒）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;慢调用阈值&lt;/td&gt;&lt;td&gt;500&lt;/td&gt;&lt;td&gt;（慢调用比例策略用）响应时间超过 500ms 视为慢调用&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 6：自定义降级响应（可选）&lt;a href=&quot;#步骤-6自定义降级响应可选&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;默认降级响应可满足基本需求，若需更灵活的自定义响应，可实现 &lt;code&gt;BlockRequestHandler&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import com.alibaba.csp.sentinel.slots.block.BlockException;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.core.io.buffer.DataBuffer;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.http.HttpStatus;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.http.MediaType;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.http.server.reactive.ServerHttpResponse;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.stereotype.Component;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.web.reactive.function.server.ServerResponse;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.web.server.ServerWebExchange;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import reactor.core.publisher.Mono;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import java.nio.charset.StandardCharsets;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 自定义Sentinel降级响应处理器&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Component&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class CustomSentinelBlockHandler extends SentinelGatewayBlockExceptionHandler {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public Mono&amp;lt;Void&amp;gt; handle(ServerWebExchange exchange, Throwable ex) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ServerHttpResponse response = exchange.getResponse();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;response.setStatusCode(HttpStatus.SERVICE_UNAVAILABLE);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;response.getHeaders().setContentType(MediaType.APPLICATION_JSON);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String msg;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if (ex instanceof BlockException) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 限流触发的降级&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;msg = &quot;{\&quot;code\&quot;:429,\&quot;msg\&quot;:\&quot;请求过于频繁，请稍后再试\&quot;,\&quot;success\&quot;:false}&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} else {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 熔断触发的降级&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;msg = &quot;{\&quot;code\&quot;:503,\&quot;msg\&quot;:\&quot;服务暂不可用，请稍后再试\&quot;,\&quot;success\&quot;:false}&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DataBuffer buffer = response.bufferFactory().wrap(msg.getBytes(StandardCharsets.UTF_8));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return response.writeWith(Mono.just(buffer));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;三、备选方案：Resilience4j 实现熔断降级（不依赖 Sentinel）&lt;a href=&quot;#三备选方案resilience4j-实现熔断降级不依赖-sentinel&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;若不想引入 Sentinel，可使用 Resilience4j（轻量熔断组件）结合 Redis 限流实现防护：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;1. 引入 Resilience4j 依赖&lt;a href=&quot;#1-引入-resilience4j-依赖&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- Resilience4j 熔断依赖 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;io.github.resilience4j&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;resilience4j-spring-cloud-gateway&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;io.github.resilience4j&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;resilience4j-circuitbreaker&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 配置熔断规则（application.yml）&lt;a href=&quot;#2-配置熔断规则applicationyml&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;resilience4j:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;circuitbreaker:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;instances:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;user-service:  # 对应路由ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;failureRateThreshold: 50  # 失败率阈值50%&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;waitDurationInOpenState: 10000  # 熔断时长10秒&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;minimumNumberOfCalls: 5  # 最小触发请求数&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;slidingWindowSize: 10    # 统计窗口大小&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;timelimiter:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;instances:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;user-service:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;timeoutDuration: 1000    # 请求超时时间1秒&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;spring:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cloud:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gateway:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;routes:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- id: user-service-route&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;uri: lb://user-service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;predicates: [Path=/user/**]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;filters:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 整合Resilience4j熔断&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- name: CircuitBreaker&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;args:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: user-service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fallbackUri: forward:/fallback/user  # 降级转发到本地接口&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 实现降级接口&lt;a href=&quot;#3-实现降级接口&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.web.bind.annotation.RequestMapping;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.web.bind.annotation.RestController;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import reactor.core.publisher.Mono;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@RestController&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class FallbackController {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@RequestMapping(&quot;/fallback/user&quot;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public Mono&amp;lt;String&amp;gt; userFallback() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return Mono.just(&quot;{\&quot;code\&quot;:503,\&quot;msg\&quot;:\&quot;用户服务暂不可用\&quot;,\&quot;success\&quot;:false}&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;四、关键优化点（避免雪崩的核心细节）&lt;a href=&quot;#四关键优化点避免雪崩的核心细节&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;设置合理的超时时间&lt;/strong&gt;：Gateway 转发请求时设置超时（如 1 秒），避免线程长时间阻塞：&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;spring:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cloud:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gateway:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;httpclient:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;connect-timeout: 500  # 连接超时500ms&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;response-timeout: 1000 # 响应超时1秒&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;降级策略差异化&lt;/strong&gt;：核心接口（如支付）返回 “排队提示”，非核心接口（如商品列表）返回 “缓存数据”，提升用户体验；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;监控告警&lt;/strong&gt;：通过 Sentinel 控制台 / Prometheus 监控限流 / 熔断指标，异常时及时告警（如熔断触发、QPS 突增）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;限流粒度精细化&lt;/strong&gt;：对热点接口（如秒杀）按 “用户 ID + 商品 ID” 限流，避免单一用户刷爆接口；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;预热限流&lt;/strong&gt;：对新上线服务使用 “Warm Up” 流控效果，避免刚启动就承受满流量。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;总结&lt;a href=&quot;#总结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Gateway 避免服务雪崩的核心是&lt;/p&gt;
&lt;p&gt;限流（前置控流量）+ 熔断降级（故障隔离）&lt;/p&gt;
&lt;p&gt;双管齐下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;限流：控制进入系统的总流量，避免后端服务过载；&lt;/li&gt;
&lt;li&gt;熔断降级：后端服务故障时切断请求，防止故障扩散；&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;优先选择 &lt;strong&gt;Sentinel&lt;/strong&gt; 实现一体化防护（限流 + 熔断 + 监控），轻量场景可选用 Resilience4j + Redis 限流；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;关键细节：合理设置超时时间、降级策略、监控告警，确保防护规则适配业务场景。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;通过这套方案，能有效隔离故障服务、控制流量峰值，从入口层杜绝服务雪崩的发生。&lt;/p&gt;&lt;section&gt;&lt;h5&gt;先理清两种限流在 Gateway 中的基础实现&lt;a href=&quot;#先理清两种限流在-gateway-中的基础实现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;在对比前，先简单回顾两种方案的核心实现方式，帮你建立基础认知：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Redis 限流&lt;/strong&gt;：基于 Gateway 官方的 &lt;code&gt;RequestRateLimiterGatewayFilterFactory&lt;/code&gt;，核心是&lt;strong&gt;令牌桶算法&lt;/strong&gt; + Redis Lua 脚本（保证原子性），依赖 Redis 存储令牌状态，功能聚焦 “纯限流”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sentinel 限流&lt;/strong&gt;：基于 Sentinel 对 Gateway 的适配组件（&lt;code&gt;spring-cloud-alibaba-sentinel-gateway&lt;/code&gt;），核心是 Sentinel 核心库的&lt;strong&gt;多限流算法&lt;/strong&gt;（令牌桶 / 漏桶 / 预热等），支持限流、熔断、降级一体化，可本地嵌入式运行或结合 Sentinel 控制台管理。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、Sentinel vs Redis 限流（Gateway 中）核心区别&lt;a href=&quot;#二sentinel-vs-redis-限流gateway-中核心区别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;以下是最关键的维度对比，清晰展示两者的差异：&lt;/p&gt;



























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;对比维度&lt;/th&gt;&lt;th&gt;Redis 限流（Gateway 官方）&lt;/th&gt;&lt;th&gt;Sentinel 限流（Alibaba）&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;核心定位&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;单一功能：仅实现限流（令牌桶）&lt;/td&gt;&lt;td&gt;全栈流量治理：限流 + 熔断 + 降级 + 热点限流 + 系统保护&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;底层算法&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;仅支持&lt;strong&gt;令牌桶算法&lt;/strong&gt;（固定速率 + 突发容量）&lt;/td&gt;&lt;td&gt;支持多种算法：令牌桶、漏桶、预热限流、匀速排队、热点参数限流等&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;限流维度&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;基础维度：IP、用户 ID、接口路径（需自定义 KeyResolver）&lt;/td&gt;&lt;td&gt;丰富维度：路径、请求参数、Header、来源 IP、热点参数（如商品 ID）、系统维度（QPS/CPU）等&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;依赖组件&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;必须依赖 Redis 服务（网络调用）&lt;/td&gt;&lt;td&gt;无强制外部依赖：规则可本地存储；可选 Sentinel 控制台（可视化）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;监控与可视化&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;无原生监控，需自行开发（如 Redis 监控令牌数、埋点统计）&lt;/td&gt;&lt;td&gt;自带 Sentinel 控制台：实时查看限流指标、修改规则、查看熔断状态、链路追踪&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;动态规则&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;需修改配置中心（Nacos/Redis）+ 触发路由刷新&lt;/td&gt;&lt;td&gt;控制台实时修改规则，无需重启网关；支持规则持久化到 Nacos/Apollo&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;熔断降级&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;无原生支持，需自行结合 Resilience4j 等实现&lt;/td&gt;&lt;td&gt;原生支持：基于异常比例 / 异常数 / 慢调用比例熔断后端服务&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;性能&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;有网络开销（调用 Redis），高并发下略逊&lt;/td&gt;&lt;td&gt;本地限流（无网络调用），性能更高；规则拉取仅首次 / 变更时触发&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;规则粒度&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;路由级 / 全局级（粗粒度）&lt;/td&gt;&lt;td&gt;接口级 / 参数级（细粒度，如对 /order/{id} 中 id=1001 的请求单独限流）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;异常处理&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;需自定义返回结果（429）&lt;/td&gt;&lt;td&gt;自带降级返回策略，支持自定义限流 / 熔断响应（JSON / 跳转）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;三、关键区别拆解（通俗易懂版）&lt;a href=&quot;#三关键区别拆解通俗易懂版&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;1. 核心定位：“单一工具” vs “全栈平台”&lt;a href=&quot;#1-核心定位单一工具-vs-全栈平台&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Redis 限流&lt;/strong&gt;：就像 “一把专门的限流锁”，只解决 “控制请求数量” 的问题，功能单一，没有其他附加能力。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sentinel&lt;/strong&gt;：就像 “流量治理管家”，除了限流，还能监控请求、熔断故障服务（避免雪崩）、降级非核心接口、限制热点参数请求，是一站式解决方案。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 性能：“远程调用” vs “本地计算”&lt;a href=&quot;#2-性能远程调用-vs-本地计算&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Redis 限流&lt;/strong&gt;：每次请求都要调用 Redis 执行 Lua 脚本（网络 IO），高并发下会有一定的网络开销；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sentinel 限流&lt;/strong&gt;：限流规则加载到本地内存，每次请求直接本地计算是否限流（无网络调用），性能更高，仅在规则变更时从控制台 / 配置中心拉取新规则。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 功能丰富度：“基础限流” vs “精细化治理”&lt;a href=&quot;#3-功能丰富度基础限流-vs-精细化治理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;举两个典型场景：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;场景 1：热点参数限流&lt;/p&gt;
&lt;p&gt;想限制 “商品详情接口（/goods/{id}）中，id=1001 的爆款商品请求不超过 100 QPS”——Redis 限流做不到（只能按路径 / IP 限流），Sentinel 可通过 “热点参数限流” 轻松实现。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;场景 2：服务熔断&lt;/p&gt;
&lt;p&gt;后端用户服务故障（异常率 &amp;gt; 50%），想暂时熔断该服务，返回兜底响应 ——Redis 限流无此能力，Sentinel 可基于异常比例自动熔断，避免网关持续转发请求到故障服务。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;4. 运维成本：“需自研监控” vs “可视化控制台”&lt;a href=&quot;#4-运维成本需自研监控-vs-可视化控制台&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Redis 限流&lt;/strong&gt;：限流效果、令牌剩余数、触发次数等指标，需要你自己写监控脚本（如从 Redis 读取 key 统计）、 Grafana 绘图；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sentinel 控制台&lt;/strong&gt;：开箱即用，能看到每个接口的 QPS、限流次数、熔断状态，还能实时修改限流规则（比如临时把 QPS 从 100 调整为 200），无需重启网关。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;四、选型建议（帮你快速决策）&lt;a href=&quot;#四选型建议帮你快速决策&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;选择 Redis 限流&lt;/th&gt;&lt;th&gt;选择 Sentinel 限流&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;场景简单：仅需基础限流（IP / 接口）&lt;/td&gt;&lt;td&gt;场景复杂：需精细化限流 + 熔断 + 降级 + 监控&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;已有 Redis 集群，不想引入新组件&lt;/td&gt;&lt;td&gt;追求低性能开销、可视化运维、全栈流量治理&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;网关流量适中，对网络开销不敏感&lt;/td&gt;&lt;td&gt;高并发场景（如秒杀），要求本地限流、低延迟&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;仅需 “限流” 单一功能，无熔断需求&lt;/td&gt;&lt;td&gt;微服务架构，需保护后端服务（熔断降级）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;总结&lt;a href=&quot;#总结-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Redis 限流是&lt;strong&gt;轻量、单一功能&lt;/strong&gt;的方案，依赖 Redis，适合简单限流场景，已有 Redis 集群时接入成本低；&lt;/li&gt;
&lt;li&gt;Sentinel 是&lt;strong&gt;功能全面、性能更优&lt;/strong&gt;的流量治理方案，无强制外部依赖，支持限流、熔断、监控一体化，适合复杂微服务场景；&lt;/li&gt;
&lt;li&gt;核心决策点：是否需要熔断 / 降级 / 精细化限流 / 可视化监控 —— 需要则选 Sentinel，仅基础限流则选 Redis。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;Gateway 权限校验的核心原理&lt;a href=&quot;#gateway-权限校验的核心原理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Gateway 本身不提供内置的权限校验功能，但可通过 &lt;strong&gt;GlobalFilter（全局过滤器）&lt;/strong&gt; 实现全量请求拦截，在请求转发到后端服务的 &lt;code&gt;pre&lt;/code&gt; 阶段完成权限校验，核心流程：&lt;/p&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;无效&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;有效&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;无&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;有&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;客户端发送请求到Gateway&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;GlobalFilter拦截请求&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是否在白名单？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;直接放行请求&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;提取身份凭证（Token/JWT）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;校验凭证有效性？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回401 Unauthorized&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;解析用户信息，校验接口访问权限&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是否有权限？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回403 Forbidden&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;放行请求到后端服务&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&amp;lt;/svg&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;graph TD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A[客户端发送请求到Gateway] --&amp;gt; B[GlobalFilter拦截请求]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;B --&amp;gt; C{是否在白名单？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C -- 是 --&amp;gt; D[直接放行请求]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C -- 否 --&amp;gt; E[提取身份凭证（Token/JWT）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E --&amp;gt; F{校验凭证有效性？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;F -- 无效 --&amp;gt; G[返回401 Unauthorized]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;F -- 有效 --&amp;gt; H[解析用户信息，校验接口访问权限]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;H --&amp;gt; I{是否有权限？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;I -- 无 --&amp;gt; J[返回403 Forbidden]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;I -- 有 --&amp;gt; K[放行请求到后端服务]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;无效&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;有效&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;无&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;有&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;客户端发送请求到Gateway&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;GlobalFilter拦截请求&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是否在白名单？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;直接放行请求&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;提取身份凭证（Token/JWT）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;校验凭证有效性？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回401 Unauthorized&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;解析用户信息，校验接口访问权限&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是否有权限？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回403 Forbidden&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;放行请求到后端服务&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、实操实现：权限校验完整方案&lt;a href=&quot;#二实操实现权限校验完整方案&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;以下是最常用的 &lt;strong&gt;JWT Token 身份校验 + 接口权限校验&lt;/strong&gt; 实现步骤，适配绝大多数业务场景：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;步骤 1：引入核心依赖&lt;a href=&quot;#步骤-1引入核心依赖-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- Gateway 核心依赖 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;org.springframework.cloud&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;spring-cloud-starter-gateway&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- JWT 工具依赖（用于Token生成/解析） --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;io.jsonwebtoken&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;jjwt-api&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;version&amp;gt;0.11.5&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;io.jsonwebtoken&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;jjwt-impl&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;version&amp;gt;0.11.5&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;io.jsonwebtoken&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;jjwt-jackson&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;version&amp;gt;0.11.5&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- 可选：Redis依赖（存储黑名单Token/用户权限） --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;spring-boot-starter-data-redis-reactive&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 2：编写 JWT 工具类（Token 生成 / 校验）&lt;a href=&quot;#步骤-2编写-jwt-工具类token-生成--校验&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import io.jsonwebtoken.*;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import io.jsonwebtoken.security.Keys;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.beans.factory.annotation.Value;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.stereotype.Component;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import javax.crypto.SecretKey;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import java.util.Date;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import java.util.Map;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* JWT工具类：生成、解析、验证Token&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Component&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class JwtUtil {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// JWT密钥（建议配置在Nacos/配置文件，长度至少256位）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Value(&quot;${jwt.secret:abcdefghijklmnopqrstuvwxyz1234567890abcdef}&quot;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private String secret;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// Token过期时间（单位：毫秒，示例：2小时）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Value(&quot;${jwt.expire:7200000}&quot;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private long expire;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 生成Token&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public String generateToken(String userId, Map&amp;lt;String, Object&amp;gt; claims) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return Jwts.builder()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.setClaims(claims) // 自定义载荷（如角色、权限）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.setSubject(userId) // 主题（用户ID）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.setIssuedAt(new Date()) // 签发时间&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.setExpiration(new Date(System.currentTimeMillis() + expire)) // 过期时间&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.signWith(key, SignatureAlgorithm.HS256) // 签名算法&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.compact();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 解析Token，获取载荷信息&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public Claims parseToken(String token) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return Jwts.parserBuilder()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.setSigningKey(key)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.build()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.parseClaimsJws(token)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.getBody();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 验证Token是否有效（过期/签名错误）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public boolean validateToken(String token) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;try {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;47&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;parseToken(token);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;48&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return true;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;49&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} catch (ExpiredJwtException e) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;50&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// Token过期&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;51&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return false;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;52&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} catch (MalformedJwtException | SignatureException | IllegalArgumentException e) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;53&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 签名错误/Token格式错误/空Token&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;54&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return false;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;55&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;56&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;57&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 3：配置白名单（无需校验的接口）&lt;a href=&quot;#步骤-3配置白名单无需校验的接口&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.context.annotation.Configuration;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import java.util.HashSet;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import java.util.Set;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 权限白名单配置：登录、注册、健康检查等接口无需校验&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Configuration&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class AuthWhiteListConfig {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 白名单路径（支持通配符**）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public static final Set&amp;lt;String&amp;gt; WHITE_LIST = new HashSet&amp;lt;&amp;gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;static {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 登录/注册接口&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;WHITE_LIST.add(&quot;/auth/login&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;WHITE_LIST.add(&quot;/auth/register&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 健康检查接口&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;WHITE_LIST.add(&quot;/actuator/**&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 公开接口&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;WHITE_LIST.add(&quot;/public/**&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 4：自定义权限校验 GlobalFilter（核心）&lt;a href=&quot;#步骤-4自定义权限校验-globalfilter核心&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.gateway.filter.GlobalFilter;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.gateway.filter.GatewayFilterChain;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.core.Ordered;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.http.HttpStatus;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.http.MediaType;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.http.server.reactive.ServerHttpRequest;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.http.server.reactive.ServerHttpResponse;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.stereotype.Component;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.util.AntPathMatcher;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.web.server.ServerWebExchange;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import reactor.core.publisher.Mono;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import javax.annotation.Resource;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import java.nio.charset.StandardCharsets;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import java.util.List;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import java.util.Map;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 全局权限校验过滤器：Token校验 + 接口权限校验&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Component&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class AuthGlobalFilter implements GlobalFilter, Ordered {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Resource&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private JwtUtil jwtUtil;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 路径匹配器（支持通配符）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private final AntPathMatcher pathMatcher = new AntPathMatcher();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public Mono&amp;lt;Void&amp;gt; filter(ServerWebExchange exchange, GatewayFilterChain chain) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ServerHttpRequest request = exchange.getRequest();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String path = request.getPath().toString();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 1. 判断是否在白名单，白名单接口直接放行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if (isWhiteList(path)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return chain.filter(exchange);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 2. 提取Token（从请求头Authorization中获取，格式：Bearer xxx）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&amp;lt;String&amp;gt; authHeaders = request.getHeaders().get(&quot;Authorization&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if (authHeaders == null || authHeaders.isEmpty()) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 无Token，返回401&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return responseError(exchange, HttpStatus.UNAUTHORIZED, &quot;未登录，请先登录&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String token = authHeaders.get(0).replace(&quot;Bearer &quot;, &quot;&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 3. 校验Token有效性&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;47&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if (!jwtUtil.validateToken(token)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;48&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return responseError(exchange, HttpStatus.UNAUTHORIZED, &quot;Token无效或已过期&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;49&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;50&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;51&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 4. 解析Token，获取用户信息和权限&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;52&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Map&amp;lt;String, Object&amp;gt; claims = jwtUtil.parseToken(token);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;53&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String userId = claims.get(&quot;sub&quot;).toString(); // 用户ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;54&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&amp;lt;String&amp;gt; userPermissions = (List&amp;lt;String&amp;gt;) claims.get(&quot;permissions&quot;); // 用户权限列表&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;55&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;56&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 5. 校验接口访问权限（示例：路径=/user/add 对应权限码user:add）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;57&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String requiredPermission = getPermissionByPath(path);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;58&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if (requiredPermission != null &amp;amp;&amp;amp; !userPermissions.contains(requiredPermission)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;59&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 无接口权限，返回403&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;60&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return responseError(exchange, HttpStatus.FORBIDDEN, &quot;无接口访问权限&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;61&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;62&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;63&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 6. 将用户信息放入请求头，供后端服务使用&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;64&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ServerHttpRequest newRequest = request.mutate()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;65&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.header(&quot;X-User-Id&quot;, userId)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;66&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.header(&quot;X-User-Permissions&quot;, String.join(&quot;,&quot;, userPermissions))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;67&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.build();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;68&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;69&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;70&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 7. 权限校验通过，放行请求&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;71&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return chain.filter(newExchange);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;72&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;73&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;74&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 判断路径是否在白名单&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;75&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private boolean isWhiteList(String path) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;76&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return AuthWhiteListConfig.WHITE_LIST.stream()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;77&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.anyMatch(whitePath -&amp;gt; pathMatcher.match(whitePath, path));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;78&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;79&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;80&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 自定义：路径转换为权限码（根据业务规则调整）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;81&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private String getPermissionByPath(String path) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;82&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 示例规则：/user/add → user:add；/order/list → order:list&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;83&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if (pathMatcher.match(&quot;/user/add&quot;, path)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;84&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &quot;user:add&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;85&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} else if (pathMatcher.match(&quot;/user/delete/**&quot;, path)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;86&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &quot;user:delete&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;87&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} else if (pathMatcher.match(&quot;/order/list&quot;, path)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;88&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &quot;order:list&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;89&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;90&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 无需权限的接口返回null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;91&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return null;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;92&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;93&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;94&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 统一返回权限错误响应（JSON格式）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;95&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private Mono&amp;lt;Void&amp;gt; responseError(ServerWebExchange exchange, HttpStatus status, String msg) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;96&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ServerHttpResponse response = exchange.getResponse();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;97&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;response.setStatusCode(status);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;98&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;response.getHeaders().setContentType(MediaType.APPLICATION_JSON);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;99&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;100&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String errorJson = String.format(&quot;{\&quot;code\&quot;:%d,\&quot;msg\&quot;:\&quot;%s\&quot;,\&quot;success\&quot;:false}&quot;, status.value(), msg);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;101&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return response.writeWith(Mono.just(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;102&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;response.bufferFactory().wrap(errorJson.getBytes(StandardCharsets.UTF_8))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;103&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;104&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;105&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;106&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 设置过滤器优先级（数值越小越先执行，建议比限流过滤器高）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;107&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;108&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public int getOrder() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;109&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return -100;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;110&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;111&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 5：配置 JWT 相关参数（application.yml）&lt;a href=&quot;#步骤-5配置-jwt-相关参数applicationyml&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;yaml&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# JWT配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;jwt:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;secret: abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz123456&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;expire: 7200000 # Token有效期2小时&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# Gateway路由配置（示例）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;spring:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cloud:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gateway:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;routes:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- id: user-service-route&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;uri: lb://user-service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;predicates:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Path=/user/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- id: auth-service-route&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;uri: lb://auth-service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;predicates:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Path=/auth/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;三、进阶优化（可选）&lt;a href=&quot;#三进阶优化可选&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Token 黑名单&lt;/strong&gt;：若用户登出 / Token 被盗，需将 Token 加入 Redis 黑名单，在 &lt;code&gt;validateToken&lt;/code&gt; 中增加黑名单校验；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分布式权限缓存&lt;/strong&gt;：将用户权限列表缓存到 Redis，避免每次解析 Token（适合权限频繁变更的场景）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;细粒度角色校验&lt;/strong&gt;：在过滤器中增加角色校验（如 &lt;code&gt;admin&lt;/code&gt; 角色可访问所有接口）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;限流 + 权限联动&lt;/strong&gt;：权限校验通过后，再执行限流逻辑（调整过滤器 order 值，权限过滤器 order 更小）。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;总结&lt;a href=&quot;#总结-2&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Gateway 权限校验核心是&lt;strong&gt;GlobalFilter&lt;/strong&gt;，在 &lt;code&gt;pre&lt;/code&gt; 阶段拦截请求，优先校验白名单，再做 Token 身份认证和接口权限校验；&lt;/li&gt;
&lt;li&gt;基础校验需实现 JWT 工具类（Token 生成 / 解析）+ 白名单配置 + 全局过滤器；&lt;/li&gt;
&lt;li&gt;校验失败需返回标准化 JSON 响应（401 未登录 / 403 无权限），而非默认空响应，提升前端适配性。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;关键点：过滤器 &lt;code&gt;order&lt;/code&gt; 值需合理设置（建议 -100 左右），确保权限校验优先于限流、路由转发等过滤器执行。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;Gateway 负载均衡的核心原理&lt;a href=&quot;#gateway-负载均衡的核心原理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Gateway 本身不直接实现负载均衡逻辑，而是&lt;strong&gt;集成 Spring Cloud LoadBalancer（SCLB）&lt;/strong&gt;（Spring Cloud 官方替换 Ribbon 的负载均衡组件），结合服务发现（Nacos/Eureka 等）实现请求的负载分发，核心原理可拆解为以下步骤：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;1. 核心依赖（自动集成）&lt;a href=&quot;#1-核心依赖自动集成&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;只要引入 Gateway 和服务发现依赖（如 Nacos/Eureka），Spring Cloud 会自动引入 &lt;code&gt;spring-cloud-starter-loadbalancer&lt;/code&gt; 依赖，无需手动添加：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- Gateway 核心依赖 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;org.springframework.cloud&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;spring-cloud-starter-gateway&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- Nacos 服务发现（触发 LoadBalancer 自动装配） --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;com.alibaba.cloud&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;spring-cloud-starter-alibaba-nacos-discovery&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 负载均衡核心流程&lt;a href=&quot;#2-负载均衡核心流程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;客户端发送请求到Gateway&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Gateway识别uri前缀为lb://（如lb://user-service）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;通过服务发现组件（Nacos）获取user-service的所有实例列表（IP+端口）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Spring Cloud LoadBalancer根据策略（默认轮询）选择一个实例&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Gateway将uri替换为选中实例的真实地址（如&lt;a href=&quot;http://192.168.1.100:8080%EF%BC%89&quot; target=&quot;_blank&quot;&gt;http://192.168.1.100:8080）&lt;/a&gt;&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Gateway转发请求到选中的后端实例&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;后端实例返回响应，Gateway透传给客户端&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;graph TD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A[客户端发送请求到Gateway] --&amp;gt; B[Gateway识别uri前缀为lb://（如lb://user-service）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;B --&amp;gt; C[通过服务发现组件（Nacos）获取user-service的所有实例列表（IP+端口）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C --&amp;gt; D[Spring Cloud LoadBalancer根据策略（默认轮询）选择一个实例]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;D --&amp;gt; E[Gateway将uri替换为选中实例的真实地址（如http://192.168.1.100:8080）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E --&amp;gt; F[Gateway转发请求到选中的后端实例]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;F --&amp;gt; G[后端实例返回响应，Gateway透传给客户端]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;客户端发送请求到Gateway&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Gateway识别uri前缀为lb://（如lb://user-service）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;通过服务发现组件（Nacos）获取user-service的所有实例列表（IP+端口）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Spring Cloud LoadBalancer根据策略（默认轮询）选择一个实例&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Gateway将uri替换为选中实例的真实地址（如&lt;a href=&quot;http://192.168.1.100:8080%EF%BC%89&quot; target=&quot;_blank&quot;&gt;http://192.168.1.100:8080）&lt;/a&gt;&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Gateway转发请求到选中的后端实例&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;后端实例返回响应，Gateway透传给客户端&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 关键组件说明&lt;a href=&quot;#3-关键组件说明&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;lb://&lt;/code&gt; 前缀&lt;/strong&gt;：Gateway 识别该前缀时，会触发负载均衡逻辑（而非直接转发到固定地址）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ReactiveLoadBalancer&lt;/strong&gt;：SCLB 为响应式场景（Gateway 基于 WebFlux）提供的负载均衡器接口，默认实现是 &lt;code&gt;RoundRobinLoadBalancer&lt;/code&gt;（轮询策略）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ServiceInstanceListSupplier&lt;/strong&gt;：负责从服务发现组件获取目标服务的实例列表（如从 Nacos 拉取 &lt;code&gt;user-service&lt;/code&gt; 的所有实例）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LoadBalancerClientFilter&lt;/strong&gt;：Gateway 内置的全局过滤器（order=0），核心作用是将 &lt;code&gt;lb://&lt;/code&gt; 前缀的 uri 替换为选中实例的真实地址。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、自定义负载均衡策略（实操步骤）&lt;a href=&quot;#二自定义负载均衡策略实操步骤&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;SCLB 默认提供&lt;strong&gt;轮询（RoundRobin）&lt;/strong&gt; 和&lt;strong&gt;随机（Random）&lt;/strong&gt; 策略，实际场景中常需自定义（如权重、最少连接数、IP 哈希等），以下以&lt;strong&gt;权重策略&lt;/strong&gt;为例，讲解完整实现步骤：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;步骤 1：排除默认策略（可选，避免冲突）&lt;a href=&quot;#步骤-1排除默认策略可选避免冲突&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;若需全局替换默认策略，可在启动类排除默认配置：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.boot.SpringApplication;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.boot.autoconfigure.SpringBootApplication;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 排除默认的负载均衡自动配置（可选）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@SpringBootApplication(exclude = LoadBalancerAutoConfiguration.class)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 指定自定义策略的配置类（全局生效）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@LoadBalancerClients(defaultConfiguration = CustomLoadBalancerConfig.class)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class GatewayApplication {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public static void main(String[] args) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;SpringApplication.run(GatewayApplication.class, args);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 2：自定义负载均衡策略类（权重策略）&lt;a href=&quot;#步骤-2自定义负载均衡策略类权重策略&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;实现 &lt;code&gt;ReactorServiceInstanceLoadBalancer&lt;/code&gt; 接口，适配响应式场景：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.client.ServiceInstance;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.core.env.Environment;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import reactor.core.publisher.Mono;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import java.util.List;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import java.util.Random;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import java.util.stream.Collectors;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 自定义权重负载均衡策略&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 要求后端实例的metadata中配置weight（如：10、20、30）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class WeightedLoadBalancer implements ReactorServiceInstanceLoadBalancer {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 服务名称&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private final String serviceId;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 实例列表供应商&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private ServiceInstanceListSupplier serviceInstanceListSupplier;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 随机数生成器&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private final Random random = new Random();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public WeightedLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 获取当前服务ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;this.serviceId = loadBalancerClientFactory.getName(environment);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 获取实例列表供应商&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;this.serviceInstanceListSupplier = loadBalancerClientFactory.getLazyProvider(serviceId, ServiceInstanceListSupplier.class);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public Mono&amp;lt;ServiceInstance&amp;gt; choose(Request request) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 1. 获取所有可用实例列表&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return serviceInstanceListSupplier.get()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.filter(instances -&amp;gt; !instances.isEmpty())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.flatMap(instances -&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 2. 解析每个实例的权重，计算总权重&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&amp;lt;WeightedInstance&amp;gt; weightedInstances = instances.stream()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.map(instance -&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 从实例metadata中获取权重（默认1）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String weightStr = instance.getMetadata().getOrDefault(&quot;weight&quot;, &quot;1&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;int weight = Integer.parseInt(weightStr);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return new WeightedInstance(instance, weight);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.collect(Collectors.toList());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;47&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 3. 计算总权重&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;48&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;int totalWeight = weightedInstances.stream().mapToInt(WeightedInstance::getWeight).sum();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;49&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if (totalWeight &amp;lt;= 0) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;50&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return Mono.empty();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;51&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;52&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;53&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 4. 随机生成一个0~totalWeight-1的数，按权重区间选择实例&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;54&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;int randomWeight = random.nextInt(totalWeight);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;55&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;int currentWeight = 0;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;56&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;for (WeightedInstance weightedInstance : weightedInstances) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;57&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;currentWeight += weightedInstance.getWeight();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;58&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if (randomWeight &amp;lt; currentWeight) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;59&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return Mono.just(weightedInstance.getInstance());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;60&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;61&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;62&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;63&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 兜底：返回第一个实例&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;64&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return Mono.just(weightedInstances.get(0).getInstance());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;65&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;66&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;67&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;68&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 内部类：封装实例和权重&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;69&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private static class WeightedInstance {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;70&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private final ServiceInstance instance;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;71&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private final int weight;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;72&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;73&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public WeightedInstance(ServiceInstance instance, int weight) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;74&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;this.instance = instance;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;75&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;this.weight = weight;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;76&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;77&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;78&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public ServiceInstance getInstance() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;79&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return instance;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;80&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;81&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;82&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public int getWeight() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;83&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return weight;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;84&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;85&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;86&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 3：配置自定义策略（绑定到服务）&lt;a href=&quot;#步骤-3配置自定义策略绑定到服务&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;创建配置类，将自定义策略注册为 Bean：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.context.annotation.Bean;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.context.annotation.Configuration;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.core.env.Environment;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 负载均衡策略配置类&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Configuration&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class CustomLoadBalancerConfig {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 注册自定义权重策略&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Bean&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public ReactorServiceInstanceLoadBalancer weightedLoadBalancer(Environment environment,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                                                   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;LoadBalancerClientFactory loadBalancerClientFactory) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return new WeightedLoadBalancer(environment, loadBalancerClientFactory);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 配置实例列表供应商（默认即可，无需修改）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Bean&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public ServiceInstanceListSupplier serviceInstanceListSupplier(LoadBalancerClientFactory factory) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return factory.getLazyProvider(factory.getName(environment), ServiceInstanceListSupplier.class);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 4：配置后端服务实例权重（以 Nacos 为例）&lt;a href=&quot;#步骤-4配置后端服务实例权重以-nacos-为例&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;在 Nacos 控制台为 &lt;code&gt;user-service&lt;/code&gt; 的不同实例配置 &lt;code&gt;metadata&lt;/code&gt;：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;进入 Nacos 控制台 → 服务管理 → 服务列表 → 点击 &lt;code&gt;user-service&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;编辑实例的&lt;strong&gt;元数据&lt;/strong&gt;，添加 &lt;code&gt;weight: 10&lt;/code&gt;、&lt;code&gt;weight: 20&lt;/code&gt;、&lt;code&gt;weight: 30&lt;/code&gt;（权重越高，被选中概率越大）。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 5：测试自定义策略&lt;a href=&quot;#步骤-5测试自定义策略&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;启动网关和多个 &lt;code&gt;user-service&lt;/code&gt; 实例（配置不同权重）；&lt;/li&gt;
&lt;li&gt;多次访问 &lt;code&gt;http://网关IP:网关端口/user/xxx&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;查看后端实例的日志，验证权重高的实例接收的请求数更多（如权重 30 的实例接收请求数约为权重 10 的 3 倍）。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;三、其他自定义策略场景&lt;a href=&quot;#三其他自定义策略场景&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;策略类型&lt;/th&gt;&lt;th&gt;适用场景&lt;/th&gt;&lt;th&gt;核心实现思路&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;最少连接数策略&lt;/td&gt;&lt;td&gt;后端实例性能差异大&lt;/td&gt;&lt;td&gt;记录每个实例的当前连接数，选择连接数最少的实例&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;IP 哈希策略&lt;/td&gt;&lt;td&gt;保证同一客户端请求到同一实例&lt;/td&gt;&lt;td&gt;对客户端 IP 做哈希运算，映射到固定实例（解决会话粘滞问题）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;区域优先策略&lt;/td&gt;&lt;td&gt;多机房部署&lt;/td&gt;&lt;td&gt;优先选择与网关同区域的实例，跨区域实例作为兜底&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;总结&lt;a href=&quot;#总结-3&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Gateway 负载均衡依赖 &lt;strong&gt;Spring Cloud LoadBalancer&lt;/strong&gt;，通过 &lt;code&gt;lb://&lt;/code&gt; 前缀触发，核心流程是「获取实例列表 → 按策略选实例 → 替换 uri 转发」；&lt;/li&gt;
&lt;li&gt;自定义负载均衡策略需实现 &lt;code&gt;ReactorServiceInstanceLoadBalancer&lt;/code&gt; 接口，适配 Gateway 的响应式架构；&lt;/li&gt;
&lt;li&gt;权重策略是最常用的自定义场景，需在后端实例的 metadata 中配置权重，在策略类中按权重区间选择实例。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;关键点：自定义策略需适配&lt;strong&gt;响应式模型&lt;/strong&gt;（使用 Mono/Flux），避免阻塞操作，确保 Gateway 的高性能。&lt;/p&gt;&lt;section&gt;&lt;h5&gt;动态路由的核心原理&lt;a href=&quot;#动态路由的核心原理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;Gateway 的路由规则默认从本地配置文件（&lt;code&gt;application.yml&lt;/code&gt;）加载（静态路由），由 &lt;code&gt;PropertiesRouteDefinitionRepository&lt;/code&gt; 管理，修改后需重启网关生效。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;动态路由的核心思路&lt;/strong&gt;：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;替换默认的 &lt;code&gt;RouteDefinitionRepository&lt;/code&gt; 接口实现（该接口负责路由规则的增、删、改、查）；&lt;/li&gt;
&lt;li&gt;将路由规则存储到&lt;strong&gt;外部配置源&lt;/strong&gt;（Nacos/Redis/MySQL 等）；&lt;/li&gt;
&lt;li&gt;监听外部配置源的变更，触发 Gateway 发布 &lt;code&gt;RefreshRoutesEvent&lt;/code&gt; 事件，实时刷新路由规则。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;简单来说：动态路由 = 外部配置源存储路由规则 + 自定义路由仓库 + 配置变更监听 + 路由刷新事件。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、实战实现：基于 Nacos 的动态路由（推荐）&lt;a href=&quot;#二实战实现基于-nacos-的动态路由推荐&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Nacos 是 Spring Cloud 生态中主流的配置中心，支持配置实时推送，是实现动态路由的首选方案，具体步骤如下：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;1. 引入核心依赖&lt;a href=&quot;#1-引入核心依赖&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;xml&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- Gateway 核心依赖 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;org.springframework.cloud&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;spring-cloud-starter-gateway&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- Nacos 配置中心依赖（支持配置动态推送） --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;com.alibaba.cloud&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;spring-cloud-starter-alibaba-nacos-config&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- Nacos 服务发现（可选，配合负载均衡） --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;com.alibaba.cloud&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;spring-cloud-starter-alibaba-nacos-discovery&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 配置 Nacos 连接（bootstrap.yml）&lt;a href=&quot;#2-配置-nacos-连接bootstrapyml&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Nacos 配置需放在 &lt;code&gt;bootstrap.yml&lt;/code&gt;（优先于 &lt;code&gt;application.yml&lt;/code&gt; 加载）：&lt;/p&gt;&lt;p&gt;yaml&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;spring:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;application:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: gateway-service  # 网关服务名（对应Nacos配置Data ID）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cloud:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# Nacos 配置中心配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;nacos:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;config:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server-addr: localhost:8848  # Nacos 服务地址&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;file-extension: yaml         # 配置文件格式（yaml/yml/properties）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;group: DEFAULT_GROUP         # 配置分组&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;namespace: public            # 命名空间（默认public）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# Nacos 服务发现配置（可选）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;discovery:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server-addr: localhost:8848&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 自定义动态路由仓库（核心）&lt;a href=&quot;#3-自定义动态路由仓库核心&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;实现 &lt;code&gt;RouteDefinitionRepository&lt;/code&gt; 接口，从 Nacos 读取路由规则，并监听配置变更触发路由刷新：&lt;/p&gt;&lt;p&gt;java运行&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import com.alibaba.cloud.nacos.NacosConfigManager;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import com.alibaba.fastjson2.JSON;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import com.alibaba.fastjson2.TypeReference;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.gateway.route.RouteDefinition;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.gateway.route.RouteDefinitionRepository;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.gateway.support.NotFoundException;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.context.ApplicationEventPublisher;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.context.ApplicationEventPublisherAware;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.stereotype.Component;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import reactor.core.publisher.Flux;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import reactor.core.publisher.Mono;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import javax.annotation.PostConstruct;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import java.util.List;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Component&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class NacosRouteDefinitionRepository implements RouteDefinitionRepository, ApplicationEventPublisherAware {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// Nacos 配置管理器（读取配置）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private final NacosConfigManager nacosConfigManager;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 路由配置的Data ID（格式：${spring.application.name}.${file-extension}）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private final String dataId = &quot;gateway-service.yaml&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 配置分组&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private final String group = &quot;DEFAULT_GROUP&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 应用事件发布器（发布路由刷新事件）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private ApplicationEventPublisher publisher;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public NacosRouteDefinitionRepository(NacosConfigManager nacosConfigManager) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;this.nacosConfigManager = nacosConfigManager;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 初始化：监听Nacos配置变更&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@PostConstruct&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public void init() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;try {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 监听Nacos配置变更，触发路由刷新&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;nacosConfigManager.getConfigService().addListener(dataId, group, event -&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 发布路由刷新事件，Gateway会重新加载路由&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;publisher.publishEvent(new org.springframework.cloud.gateway.event.RefreshRoutesEvent(this));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} catch (Exception e) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;throw new RuntimeException(&quot;Nacos路由配置监听失败&quot;, e);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 读取所有路由规则（Gateway启动/刷新时调用）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;47&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public Flux&amp;lt;RouteDefinition&amp;gt; getRouteDefinitions() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;48&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;try {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;49&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 从Nacos读取配置内容&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;50&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String configContent = nacosConfigManager.getConfigService().getConfig(dataId, group, 5000);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;51&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 将JSON/YAML转为RouteDefinition列表（FastJSON解析）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;52&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;List&amp;lt;RouteDefinition&amp;gt; routeDefinitions = JSON.parseObject(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;53&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;configContent,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;54&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new TypeReference&amp;lt;List&amp;lt;RouteDefinition&amp;gt;&amp;gt;() {}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;55&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;56&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return Flux.fromIterable(routeDefinitions);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;57&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} catch (Exception e) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;58&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return Flux.error(new NotFoundException(&quot;从Nacos加载路由规则失败：&quot; + e.getMessage()));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;59&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;60&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;61&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;62&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 新增路由（按需实现，可调用Nacos API写入配置）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;63&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;64&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public Mono&amp;lt;Void&amp;gt; save(Mono&amp;lt;RouteDefinition&amp;gt; route) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;65&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 实际场景可实现：将新增的路由写入Nacos配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;66&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return route.flatMap(r -&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;67&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 省略：读取现有配置 → 添加新路由 → 调用Nacos API更新配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;68&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return Mono.empty();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;69&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;70&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;71&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;72&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 删除路由（按需实现）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;73&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;74&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public Mono&amp;lt;Void&amp;gt; delete(Mono&amp;lt;String&amp;gt; routeId) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;75&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 实际场景可实现：读取现有配置 → 删除指定路由 → 调用Nacos API更新配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;76&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return routeId.flatMap(id -&amp;gt; Mono.empty());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;77&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;78&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;79&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 设置事件发布器&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;80&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;81&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;82&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;this.publisher = applicationEventPublisher;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;83&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;84&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;4. 在 Nacos 配置中心添加路由规则&lt;a href=&quot;#4-在-nacos-配置中心添加路由规则&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;登录 Nacos 控制台 → 配置管理 → 配置列表 → 新建配置：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Data ID：&lt;code&gt;gateway-service.yaml&lt;/code&gt;（与代码中一致）&lt;/li&gt;
&lt;li&gt;Group：&lt;code&gt;DEFAULT_GROUP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;配置格式：YAML&lt;/li&gt;
&lt;li&gt;配置内容（路由规则示例）：&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;yaml&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;id&quot;: &quot;user-service-route&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;uri&quot;: &quot;lb://user-service&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;predicates&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;name&quot;: &quot;Path&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;args&quot;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;_genkey_0&quot;: &quot;/user/**&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;name&quot;: &quot;Method&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;args&quot;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;_genkey_0&quot;: &quot;GET&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;filters&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;name&quot;: &quot;AddRequestHeader&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;args&quot;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;_genkey_0&quot;: &quot;X-From-Gateway&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;_genkey_1&quot;: &quot;Nacos-Dynamic-Route&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;id&quot;: &quot;order-service-route&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;uri&quot;: &quot;lb://order-service&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;predicates&quot;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;name&quot;: &quot;Path&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;args&quot;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;_genkey_0&quot;: &quot;/order/**&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;5. 测试动态路由&lt;a href=&quot;#5-测试动态路由&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;启动网关服务，验证路由规则是否从 Nacos 加载；&lt;/li&gt;
&lt;li&gt;在 Nacos 控制台修改路由规则（比如新增 / 删除路由、修改 Predicate/Filter）；&lt;/li&gt;
&lt;li&gt;无需重启网关，直接访问测试，验证路由规则已实时生效。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;三、其他动态路由实现方式&lt;a href=&quot;#三其他动态路由实现方式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;除了 Nacos，还可基于 Redis/MySQL 实现，核心思路一致（替换路由仓库 + 监听配置变更）：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;1. 基于 Redis 实现&lt;a href=&quot;#1-基于-redis-实现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;路由规则以 JSON 格式存储在 Redis 的 Hash/List 中；&lt;/li&gt;
&lt;li&gt;自定义 &lt;code&gt;RouteDefinitionRepository&lt;/code&gt;，从 Redis 读取路由规则；&lt;/li&gt;
&lt;li&gt;监听 Redis Key 过期 / 变更事件（或通过消息通知），触发路由刷新。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 基于 MySQL 实现&lt;a href=&quot;#2-基于-mysql-实现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;建表存储路由规则（字段：id、uri、predicates、filters、order 等）；&lt;/li&gt;
&lt;li&gt;自定义 &lt;code&gt;RouteDefinitionRepository&lt;/code&gt;，从数据库读取路由规则；&lt;/li&gt;
&lt;li&gt;通过定时任务轮询数据库（简单）或基于 Binlog 监听表变更（实时），触发路由刷新。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;四、总结&lt;a href=&quot;#四总结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Gateway 动态路由的核心是&lt;strong&gt;替换 &lt;code&gt;RouteDefinitionRepository&lt;/code&gt;&lt;/strong&gt;，将路由源从本地配置改为外部存储（Nacos/Redis/MySQL）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nacos 是 Spring Cloud 生态首选&lt;/strong&gt;，配置简单且支持配置自动推送，无需额外实现监听逻辑；&lt;/li&gt;
&lt;li&gt;路由变更后需发布 &lt;code&gt;RefreshRoutesEvent&lt;/code&gt; 事件，让 Gateway 实时感知并重新加载路由规则；&lt;/li&gt;
&lt;li&gt;按需实现 &lt;code&gt;save/delete&lt;/code&gt; 方法，可通过 API 动态增删路由，无需手动修改配置中心内容。&lt;/li&gt;
&lt;/ol&gt;&lt;section&gt;&lt;h5&gt;什么是 Gateway 的 Predicate？&lt;a href=&quot;#什么是-gateway-的-predicate&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;Gateway 中的 Predicate（断言）本质是&lt;strong&gt;基于 Java 8 &lt;code&gt;java.util.function.Predicate&lt;/code&gt; 接口实现的路由匹配条件&lt;/strong&gt;，核心作用是：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;当客户端请求进入网关时，网关会遍历所有路由的 Predicate 集合，判断请求是否满足该路由的所有 Predicate 条件；&lt;/li&gt;
&lt;li&gt;只有&lt;strong&gt;所有 Predicate 都返回 &lt;code&gt;true&lt;/code&gt;&lt;/strong&gt;，请求才会被该路由匹配，并转发到路由指定的目标 URI；&lt;/li&gt;
&lt;li&gt;特点：支持多个 Predicate 组合（逻辑与），也可自定义 Predicate 满足个性化匹配需求（比如按请求体内容匹配）。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;简单来说，Predicate 就是网关的 “路由筛选规则”—— 符合规则的请求走这条路由，不符合的跳过。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、常用的 Predicate 类型及实战示例&lt;a href=&quot;#二常用的-predicate-类型及实战示例&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Gateway 内置了十余种常用 Predicate，覆盖请求路径、方法、参数、IP、时间等维度，以下是最常用的类型（均以 &lt;code&gt;application.yml&lt;/code&gt; 配置为例）：&lt;/p&gt;






















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Predicate 类型&lt;/th&gt;&lt;th&gt;核心作用&lt;/th&gt;&lt;th&gt;配置示例&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Path&lt;/code&gt;&lt;/td&gt;&lt;td&gt;匹配请求路径（最常用）&lt;/td&gt;&lt;td&gt;```yaml&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;predicates:&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Path=/user/&lt;/p&gt;
&lt;p&gt;,/order/&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| 匹配所有以 `/user/` 或 `/order/` 开头的请求；`**` 表示多级路径通配，`*` 表示单级路径通配 | | `Method` | 匹配 HTTP 请求方法 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;yaml&lt;/p&gt;
&lt;p&gt;predicates:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Method=GET,POST&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| `Header` | 匹配请求头参数 | ```yaml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;predicates:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Header=X-Token, \d+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;``` | 匹配请求头中包含 `X-Token` 且值为数字的请求；第二个参数支持正则表达式 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| `Query` | 匹配 URL 请求参数 | ```yaml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;predicates:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Query=userId, 100[1-9]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Query=token  # 仅判断参数存在，不校验值&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;``` | 第一个示例：匹配包含 `userId` 参数且值为 1001-1009 的请求；第二个示例：仅判断 `token` 参数存在即可 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| `RemoteAddr` | 匹配客户端 IP 地址 | ```yaml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;predicates:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- RemoteAddr=192.168.1.0/24,10.0.0.0/8&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;``` | 匹配来自 192.168.1.x 网段或 10.x.x.x 网段的客户端请求；支持 CIDR 格式 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| `After` | 匹配指定时间**之后**的请求 | ```yaml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;predicates:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- After=2026-01-01T00:00:00+08:00[Asia/Shanghai]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;``` | 仅匹配 2026年1月1日 0点之后的请求；时间格式为 `ISO-8601`，需指定时区 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| `Before` | 匹配指定时间**之前**的请求 | ```yaml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;predicates:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Before=2026-12-31T23:59:59+08:00[Asia/Shanghai]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;``` | 仅匹配 2026年12月31日 23:59:59 之前的请求 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| `Between` | 匹配指定时间区间内的请求 | ```yaml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;predicates:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Between=2026-01-01T00:00:00+08:00[Asia/Shanghai],2026-12-31T23:59:59+08:00[Asia/Shanghai]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;``` | 仅匹配 2026 全年的请求 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| `Cookie` | 匹配客户端 Cookie | ```yaml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;predicates:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Cookie=username, zhangsan&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;``` | 匹配客户端 Cookie 中包含 `username` 且值为 `zhangsan` 的请求；支持正则 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#### 补充：多 Predicate 组合使用（逻辑与）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;实际场景中，常组合多个 Predicate 实现精准匹配，比如：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;```yaml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;spring:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cloud:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gateway:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;routes:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- id: user-service-route&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;uri: lb://user-service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;predicates:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Path=/user/**          # 路径匹配&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Method=GET             # 方法匹配&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Header=X-Token, \d+    # 请求头匹配&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- RemoteAddr=192.168.1.0/24  # IP匹配&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;上述配置表示：&lt;strong&gt;只有同时满足 “路径以 /user/ 开头 + GET 方法 + 请求头 X-Token 为数字 + 客户端 IP 在 192.168.1.x 网段”&lt;/strong&gt; 的请求，才会被该路由处理。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;三、总结&lt;a href=&quot;#三总结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Gateway 的 Predicate 是&lt;strong&gt;路由的匹配条件&lt;/strong&gt;，基于 Java 8 Predicate 接口，只有满足所有 Predicate 的请求才会被对应路由转发；&lt;/li&gt;
&lt;li&gt;常用 Predicate 覆盖&lt;strong&gt;路径（Path）、方法（Method）、参数（Query）、IP（RemoteAddr）、时间（After/Before）&lt;/strong&gt; 等核心维度，是配置路由规则的基础；&lt;/li&gt;
&lt;li&gt;多 Predicate 组合为&lt;strong&gt;逻辑与&lt;/strong&gt;关系，可实现精准的请求匹配，满足不同业务场景的路由需求。&lt;/li&gt;
&lt;/ol&gt;&lt;section&gt;&lt;h5&gt;Gateway 的 Filter 核心分类&lt;a href=&quot;#gateway-的-filter-核心分类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;Gateway 的 Filter 主要分为 &lt;strong&gt;GatewayFilter（路由级别过滤器）&lt;/strong&gt; 和 &lt;strong&gt;GlobalFilter（全局级别过滤器）&lt;/strong&gt; 两类，两者在作用范围、使用方式上有明确区别，具体对比如下：&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;过滤器类型&lt;/th&gt;&lt;th&gt;核心定义&lt;/th&gt;&lt;th&gt;作用范围&lt;/th&gt;&lt;th&gt;配置 / 实现方式&lt;/th&gt;&lt;th&gt;典型使用场景&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;GatewayFilter&lt;/td&gt;&lt;td&gt;路由专属过滤器&lt;/td&gt;&lt;td&gt;仅作用于&lt;strong&gt;指定 / 部分路由&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;1. 配置文件中通过 &lt;code&gt;filters&lt;/code&gt; 节点配置（内置 GatewayFilter）；2. 代码自定义 GatewayFilterFactory&lt;/td&gt;&lt;td&gt;为特定路由添加请求头、去除路径前缀、路由级限流 / 重试、修改响应头&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GlobalFilter&lt;/td&gt;&lt;td&gt;全局通用过滤器&lt;/td&gt;&lt;td&gt;作用于&lt;strong&gt;所有路由&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;1. 实现 &lt;code&gt;GlobalFilter + Ordered&lt;/code&gt; 接口；2. 结合 &lt;code&gt;@Order&lt;/code&gt; 注解&lt;/td&gt;&lt;td&gt;全局身份认证（Token 校验）、全局日志记录、跨域处理（CORS）、全局限流 / 熔断&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;两类过滤器的简单示例&lt;a href=&quot;#两类过滤器的简单示例&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;section&gt;&lt;h5&gt;1. GatewayFilter（路由级，配置文件方式）&lt;a href=&quot;#1-gatewayfilter路由级配置文件方式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;yaml&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;spring:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cloud:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gateway:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;routes:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- id: user-service-route&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;uri: lb://user-service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;predicates: [Path=/user/**]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;filters:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 内置 GatewayFilter：添加请求头（仅作用于该路由）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- AddRequestHeader=X-Route-Id, user-service-route&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 自定义 GatewayFilter：指定 order（优先级）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- name: CustomGatewayFilter&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;args:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order: 10&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;2. GlobalFilter（全局级，代码实现）&lt;a href=&quot;#2-globalfilter全局级代码实现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;java运行&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.gateway.filter.GlobalFilter;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.gateway.filter.GatewayFilterChain;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.core.Ordered;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.http.server.reactive.ServerHttpRequest;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.stereotype.Component;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.web.server.ServerWebExchange;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import reactor.core.publisher.Mono;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 全局过滤器：记录所有请求的路径（作用于所有路由）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Component&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class LogGlobalFilter implements GlobalFilter, Ordered {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public Mono&amp;lt;Void&amp;gt; filter(ServerWebExchange exchange, GatewayFilterChain chain) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ServerHttpRequest request = exchange.getRequest();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;System.out.println(&quot;全局过滤器：请求路径 = &quot; + request.getPath());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 继续执行过滤器链&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return chain.filter(exchange);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 设置 order（数值越小，优先级越高）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public int getOrder() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return 5;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、Filter 执行顺序的控制规则&lt;a href=&quot;#二filter-执行顺序的控制规则&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Gateway 过滤器的执行顺序核心由 &lt;strong&gt;&lt;code&gt;order&lt;/code&gt; 属性&lt;/strong&gt; 控制，同时区分「请求转发前（pre 阶段）」和「响应返回后（post 阶段）」两个执行阶段，具体规则如下：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;1. 核心规则&lt;a href=&quot;#1-核心规则&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;order 数值越小，优先级越高&lt;/strong&gt;：不管是 GatewayFilter 还是 GlobalFilter，&lt;code&gt;order&lt;/code&gt; 数值越小，越先执行；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pre 阶段（请求转发前）&lt;/strong&gt;：过滤器按 &lt;code&gt;order&lt;/code&gt; 升序执行（小 → 大）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;post 阶段（响应返回后）&lt;/strong&gt;：过滤器按 &lt;code&gt;order&lt;/code&gt; 降序执行（大 → 小）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;混合执行&lt;/strong&gt;：GatewayFilter 和 GlobalFilter 会被合并到同一个过滤器链中，按 &lt;code&gt;order&lt;/code&gt; 统一排序执行。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 不同过滤器设置 order 的方式&lt;a href=&quot;#2-不同过滤器设置-order-的方式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;section&gt;&lt;h5&gt;（1）GatewayFilter 设置 order&lt;a href=&quot;#1gatewayfilter-设置-order&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;内置 GatewayFilter&lt;/p&gt;
&lt;p&gt;：配置文件中通过&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;order&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;参数指定（默认值一般为&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;）；yaml&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;filters:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- name: RequestRateLimiter  # 内置限流过滤器&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;args:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;order: 1  # 设为1，优先级高于默认0的过滤器&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;key-resolver: &quot;#{@ipKeyResolver}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;自定义 GatewayFilter&lt;/p&gt;
&lt;p&gt;：通过&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;GatewayFilterFactory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;实现时，重写&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;getOrder()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;方法：&lt;/p&gt;
&lt;p&gt;java运行&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.gateway.filter.GatewayFilter;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.gateway.filter.GatewayFilterFactory;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.core.Ordered;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.stereotype.Component;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Component&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class CustomGatewayFilterFactory implements GatewayFilterFactory&amp;lt;Object&amp;gt;, Ordered {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public GatewayFilter apply(Object config) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return (exchange, chain) -&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;System.out.println(&quot;路由级过滤器执行&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return chain.filter(exchange);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 设置 order&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public int getOrder() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return 10;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;（2）GlobalFilter 设置 order&lt;a href=&quot;#2globalfilter-设置-order&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;有两种方式（&lt;strong&gt;Ordered 接口优先级高于 @Order 注解&lt;/strong&gt;）：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;方式 1：实现 Ordered 接口&lt;/p&gt;
&lt;p&gt;（推荐，更直观）：&lt;/p&gt;
&lt;p&gt;如上面的&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;LogGlobalFilter&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;示例，通过&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;getOrder()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;方法返回 order 值；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方式 2：使用 @Order 注解&lt;/p&gt;
&lt;p&gt;：java运行&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.gateway.filter.GlobalFilter;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.core.annotation.Order;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.stereotype.Component;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.web.server.ServerWebExchange;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import reactor.core.publisher.Mono;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Component&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Order(8)  // order 值为8，优先级高于上面的 LogGlobalFilter（order=5？不，8&amp;gt;5，优先级更低）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class AuthGlobalFilter implements GlobalFilter {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public Mono&amp;lt;Void&amp;gt; filter(ServerWebExchange exchange, GatewayFilterChain chain) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;System.out.println(&quot;全局认证过滤器执行&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return chain.filter(exchange);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 执行顺序示例（直观理解）&lt;a href=&quot;#3-执行顺序示例直观理解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;假设存在以下过滤器：&lt;/p&gt;







































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;过滤器类型&lt;/th&gt;&lt;th&gt;order 值&lt;/th&gt;&lt;th&gt;执行阶段&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;GlobalFilter A&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;pre&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GatewayFilter B&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;pre&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GlobalFilter C&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;pre&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GatewayFilter B&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;post&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GlobalFilter C&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;post&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GlobalFilter A&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;post&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;strong&gt;实际执行顺序&lt;/strong&gt;：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;pre 阶段：A（5）→ B（10）→ C（15）→ 转发请求到后端服务；&lt;/li&gt;
&lt;li&gt;post 阶段：C（15）→ B（10）→ A（5）→ 返回响应给客户端。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;三、关键补充：过滤器执行的特殊场景&lt;a href=&quot;#三关键补充过滤器执行的特殊场景&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;负数 order&lt;/strong&gt;：允许设置负数（如 &lt;code&gt;order=-1&lt;/code&gt;），优先级更高（比 0 先执行），常用于核心全局过滤器（如跨域处理）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;相同 order&lt;/strong&gt;：同 order 的过滤器执行顺序不保证（尽量避免），可通过微调 order 值（如一个设 5，一个设 6）解决；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内置过滤器的默认 order&lt;/strong&gt;：Gateway 内置的 GlobalFilter（如负载均衡、路由转发）有默认 order，比如负载均衡过滤器 &lt;code&gt;LoadBalancerClientFilter&lt;/code&gt; 的 order 是 &lt;code&gt;0&lt;/code&gt;，路由转发过滤器 &lt;code&gt;RouteToRequestUrlFilter&lt;/code&gt; 的 order 是 &lt;code&gt;10000&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;总结&lt;a href=&quot;#总结-4&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Gateway Filter 分为&lt;strong&gt;路由级（GatewayFilter）&lt;/strong&gt;（作用于指定路由）和&lt;strong&gt;全局级（GlobalFilter）&lt;/strong&gt;（作用于所有路由）两类；&lt;/li&gt;
&lt;li&gt;执行顺序核心靠 &lt;code&gt;order&lt;/code&gt; 属性控制：数值越小优先级越高，pre 阶段升序执行、post 阶段降序执行；&lt;/li&gt;
&lt;li&gt;GatewayFilter 可通过配置 / 代码设置 order，GlobalFilter 推荐实现 Ordered 接口（或用 @Order 注解）指定 order，且 Ordered 接口优先级更高。&lt;/li&gt;
&lt;/ol&gt;&lt;section&gt;&lt;h5&gt;Spring Cloud Gateway 实现限流的方式&lt;a href=&quot;#spring-cloud-gateway-实现限流的方式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;Spring Cloud Gateway 官方提供了 &lt;code&gt;RequestRateLimiterGatewayFilterFactory&lt;/code&gt; 组件实现限流，核心是结合 &lt;strong&gt;Redis&lt;/strong&gt; 和 &lt;strong&gt;令牌桶算法（Token Bucket）&lt;/strong&gt;，支持按 IP、用户 ID、接口路径等多维度限流，具体实现步骤如下：&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1. 引入必要依赖&lt;a href=&quot;#1-引入必要依赖&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Gateway 是响应式框架，需引入 Redis 响应式依赖（非普通的 &lt;code&gt;spring-boot-starter-data-redis&lt;/code&gt;），同时引入 Gateway 核心依赖：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- Gateway 核心依赖 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;org.springframework.cloud&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;spring-cloud-starter-gateway&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- Redis 响应式依赖（限流核心，必须用 reactive 版本） --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;artifactId&amp;gt;spring-boot-starter-data-redis-reactive&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 定义限流维度（KeyResolver）&lt;a href=&quot;#2-定义限流维度keyresolver&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;code&gt;KeyResolver&lt;/code&gt; 是限流的 “维度定义器”，用于决定按什么维度限流（如 IP、用户、接口），需自定义实现该接口：java运行&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.context.annotation.Bean;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.context.annotation.Configuration;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.web.server.ServerWebExchange;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import reactor.core.publisher.Mono;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Configuration&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class RateLimitConfig {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 1. 按客户端IP限流（最常用）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Bean&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public KeyResolver ipKeyResolver() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return exchange -&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 获取客户端IP地址&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String clientIp = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return Mono.just(clientIp);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 2. 按用户ID限流（需用户登录，从请求头/参数取userId）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Bean&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public KeyResolver userIdKeyResolver() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return exchange -&amp;gt; Mono.just(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;exchange.getRequest().getHeaders().getFirst(&quot;X-User-Id&quot;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 3. 按接口路径限流&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Bean&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public KeyResolver apiKeyResolver() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return exchange -&amp;gt; Mono.just(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;exchange.getRequest().getPath().toString()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 配置 Redis 连接和限流规则&lt;a href=&quot;#3-配置-redis-连接和限流规则&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;在 &lt;code&gt;application.yml&lt;/code&gt; 中配置 Redis 地址、令牌桶核心参数（速率、容量）：&lt;/p&gt;&lt;p&gt;yaml&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;spring:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# Redis 连接配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redis:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;host: localhost  # Redis 服务地址&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;port: 6379       # Redis 端口&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;database: 0      # 数据库索引（默认0）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# Gateway 限流配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cloud:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gateway:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;routes:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- id: user-service-route  # 路由ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;uri: lb://user-service  # 目标服务（负载均衡）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;predicates:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Path=/user/**       # 匹配/user开头的请求&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;filters:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 限流过滤器配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- name: RequestRateLimiter&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;args:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 指定限流维度（对应上面定义的 KeyResolver bean 名称）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;key-resolver: &quot;#{@ipKeyResolver}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 令牌桶每秒填充速率（允许的平均QPS）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redis-rate-limiter.replenishRate: 10&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 令牌桶最大容量（允许的突发QPS）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redis-rate-limiter.burstCapacity: 20&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 每次请求消耗的令牌数（默认1）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redis-rate-limiter.requestedTokens: 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;4. 自定义限流响应（可选）&lt;a href=&quot;#4-自定义限流响应可选&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;默认限流触发时返回 &lt;code&gt;429 Too Many Requests&lt;/code&gt; 空响应，可自定义返回 JSON 提示：&lt;/p&gt;&lt;p&gt;java运行&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.core.io.buffer.DataBuffer;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.http.HttpStatus;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.http.MediaType;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.http.server.reactive.ServerHttpResponse;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.stereotype.Component;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import org.springframework.web.server.ServerWebExchange;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import reactor.core.publisher.Mono;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import java.nio.charset.StandardCharsets;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Component&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class CustomRateLimitHandler {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public Mono&amp;lt;Void&amp;gt; handle(ServerWebExchange exchange) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ServerHttpResponse response = exchange.getResponse();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;response.getHeaders().setContentType(MediaType.APPLICATION_JSON);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String msg = &quot;{\&quot;code\&quot;:429,\&quot;msg\&quot;:\&quot;请求过于频繁，请稍后再试\&quot;}&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DataBuffer buffer = response.bufferFactory().wrap(msg.getBytes(StandardCharsets.UTF_8));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return response.writeWith(Mono.just(buffer));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、Redis 限流的原理&lt;a href=&quot;#二redis-限流的原理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Gateway 基于 Redis 的限流核心是 &lt;strong&gt;令牌桶算法&lt;/strong&gt;，并通过 Redis 的 Lua 脚本保证限流逻辑的原子性，避免并发问题，具体原理拆解如下：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;1. 令牌桶算法（Token Bucket）基础&lt;a href=&quot;#1-令牌桶算法token-bucket基础&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;令牌桶是限流的经典算法，核心规则：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;系统以&lt;strong&gt;固定速率&lt;/strong&gt;（&lt;code&gt;replenishRate&lt;/code&gt;）向令牌桶中填充令牌；&lt;/li&gt;
&lt;li&gt;令牌桶有&lt;strong&gt;最大容量&lt;/strong&gt;（&lt;code&gt;burstCapacity&lt;/code&gt;），满了之后不再填充；&lt;/li&gt;
&lt;li&gt;每次请求需要从桶中获取指定数量的令牌（&lt;code&gt;requestedTokens&lt;/code&gt;）；&lt;/li&gt;
&lt;li&gt;令牌足够则放行请求（令牌数减少），不足则拒绝请求（限流）。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. Redis 实现令牌桶的核心逻辑（Lua 脚本）&lt;a href=&quot;#2-redis-实现令牌桶的核心逻辑lua-脚本&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Gateway 的 &lt;code&gt;RedisRateLimiter&lt;/code&gt; 内置了 Lua 脚本，通过 Redis 存储每个限流维度的令牌状态，保证 “计算令牌数 - 更新令牌数” 的原子性（Redis 单线程执行 Lua 脚本，避免并发计数错误）。&lt;/p&gt;&lt;section&gt;&lt;h5&gt;核心 Lua 脚本逻辑（简化版）&lt;a href=&quot;#核心-lua-脚本逻辑简化版&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;-- 限流维度的key（如 ip:192.168.1.1）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;local key = KEYS[1]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;-- 参数：replenishRate(每秒填充速率), burstCapacity(最大容量), requestedTokens(单次消耗令牌数), 当前时间&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;local replenishRate = tonumber(ARGV[1])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;local burstCapacity = tonumber(ARGV[2])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;local requestedTokens = tonumber(ARGV[3])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;local now = tonumber(ARGV[4])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;-- 存储令牌状态的两个key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;local lastRefreshedKey = key .. &quot;:last_refreshed&quot;  -- 最后一次填充令牌的时间&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;local tokensKey = key .. &quot;:tokens&quot;                -- 当前剩余令牌数&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;-- 获取上次状态（默认：last_refreshed=0，tokens=burstCapacity）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;local lastRefreshed = tonumber(redis.call(&quot;get&quot;, lastRefreshedKey) or 0)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;local currentTokens = tonumber(redis.call(&quot;get&quot;, tokensKey) or burstCapacity)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;-- 计算时间差，填充令牌（按速率补充，最多到burstCapacity）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;local timeElapsed = now - lastRefreshed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if timeElapsed &amp;gt; 0 then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;local newTokens = currentTokens + (timeElapsed * replenishRate)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;currentTokens = math.min(newTokens, burstCapacity)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redis.call(&quot;set&quot;, lastRefreshedKey, now)  -- 更新最后填充时间&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;-- 判断是否有足够令牌&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;local allowed = 0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if currentTokens &amp;gt;= requestedTokens then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;allowed = 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;currentTokens = currentTokens - requestedTokens  -- 消耗令牌&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;-- 保存更新后的令牌数&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;redis.call(&quot;set&quot;, tokensKey, currentTokens)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;-- 返回结果：是否允许(1/0)、剩余令牌数、最大令牌数&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;return {allowed, currentTokens, burstCapacity}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;Redis 限流的关键要点&lt;a href=&quot;#redis-限流的关键要点&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;原子性保障&lt;/strong&gt;：Lua 脚本将 “读取状态→计算令牌→更新状态” 封装为原子操作，避免多请求并发时的令牌数错误；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;状态存储&lt;/p&gt;
&lt;p&gt;：每个限流维度在 Redis 中存储两个 key：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;{key}:last_refreshed&lt;/code&gt;：记录最后一次填充令牌的时间戳；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{key}:tokens&lt;/code&gt;：记录当前剩余令牌数；&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;动态填充&lt;/strong&gt;：每次请求都会计算从上一次请求到现在的时间差，按速率补充令牌，保证令牌数动态更新；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;突发流量处理&lt;/strong&gt;：&lt;code&gt;burstCapacity&lt;/code&gt; 允许短时间内的突发流量（比如每秒允许 10 个，最多一次处理 20 个），兼顾限流和灵活性。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;总结&lt;a href=&quot;#总结-5&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Spring Cloud Gateway 核心通过 &lt;code&gt;RequestRateLimiterGatewayFilterFactory&lt;/code&gt; 实现限流，需配置 &lt;code&gt;KeyResolver&lt;/code&gt; 定义限流维度、Redis 连接和令牌桶参数（速率 / 容量）；&lt;/li&gt;
&lt;li&gt;Redis 限流的核心是&lt;strong&gt;令牌桶算法&lt;/strong&gt;，通过 Lua 脚本保证令牌计算 / 更新的原子性，避免并发问题；&lt;/li&gt;
&lt;li&gt;Redis 存储限流维度的令牌状态（最后填充时间、剩余令牌数），每次请求动态填充令牌并判断是否放行。&lt;/li&gt;
&lt;/ol&gt;&lt;section&gt;&lt;h5&gt;Spring Cloud Gateway 核心组件及作用&lt;a href=&quot;#spring-cloud-gateway-核心组件及作用&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;Spring Cloud Gateway 是基于 Spring 5、Spring Boot 2 和 Project Reactor 构建的非阻塞式 API 网关，其核心设计围绕&lt;strong&gt;路由 (Route)&lt;/strong&gt;、&lt;strong&gt;断言 (Predicate)&lt;/strong&gt;、&lt;strong&gt;过滤器 (Filter)&lt;/strong&gt; 三大组件展开，此外还有一些辅助核心组件（如网关处理器映射器、网关 web 处理器），下面逐一说明：&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1. 核心组件一：路由（Route）&lt;a href=&quot;#1-核心组件一路由route&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;：路由是网关最核心的基础单元，是网关的 “转发规则”，由 &lt;code&gt;ID&lt;/code&gt;、&lt;code&gt;目标URI&lt;/code&gt;、&lt;code&gt;断言集合&lt;/code&gt;、&lt;code&gt;过滤器集合&lt;/code&gt; 组成。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;作用&lt;/strong&gt;：网关接收到请求后，会根据路由规则匹配请求，匹配成功则将请求转发到指定的目标 URI（后端服务地址）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;核心属性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;id&lt;/code&gt;：路由唯一标识（如 &lt;code&gt;user-service-route&lt;/code&gt;），避免重复；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uri&lt;/code&gt;：请求转发的目标地址（支持 &lt;code&gt;http/https&lt;/code&gt;、&lt;code&gt;lb://&lt;/code&gt;（负载均衡）、&lt;code&gt;ws/wss&lt;/code&gt; 等）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;predicates&lt;/code&gt;：断言集合（判断请求是否匹配当前路由）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;filters&lt;/code&gt;：过滤器集合（对请求 / 响应进行修改）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;order&lt;/code&gt;：路由优先级（数值越小优先级越高，多个路由匹配时生效）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;示例（配置文件方式）yaml&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;spring&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cloud&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;gateway&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;routes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;user-service-route&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# 路由ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;lb://user-service&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# 转发到用户服务（负载均衡）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;predicates&lt;/span&gt;&lt;span&gt;:             &lt;/span&gt;&lt;span&gt;# 断言：匹配路径以/user/开头的请求&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;Path=/user/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;filters&lt;/span&gt;&lt;span&gt;:                &lt;/span&gt;&lt;span&gt;# 过滤器：添加请求头&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;AddRequestHeader=X-Request-From, Gateway&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 核心组件二：断言（Predicate）&lt;a href=&quot;#2-核心组件二断言predicate&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;：Predicate 是 Java 8 的 &lt;code&gt;java.util.function.Predicate&lt;/code&gt; 接口的实现，本质是 “条件判断规则”。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;作用&lt;/strong&gt;：判断请求是否满足当前路由的匹配条件，只有所有断言都返回 &lt;code&gt;true&lt;/code&gt;，请求才会被该路由匹配并转发。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;常用断言类型&lt;/p&gt;
&lt;p&gt;（Spring Cloud Gateway 内置）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Path&lt;/code&gt;：匹配请求路径（如 &lt;code&gt;/user/**&lt;/code&gt;）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Method&lt;/code&gt;：匹配请求方法（如 &lt;code&gt;GET&lt;/code&gt;、&lt;code&gt;POST&lt;/code&gt;）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Header&lt;/code&gt;：匹配请求头（如 &lt;code&gt;Header=X-Token, \d+&lt;/code&gt; 匹配头中 X-Token 为数字的请求）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Query&lt;/code&gt;：匹配请求参数（如 &lt;code&gt;Query=name, zhangsan&lt;/code&gt; 匹配参数 name=zhangsan 的请求）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;After/Before/Between&lt;/code&gt;：匹配请求时间（如 &lt;code&gt;After=2026-01-01T00:00:00+08:00[Asia/Shanghai]&lt;/code&gt; 匹配指定时间后的请求）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RemoteAddr&lt;/code&gt;：匹配请求客户端 IP（如 &lt;code&gt;RemoteAddr=192.168.1.0/24&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：支持多个断言组合（逻辑与），也可自定义断言满足个性化匹配需求。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 核心组件三：过滤器（Filter）&lt;a href=&quot;#3-核心组件三过滤器filter&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;：过滤器是网关对请求 / 响应进行拦截和修改的核心组件，分为 &lt;code&gt;GatewayFilter&lt;/code&gt;（路由级别）和 &lt;code&gt;GlobalFilter&lt;/code&gt;（全局级别）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;作用&lt;/strong&gt;：在请求转发到后端服务前 / 后，对请求（如添加头信息、参数、限流）或响应（如修改响应体、添加响应头）进行加工处理。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分类及示例&lt;/p&gt;
&lt;p&gt;：&lt;/p&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;类型&lt;/th&gt;&lt;th&gt;作用范围&lt;/th&gt;&lt;th&gt;常用示例&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;GatewayFilter&lt;/td&gt;&lt;td&gt;单个 / 部分路由&lt;/td&gt;&lt;td&gt;1. &lt;code&gt;AddRequestHeader&lt;/code&gt;：添加请求头；2. &lt;code&gt;StripPrefix&lt;/code&gt;：去除请求路径前缀（如 &lt;code&gt;/api/user&lt;/code&gt; 去除 /api 后转发到 /user）；3. &lt;code&gt;Retry&lt;/code&gt;：请求失败重试；4. &lt;code&gt;RequestRateLimiter&lt;/code&gt;：限流（基于 Redis）。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GlobalFilter&lt;/td&gt;&lt;td&gt;所有路由&lt;/td&gt;&lt;td&gt;1. 身份认证（如校验 Token）；2. 日志记录；3. 跨域处理；4. 全局限流。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;执行顺序&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;过滤器有 &lt;code&gt;order&lt;/code&gt; 属性，数值越小执行优先级越高；&lt;/li&gt;
&lt;li&gt;请求转发前（pre）的过滤器按 order 升序执行，请求返回后（post）的过滤器按 order 降序执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;4. 辅助核心组件&lt;a href=&quot;#4-辅助核心组件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	&lt;strong&gt;RouteLocator&lt;/strong&gt;：路由定位器，负责加载和管理所有路由规则，支持配置文件、代码编码、动态路由（如从 Nacos 拉取）等多种路由加载方式。&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;GatewayHandlerMapping&lt;/strong&gt;：网关处理器映射器，接收客户端请求后，通过 &lt;code&gt;RouteLocator&lt;/code&gt; 获取所有路由，结合断言匹配出最合适的路由。&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;GatewayWebHandler&lt;/strong&gt;：网关 Web 处理器，执行匹配路由的过滤器链（GlobalFilter + GatewayFilter），完成请求转发和响应处理。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;核心组件工作流程（补充）&lt;a href=&quot;#核心组件工作流程补充&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;为了让你更清晰理解组件间的配合，这里梳理核心流程：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;客户端发送请求到 Spring Cloud Gateway；&lt;/li&gt;
&lt;li&gt;GatewayHandlerMapping 通过 RouteLocator 获取所有路由，结合 Predicate 匹配请求对应的路由；&lt;/li&gt;
&lt;li&gt;匹配成功后，GatewayWebHandler 执行该路由的 GlobalFilter + GatewayFilter 链（pre 阶段）；&lt;/li&gt;
&lt;li&gt;请求转发到后端服务，获取响应；&lt;/li&gt;
&lt;li&gt;执行过滤器链的 post 阶段；&lt;/li&gt;
&lt;li&gt;将响应返回给客户端。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;总结&lt;a href=&quot;#总结-6&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;路由 (Route)&lt;/strong&gt; 是网关的核心转发规则，包含 ID、目标 URI、断言和过滤器，是请求转发的 “导航规则”；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;断言 (Predicate)&lt;/strong&gt; 是路由的 “匹配条件”，只有满足所有断言的请求才会被当前路由处理；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;过滤器 (Filter)&lt;/strong&gt; 是网关的 “加工工具”，分为路由级别和全局级别，负责请求 / 响应的拦截和修改，是实现限流、认证、日志等功能的核心。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;</content:encoded></item><item><title>Java基础知识</title><link>https://www.liuguang.top/posts/java/acti1-java%E7%9F%A5%E8%AF%86%E6%80%BB%E7%BB%93/</link><guid isPermaLink="true">https://www.liuguang.top/posts/java/acti1-java%E7%9F%A5%E8%AF%86%E6%80%BB%E7%BB%93/</guid><description>java相关基础知识。</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;Java基础知识&lt;a href=&quot;#java基础知识&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;section&gt;&lt;h2&gt;1、Java基础&lt;a href=&quot;#1java基础&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;1.0、java和其他语言之间的区别：&lt;a href=&quot;#10java和其他语言之间的区别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;java：&lt;a href=&quot;#java&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	Java简单，易于设计，易于编写，因此比其他任何Java都易于编译，调试和学习。Java是面向对象的，用于构建模块化程序和其他应用程序中的可重用代码。Java与平台无关，可移植复制。&lt;/p&gt;&lt;p&gt;​		首先Java是一个面向对象的编程语言，容易理解。而且略去了多重加载、指针等难以理解的概念。并且实现了自动垃圾回收，大大简化了程序设计。&lt;/p&gt;&lt;p&gt;​		跨平台是Java最大的优势。Java运行在JVM（Java虚拟机）上，在任何平台只要安装了JVM。Java就可以运行。它架构在操作系统之上，屏蔽了底层的差异。真正实现了“Write once,run anywhere”。Java中没有指针，这样就没有办法直接访问内存了。&lt;/p&gt;&lt;p&gt;​		另外Java也不容易出现内存泄露。&lt;/p&gt;&lt;p&gt;​		Java内置对多线程的支持，可以方便地在程序中实现多线程的功能。不像其他不支持多线程的语言，需要调用操作系统的多线程功能才能完成多线程的实现。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;C++：&lt;a href=&quot;#c&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;C#：&lt;a href=&quot;#c-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;python：&lt;a href=&quot;#python&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;matlab：&lt;a href=&quot;#matlab&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.1、说一说对java访问权限的了解&lt;a href=&quot;#11说一说对java访问权限的了解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;有&lt;strong&gt;四种&lt;/strong&gt;访问修饰符可以修饰&lt;strong&gt;成员变量或成员方法&lt;/strong&gt;：private、default、protected、public&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;private：修饰的成员变量仅可以在本类中使用。&lt;/li&gt;
&lt;li&gt;不写访问修饰符（default）：修饰的成员变量可以在本类中使用，在同一个包下的其他类中也可以使用。&lt;/li&gt;
&lt;li&gt;protected：修饰的成员变量可以在本类中使用，可以在同一个包下的其他类中使用，还可以在当前类的&lt;strong&gt;子类&lt;/strong&gt;（有继承关系）中使用。&lt;/li&gt;
&lt;li&gt;public：修饰的成员变量可以在项目的任意包、任意类中使用。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;有&lt;strong&gt;两种&lt;/strong&gt;访问修饰符可以修饰&lt;strong&gt;类&lt;/strong&gt;：default、public&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;不写访问修饰符（default）：可以在同一个包下使用。&lt;/li&gt;
&lt;li&gt;public：可以在项目下的任意包、任意类中使用。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.2、说一说对java变量的理解（区别）&lt;a href=&quot;#12说一说对java变量的理解区别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;成员变量&lt;/strong&gt;：成员变量是在类中定义，成员变量是有默认初始值，&lt;/p&gt;&lt;p&gt;​	被static修饰的叫静态变量也加&lt;strong&gt;类变量&lt;/strong&gt;，是存储在方法区中，生命周期与&lt;strong&gt;当前类&lt;/strong&gt;相同。&lt;/p&gt;&lt;p&gt;​	未被static修饰的成员变量叫做&lt;strong&gt;实例变量&lt;/strong&gt;，存储在堆区中，生命周期与&lt;strong&gt;实例对象&lt;/strong&gt;相同。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;局部变量&lt;/strong&gt;：局部变量是在方法中定义，局部变量没有默认初始值，（执行方法的时候，java虚拟机会创建一个栈帧，用于存储局部变量表），所以局部变量存储在栈内存中，作用范围结束、变量空间自动释放。&lt;/p&gt;&lt;section&gt;&lt;h4&gt;说一说对static关键字的理解？&lt;a href=&quot;#说一说对static关键字的理解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;static关键字可以修饰类、方法、变量、代码块。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;修饰类&lt;/strong&gt;：普通类是不允许声明为静态的，只有内部类才可以使用static修饰。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;修饰方法&lt;/strong&gt;：修饰方法的时候，可以直接通过类名来进行调用。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;修饰变量&lt;/strong&gt;：被static修饰的成员变量叫做静态变量，也叫做类变量，说明这个变量是属于这个类的，而不是属于是对象，没有被static修饰的成员变量叫做实例变量，说明这个变量是属于某个具体的对象的。静态变量&lt;strong&gt;存放在方法区&lt;/strong&gt;中，并且是被所有&lt;strong&gt;线程所共享&lt;/strong&gt;的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;修饰代码块&lt;/strong&gt;：静态代码块在类第一次被载入时执行。执行顺序：父类静态变量—&amp;gt;父类静态代码块—&amp;gt;子类静态变量—&amp;gt;子类静态代码块—&amp;gt;父类普通变量—&amp;gt;父类普通代码块—&amp;gt;父类构造函数—&amp;gt;子类普通变量—&amp;gt;子类普通代码块—&amp;gt;子类构造函数。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;成员变量和静态变量的区别&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;成员变量随着对象的创建而存在随着对象的回收而释放。静态变量随着类的加载而存在随着类的消失而消失。&lt;/li&gt;
&lt;li&gt;成员变量只能被对象调用。静态变量可以被对象调用，也可以用类名调用。（推荐用类名调用）&lt;/li&gt;
&lt;li&gt;成员变量也称为实例变量。静态变量称为类变量。&lt;/li&gt;
&lt;li&gt;成员变量数据存储在堆内存的对象中，所以也叫对象的特有数据。静态变量数据存储在方法区（共享数据区）的静态区，所以也叫对象的共享数据。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.3、了解java的基础类型有哪些吗？然后默认值是多少？然后了解基本类型对应的包装类吗？了解自动装箱与自动拆箱吗？&lt;a href=&quot;#13了解java的基础类型有哪些吗然后默认值是多少然后了解基本类型对应的包装类吗了解自动装箱与自动拆箱吗&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;基础类型：（数字类型4种）byte、short、int、long、（浮点型2种）float、double、（其他）char、boolean&lt;/p&gt;&lt;p&gt;数据类型	包装类型	默认值&lt;/p&gt;&lt;p&gt;byte			Byte			0&lt;/p&gt;&lt;p&gt;short		  Short		   0&lt;/p&gt;&lt;p&gt;int			   Integer	   0&lt;/p&gt;&lt;p&gt;long			Long		   0L&lt;/p&gt;&lt;p&gt;float			Float			0.0F&lt;/p&gt;&lt;p&gt;double		Double		0.0&lt;/p&gt;&lt;p&gt;char			Character	‘\u0000’&lt;/p&gt;&lt;p&gt;boolean	 Boolean		false&lt;/p&gt;&lt;p&gt;&lt;strong&gt;自动装箱&lt;/strong&gt;：基本数据类型自动转换成包装类的过程，可以将一个基本数据类型直接赋值给对应的包装类。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;自动拆箱&lt;/strong&gt;：包装类自动转换成基本数据类型的过程，可以将一个包装类直接赋值给对应的基本数据类型。&lt;/p&gt;&lt;p&gt;包装类属于引用数据类型，Object是所有引用数据类型的超类。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.4、说一说对面向对象的理解？&lt;a href=&quot;#14说一说对面向对象的理解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;**整体理解：**面向对象是对现实世界的理解和抽象的方法。&lt;/p&gt;&lt;p&gt;**特征：**面向对象有三个主要特征分别是封装、继承、多态&lt;/p&gt;&lt;p&gt;​	**封装：**将类的某些信息隐藏在类内部，不允许外部程序直接访问，而是通过提供的方法来实现对隐藏信息的操作和访问&lt;/p&gt;&lt;p&gt;​		使用封装的好处：只能通过规定的方法来访问数据；&lt;/p&gt;&lt;p&gt;​	**继承：**继承是为了提高代码的复用性，在定义不同类的时候是存在相同的属性，为了方便可以将这些相同的属性都抽象成一个父类，子类直接继承父类，减少代码的重复定义，子类可以使用父类的非私有成员。&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;为什么不能多继承？java只能单继承&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​			&lt;strong&gt;答&lt;/strong&gt;：为什么不能多继承，继承的话，子类是继承父类的非私有成员，如果多继承的话，假如两个类有同名的方法，那么子类在调用该方法时，就会产生混淆，同样在重写的时候也会有混淆，因此java不支持多继承。&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;接口可以多继承吗？为什么？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​			&lt;strong&gt;答&lt;/strong&gt;：java的接口可以多继承，几个接口可以有相同的实现类和实现方法，因为接口的成员变量都是 static final的，有自己静态域，只能自己使用。&lt;/p&gt;&lt;p&gt;​	**多态：**多态是指父类的引用指向子类的对象，无须任何类型转换，向上转型自动完成，在编译时的类型是父类的类型，而运行时的类型是子类的类型，调用方法时表现出子类的特征而不是父类的特征，呈现了不同的特征，这就是多态。&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;多态怎么实现？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​			多态的实现离不开继承，父类的引用指向子类的对象。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.5、重写和重载的区别？&lt;a href=&quot;#15重写和重载的区别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;​	&lt;strong&gt;重写&lt;/strong&gt;：重写是发生在子类与父类之间的关系，若子类相对父类的方法重写，那么必须保证方法名、参数列表相同，另外返回值可以小于等于父类的方法，抛出的异常要小于等于父类方法，访问修饰符要大于等于父类方法。此外，父类为private修饰的方法，子类是不能重写的。&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;重载&lt;/strong&gt;：重载是发生在同一个类中，若多个方法之间方法名相同，参数列表不同，则构成重载关系（参数列表一定需要不同，参数类型不同或者数量不同或者不同参数类型之间的顺序不同）。重载与访问修饰符和返回值无关，不能根据访问修饰符以及返回值来判断是 否重载。&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;只有修饰符和返回值不一样算不算重载？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	答：返回值类型不同或者修饰符不同的方法不算重载，编译的时候直接报方法名重复错&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.6、说说对String类的理解？&lt;a href=&quot;#16说说对string类的理解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;1.6.1 String的理解&lt;a href=&quot;#161-string的理解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	先看String的源码：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; java.io.&lt;/span&gt;&lt;span&gt;Serializable&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Comparable&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CharSequence&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Stable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;byte&lt;/span&gt;&lt;span&gt;[] value&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;byte&lt;/span&gt;&lt;span&gt; coder&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; hash&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// Default to 0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt; serialVersionUID &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span&gt;6849794470754667710L&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; COMPACT_STRINGS&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​		从源码中关于&lt;strong&gt;String类&lt;/strong&gt;的定义可以知道String类是用&lt;strong&gt;final&lt;/strong&gt;修饰的，意味着String类&lt;strong&gt;不能被继承&lt;/strong&gt;，并且它的成员方法都默认为final方法。在Java中final修饰的类是不允许被继承的。从类里面关于变量的定义我们可以知道，String存储字符串是使用一个final修饰的byte[ ]数组，&lt;strong&gt;final修饰的变量是不允许被更改&lt;/strong&gt;的，因此String一旦被创建就是不可变的，这时候对该字符串做的修改操作，比如拼接、裁剪、替换都会生成一个新的字符串。&lt;/p&gt;&lt;p&gt;​		字符串的分配和其他对象分配一样，是分配栈内存的，而且我们使用字符串的场景特别多，JVM为了提高性能和减少内存的开销，在实例化字符串的时候做了一些优化：使用&lt;strong&gt;字符串常量池&lt;/strong&gt;，每当我们创建字符串常量时，JVM会首先检查字符串常量池，如果该字符已经在字符串常量池里面，那么直接返回常量池中的引用，如果不在的话，就会实例化字符串，并且将其放到常量池中。由于String的不可变性，我们能肯定的认为字符串常量池中不会存在两个相同的字符串。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;注：常量池分为下面两种&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;静态常量池：是.class文件中的常量池，不仅仅包括字符串、数字的字面量，还包括类、方法的信息，占用class文件的绝大部分空间。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;运行时常量池：是jvm类装载完成后，将class文件的常量池放入到内存中，并保存在方法区，我们常说的常量池，就是指方法区中的运行时常量池。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;字符串的比较？是否相等？&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; s1 &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Runoob&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// String 直接创建&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; s2 &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Runoob&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// String 直接创建&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; s3 &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; s1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 相同引用&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; s4 &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Runoob&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// String 对象创建&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; s5 &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Runoob&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// String 对象创建&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; s6 &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Run&quot;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&quot;oob&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// String 对象创建&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;//s1、s2、s3、s4、s5是否相等？&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;s1&lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt;s2&lt;/span&gt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//s1和s2直接指向常量池，故相等&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;s2&lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt;s3&lt;/span&gt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;s4&lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt;s5&lt;/span&gt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;//s4是在堆区新建的对象，s5也是在堆区新建的对象，这是两个不同的对象，虽然存储的值是一样的&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;s1&lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt;s4&lt;/span&gt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;//s4是在堆区新建的对象，s1直接指向常量池&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;s1&lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt;s5&lt;/span&gt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;//同上&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;s1&lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt;s6&lt;/span&gt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//s6看String的底层源码可以看到，两个字符串常量值在相加的时候，底层会直接优化成一个字符串。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.6.2 String、StringBuffer、StringBuilder的区别？&lt;a href=&quot;#162-stringstringbufferstringbuilder的区别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	&lt;strong&gt;String&lt;/strong&gt;：字符串&lt;strong&gt;常量&lt;/strong&gt;，每次对字符串修改都会创建一个新的字符串&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;StringBuilder&lt;/strong&gt;：字符串&lt;strong&gt;变量&lt;/strong&gt;，线程&lt;strong&gt;不安全&lt;/strong&gt;，对字符串修改就在其对象本身上修改&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;StringBuffer&lt;/strong&gt;：字符串&lt;strong&gt;变量&lt;/strong&gt;，线程&lt;strong&gt;安全&lt;/strong&gt;，对字符串修改就在其对象本身上修改&lt;/p&gt;&lt;p&gt;​	执行速度：在大部分情况下：StringBuilder&amp;gt;StringBuffer&amp;gt;String&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.6.3 String在jdk1.8和jdk1.9中的区别&lt;a href=&quot;#163-string在jdk18和jdk19中的区别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;jdk1.9&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;byte&lt;/span&gt;&lt;span&gt;[] value&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;byte&lt;/span&gt;&lt;span&gt; coder&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;//jdk1.8&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;char&lt;/span&gt;&lt;span&gt;[] value&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​		String的底层实现在jdk1.8时使用的char[ ]数组，在jdk1.9的时候使用的是byte[ ]数组，这是因为开发者发现人们使用拉丁符号居多，而拉丁符号只占一个字节，char一个字符占两个字节，那么就会造成内存的浪费，GC（垃圾回收）的更加频繁，浪费了系统资源。因此jdk1.9将char数组改成了byte数组，使用byte数组会产生一个问题，因为中文字符存储是需要占两个字节，在jdk1.9中使用了一个标志位coder表示该字符串的编码方式，判断是Latin还是UTF-16。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.7、说一说对异常的理解？&lt;a href=&quot;#17说一说对异常的理解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;1.7.1 错误 Error&lt;a href=&quot;#171-错误-error&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​		错误一般是指与虚拟机相关的问题，如系统崩溃、虚拟机错误、动态链接失败等，这种错误无法恢复以及捕获，将导致程序中断。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.7.2 编译时异常 Checked&lt;a href=&quot;#172-编译时异常-checked&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​		编译时异常是可以被处理的异常，必须被显式的处理，如果不处理的话，不能通过编译。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.7.3 运行时异常 RuntimeException&lt;a href=&quot;#173-运行时异常-runtimeexception&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​		运行时异常，在程序运行时发生的异常，可以不处理也能通过编译，但是运行时会报错，需要通过try catch finally捕获或者抛出异常。&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;异常的抛出&lt;/strong&gt;：当程序出现错误时，系统会自动抛出异常，Java也允许程序员主动抛出异常，在业务代码中，判断某项错误的条件成立时，可以使用throw关键字向外抛出异常，在这种情况下，如果当前方法不知道该如何处理这个异常，可以在方法签名上通过throws关键字声明抛出异常，将异常交给JVM处理。&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;异常的处理&lt;/strong&gt;：在java语句中，处理异常的语句由try、catch、finally三部分组成，其中try用于包裹可能出现异常的业务代码，catch用于捕获并处理某个类型的异常，finally用于后处理，例如资源的释放。当业务代码发生异常时，系统会为此异常创建一个异常对象，创建异常对象之后，JVM会寻找可以处理该异常的catch块并交给其处理。finally中的语句块是总会执行的，即在执行try、catch后总会执行finally块。（finally不执行的例外情况：使用 System.exit(1); 来退出虚拟机）&lt;/p&gt;&lt;p&gt;&lt;strong&gt;在try和finally里面都有return会执行哪个？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		先执行try中的return，但不立即返回，而是去寻找是否有finally，如果有，继续执行finally中的代码，如果finally中没有return，那么执行完finally后回到try中执行return，如果finally中有return，那么直接返回。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.7.4 怎么自定义异常？&lt;a href=&quot;#174-怎么自定义异常&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;创建一个自定义异常类继承Exception&lt;/li&gt;
&lt;li&gt;在需要用到这个自定义异常类的地方使用 throw new 自定义异常类名(“异常信息”);&lt;/li&gt;
&lt;li&gt;在方法上使用throws抛出这个自定义异常。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.8、说一说对反射的理解？&lt;a href=&quot;#18说一说对反射的理解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;编译:.java文件编译后生成.class字节码文件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;加载：类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部，并存储在运行时内存区的方法区，然后将其转换为一个与目标类型对应的java.lang.Class对象实例&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;连接：细分三步&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;验证：格式（class文件规范） 语义（final类是否有子类） 操作&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;准备：静态变量赋初值和内存空间，final修饰的内存空间直接赋原值，此处不是用户指定的初值。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;解析：符号引用转化为直接引用，分配地址&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;初始化:有父类先初始化父类，然后初始化自己；将static修饰代码执行一遍，如果是静态变量，则用用户指定值覆盖原有初值；如果是代码块，则执行一遍操作。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​		Java的反射就是利用上面第二步加载到jvm中的.class文件来进行操作的。.class文件中包含java类的所有信息，当你不知道某个类具体信息时，可以使用反射获取class，然后进行各种操作。&lt;/p&gt;&lt;p&gt;​		Java反射就是在运行状态中，对于任意一个类，都能够知道这个类的所有属性和方法；对于任意一个对象，都能够调用它的任意方法和属性；并且能改变它的属性。总结说：反射就是把java类中的各种成分映射成一个个的Java对象，并且可以进行操作。&lt;/p&gt;&lt;section&gt;&lt;h4&gt;1.8.1 什么是反射？&lt;a href=&quot;#181-什么是反射&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​		一般我们要使用某个已知类时，直接使用new一个对象实例化，这是正向的创建一个类的对象，如果一开始我们不知道要创建的类是什么的时候，自然也就无法通过new关键字来创建对象，这时候我们就需要利用反射，使用JDK提供的反射API来进行反射调用。&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;反射就是在运行时才知道要操作的类是什么，并且可以在运行时获取类的完整构造，并调用对应的方法。&lt;/strong&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.8.2 通过反射获取class对象&lt;a href=&quot;#182-通过反射获取class对象&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;Class c = Class.forName(“类的全限定名称”);&lt;/li&gt;
&lt;li&gt;Class c = String.class;//事先就知道要操作的类&lt;/li&gt;
&lt;li&gt;Class c = 引用.getClass();//可以根据一个引用反向获取类的对象&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;通过反射创建对象：&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;通过Class对象的newInstance方法：&lt;/p&gt;
&lt;p&gt;Class c = String.class;&lt;/p&gt;
&lt;p&gt;String str = (String)c.newInstance();&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过Constructor对象的newInstance方法&lt;/p&gt;
&lt;p&gt;Constructor constructor = c.getConstructor();&lt;/p&gt;
&lt;p&gt;String str= (String )constructor.newInstance();&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.8.3 通过反射获取类属性、方法、构造器：&lt;a href=&quot;#183-通过反射获取类属性方法构造器&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;属性&lt;/strong&gt;：我们通过 Class 对象的 &lt;strong&gt;getFields()&lt;/strong&gt; 方法可以获取 Class 类的属性，但&lt;strong&gt;无法获取私有属性&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​		    而如果使用 Class 对象的 &lt;strong&gt;getDeclaredFields()&lt;/strong&gt; 方法则可以获取包括&lt;strong&gt;私有属性&lt;/strong&gt;在内的&lt;strong&gt;所有属性&lt;/strong&gt;。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Class&lt;/span&gt;&lt;span&gt; clz &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Field&lt;/span&gt;&lt;span&gt;[] fields &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;clz&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getFields&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;span&gt;//获取非私有属性&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;Field&lt;/span&gt;&lt;span&gt; field &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; fields) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getName&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Class&lt;/span&gt;&lt;span&gt; clz &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Field&lt;/span&gt;&lt;span&gt;[] fields &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;clz&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getDeclaredFields&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;span&gt;//获取所有属性 包括私有的&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;Field&lt;/span&gt;&lt;span&gt; field &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; fields) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;field&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getName&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;方法&lt;/strong&gt;：我们通过 Class 对象的 &lt;strong&gt;getMethods()&lt;/strong&gt; 方法可以获取 Class 类的方法，但&lt;strong&gt;无法获取私有方法&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​		    而如果使用 Class 对象的 &lt;strong&gt;getDeclaredMethods()&lt;/strong&gt; 方法则可以获取包括&lt;strong&gt;私有方法&lt;/strong&gt;在内的&lt;strong&gt;所有方法&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;构造器&lt;/strong&gt;：我们通过 Class 对象的 &lt;strong&gt;getConstructors()&lt;strong&gt;方法可以获取 Class 类的构造器，但&lt;/strong&gt;无法获取私有构造器&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​			    而如果使用 Class 对象的 &lt;strong&gt;getDeclaredConstructors()&lt;strong&gt;方法则可以获取包括&lt;/strong&gt;私有构造器&lt;/strong&gt;在内的&lt;strong&gt;所有构造器&lt;/strong&gt;。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.8.4 反射的优缺点&lt;a href=&quot;#184-反射的优缺点&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;优点：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;增加程序的灵活性，避免将程序写死到代码里。&lt;/li&gt;
&lt;li&gt;代码简洁，提高代码的复用率，外部调用方便&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;缺点：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;性能问题：反射包括了一些动态类型，所以JVM无法对这些代码进行优化。因此，反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被 执行的代码或对性能要求很高的程序中使用反射。&lt;/li&gt;
&lt;li&gt;使用反射会模糊程序内部逻辑：程序人员希望在源代码中看到程序的逻辑，反射等绕过了源代码的技术，因而会带来维护问题。反射代码比相应的直接代码更复杂。&lt;/li&gt;
&lt;li&gt;安全限制：使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行，如Applet，那么这就是个问题了。&lt;/li&gt;
&lt;li&gt;内部暴露：由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法)，所以使用反射可能会导致意料之外的副作用。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.9、说一说对注解的理解？&lt;a href=&quot;#19说一说对注解的理解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;​		注解也叫元数据。可以声明在&lt;strong&gt;包、类、字段、方法、局部变量、方法参数&lt;/strong&gt;等前面，来对这些元素进行说明、注释等。&lt;/p&gt;&lt;p&gt;​		Java提供了四种元注解：（所谓元注解就是&lt;strong&gt;负责注解其他注解的注解&lt;/strong&gt;）本质是注解，但是是用来注解其他注解，对其他注解进行说明。&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;@Target：规定注解所修饰的对象范围&lt;/p&gt;
&lt;p&gt;ElementType.CONSTRUCTIR; &lt;strong&gt;构造器声明&lt;/strong&gt;
ElementType.FIELD; &lt;strong&gt;成员变量&lt;/strong&gt;，对象，属性（包括enum实例）
ElementType.LOCAL_VARIABLE; &lt;strong&gt;局部变量&lt;/strong&gt;声明
ElementType.METHOD ; &lt;strong&gt;方法&lt;/strong&gt;声明
ElementType.PACKAGE; &lt;strong&gt;包&lt;/strong&gt;声明
ElementType.PARAMETER;&lt;strong&gt;参数&lt;/strong&gt;声明
ElementType.TYPE; &lt;strong&gt;类&lt;/strong&gt;、&lt;strong&gt;接口&lt;/strong&gt;（包括注解类型）或enum声明&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;@Retention：规定注解的生命周期&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RetentionPolicy.&lt;strong&gt;SOUREC&lt;/strong&gt;: 在&lt;strong&gt;源文件中&lt;/strong&gt;有效&lt;/li&gt;
&lt;li&gt;RetentionPolicy.&lt;strong&gt;CLASS&lt;/strong&gt;; 在&lt;strong&gt;class文件中&lt;/strong&gt;有效&lt;/li&gt;
&lt;li&gt;RetentionPolicy.&lt;strong&gt;RUNTIME&lt;/strong&gt;;在&lt;strong&gt;运行时&lt;/strong&gt;有效&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;@Inherited： 标记注解，主要说明了一种继承性，意思是子类可以继承父类中的该注解（注意：只有当被贴上@Inherited标签的注解被用在类上的时候子类才能获得这个注解）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;@Document： 用于描述其它类型的annotation（注解）应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。（表明这个注释是由 javadoc记录的，在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化，它的注释成为公共API的一部分。）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;2、集合&lt;a href=&quot;#2集合&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;Collection接口结构&quot; loading=&quot;lazy&quot; width=&quot;2062&quot; height=&quot;1241&quot; src=&quot;/_astro/Collection%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D8mslRHq_ijTIF.webp&quot; srcset=&quot;/_astro/Collection%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D8mslRHq_1O3HYa.webp 640w, /_astro/Collection%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D8mslRHq_Z15EiBY.webp 750w, /_astro/Collection%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D8mslRHq_1KIRcC.webp 828w, /_astro/Collection%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D8mslRHq_10TgO5.webp 1080w, /_astro/Collection%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D8mslRHq_Z1lF4kn.webp 1280w, /_astro/Collection%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D8mslRHq_Zb3CBy.webp 1668w, /_astro/Collection%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D8mslRHq_c0R8V.webp 2048w, /_astro/Collection%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D8mslRHq_ijTIF.webp 2062w&quot; /&gt;&lt;figcaption&gt;Collection接口结构&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;Map接口结构&quot; loading=&quot;lazy&quot; width=&quot;1916&quot; height=&quot;1388&quot; src=&quot;/_astro/Map%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D9VtSp-n_Z1JTLNA.webp&quot; srcset=&quot;/_astro/Map%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D9VtSp-n_1CCOPo.webp 640w, /_astro/Map%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D9VtSp-n_Z1UaIMt.webp 750w, /_astro/Map%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D9VtSp-n_gWWLH.webp 828w, /_astro/Map%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D9VtSp-n_Zgt0UP.webp 1080w, /_astro/Map%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D9VtSp-n_10WpIT.webp 1280w, /_astro/Map%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D9VtSp-n_FdY1V.webp 1668w, /_astro/Map%E6%8E%A5%E5%8F%A3%E7%BB%93%E6%9E%84.D9VtSp-n_Z1JTLNA.webp 1916w&quot; /&gt;&lt;figcaption&gt;Map接口结构&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;section&gt;&lt;h3&gt;2.1 Java中有哪些集合类？&lt;a href=&quot;#21-java中有哪些集合类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;​		Java的集合分为单列集合Collection和双列集合Map，其他的接口和实现类都是在这两个接口之下派生的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Collection接口&lt;/strong&gt;：单列集合：派生出三个子接口：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;List接口：List接口是有序的（元素存入的顺序和取出的顺序是一致的），可以存储重复元素&lt;/li&gt;
&lt;li&gt;Set接口：Set接口是无序的（元素存入的顺序和取出的顺序不一致），不可以存储重复元素。&lt;/li&gt;
&lt;li&gt;Queue接口：代表先入先出的队列&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;关于有序无序问题：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;有序是指：我们按1、3、2、3这样的顺序存储，那么我们取出的顺序也是1、3、2、3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;无序是指：我们按1、3、2、3这样的顺序存储，我们取出的顺序不知道有可能是2、1、3，也有可能是1、2、3，没有任何规律,不含重复元素&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;原因：元素存储时，是根据hashCode值去找存储位置，而hashCode值是随机的，所以存储的顺序也是随机的。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;那么Set是如何保证元素的唯一性？&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;答：在Set的一个实现类HashSet中，元素的唯一性，是靠hashCode()和equals()来判断，使用hashCode()方法计算出当前元素的存储位置，如果当前位置没有元素，直接存储，如果当前位置有元素，使用equals()判断两个元素是否相等，如果相等不用插入，如果不相等，启用哈希冲突的解决办法，将当前元素链接到原来元素的后面。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;哈希（散列）：是把任意长度的输入通过哈希算法变换成固定长度的输出，该输出就是散列值。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;hashCode()方法源码：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public native int hashCode();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;调用的native方法，根据新建的对象的内存地址进行哈希得到一个存储的索引。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;==和equals()的区别？&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;==运算符：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;作用于基本数据类型时，是比较两个数值是否相等；&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;作用于引用数据类型时，是比较两个对象的内存地址是否相同，即判断它们是否为同一个对象；&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;equals()方法：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;没有重写时，Object默认以 == 来实现，即比较两个对象的内存地址是否相同；&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;进行重写后，一般会按照对象的内容来进行比较，若两个对象内容相同则认为对象相等，否则认为对象不等。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;为什么要重写equals()和hashCode()？&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;规定：两个对象相等，则他们的hashCode一定相等；两个对象的hashCode相等，两个对象不一定相等。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;equals()方法如果不重写的话，默认判断的两个对象是否是同一个对象，而实际业务中，两个对象的内容相同，我们就可以认为是同一个对象，所以我们通常是需要重写equals()方法的。如果重写equals()不重写hashCode()可能会出现两个对象使用equals()判断相等但是hashCode不相等的情况，与实际不符。所以我们需要同时重写equals()和hashCode()方法。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Map接口&lt;/strong&gt;：Map接口代表具有映射关系的集合。key-value&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.2 List接口&lt;a href=&quot;#22-list接口&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;List接口下有三个常用的接口实现类：ArrayList、Vector、LinkedList&lt;/p&gt;&lt;p&gt;List接口定义的方法：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; index&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; element) 将指定元素插入此列表中的指定位置（可选操作）。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 将指定的元素追加到此列表的末尾（可选操作）。  &lt;/span&gt;&lt;span&gt;**************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; o) 如果此列表包含指定的元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。  &lt;/span&gt;&lt;span&gt;*******&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; index) 返回此列表中指定位置的元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isEmpty&lt;/span&gt;&lt;span&gt;() 如果此列表不包含任何元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。  &lt;/span&gt;&lt;span&gt;****************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; index) 删除此列表中指定位置的元素（可选操作）。  &lt;/span&gt;&lt;span&gt;***************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; o) 从该列表中删除指定元素的第一个匹配项（如果存在）（可选操作）。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 返回此列表中的元素数。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;section&gt;&lt;h4&gt;2.2.1 ArrayList实现类&lt;a href=&quot;#221-arraylist实现类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ArrayList&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbstractList&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RandomAccess&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cloneable&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; java.io.&lt;/span&gt;&lt;span&gt;Serializable&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt; serialVersionUID &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;8683452581122892189L&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; DEFAULT_CAPACITY &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;//初始默认容量&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;[] EMPTY_ELEMENTDATA &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;//被transient修饰的变量不参与序列化和反序列化&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;[] elementData&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;//存储结构&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; size&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​		ArrayList是List接口的实现类，首先满足List的特征，&lt;strong&gt;元素有序、可重复&lt;/strong&gt;，ArrayList是&lt;strong&gt;线程不安全&lt;/strong&gt;的，&lt;strong&gt;底层使用数组实现，默认初始容量是10&lt;/strong&gt;，存满后按照&lt;strong&gt;1.5倍扩容&lt;/strong&gt;，扩容源码如下：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;grow&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;grow&lt;/span&gt;&lt;span&gt;&lt;span&gt;(size &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;grow&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; minCapacity) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; elementData &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Arrays&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;copyOf&lt;/span&gt;&lt;span&gt;(elementData,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                                       &lt;/span&gt;&lt;span&gt;newCapacity&lt;/span&gt;&lt;span&gt;(minCapacity));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newCapacity&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; minCapacity) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// overflow-conscious code&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; oldCapacity &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elementData&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;//新容量大小=旧容量大小+旧容量右移一位（右移一位相当于除2）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; newCapacity &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; oldCapacity &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; (oldCapacity &lt;/span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (newCapacity &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; minCapacity &lt;/span&gt;&lt;span&gt;&amp;lt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (elementData &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; DEFAULTCAPACITY_EMPTY_ELEMENTDATA)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(DEFAULT_CAPACITY, minCapacity);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (minCapacity &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;// overflow&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OutOfMemoryError&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; minCapacity&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; (newCapacity &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;MAX_ARRAY_SIZE&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; newCapacity&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hugeCapacity&lt;/span&gt;&lt;span&gt;&lt;span&gt;(minCapacity)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;​			ArrayList在对象创建的时候并没有直接创建长度为10的数组，这个数组初始化的过程是在第一次插入元素的时候，（而在JDK1.7中是在对象创建的时候直接创建长度为10 的数组）。源码如下：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt;[] elementData&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; s) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (s &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elementData&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;//判断数组是不是空，如果是空第一次执行扩容，扩容到默认的大小&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elementData &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;grow&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elementData[s] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; e&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;size &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; s &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;ArrayList常用方法：&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; index&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; element) 将指定元素插入此列表中的指定位置。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 将指定的元素追加到此列表的末尾。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; o) 如果此列表包含指定的元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。 &lt;/span&gt;&lt;span&gt;******&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; index) 返回此列表中指定位置的元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isEmpty&lt;/span&gt;&lt;span&gt;() 如果此列表不包含任何元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。  &lt;/span&gt;&lt;span&gt;**************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;indexOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt; o) 返回此列表中第一次出现的指定元素的索引，如果此列表不包含该元素，则返回&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; index) 删除此列表中指定位置的元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; o) 从该列表中删除指定元素的第一个匹配项（如果存在）。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 返回此列表中的元素数。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;toArray&lt;/span&gt;&lt;span&gt;() 以适当的顺序（从第一个元素到最后一个元素）返回包含此列表中所有元素的数组。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;ArrayList和数组的区别：&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;数组长度固定，集合长度可变&lt;/li&gt;
&lt;li&gt;数组可以存基本数据类型，也可以存引用数据类型，而集合只能存引用数据类型。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2.2.2 Vector实现类&lt;a href=&quot;#222-vector实现类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Vector&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbstractList&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RandomAccess&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cloneable&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; java.io.&lt;/span&gt;&lt;span&gt;Serializable&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;[] elementData&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; elementCount&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; capacityIncrement&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt; serialVersionUID &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span&gt;2767605614048989439L&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Vector&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;span&gt;//默认初始容量是10&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​		Vector是List接口的实现类，首先满足List的特征，&lt;strong&gt;元素有序、可重复&lt;/strong&gt;，Vector是&lt;strong&gt;线程安全&lt;/strong&gt;的，&lt;strong&gt;底层使用数组实现，默认初始容量是10&lt;/strong&gt;，存满后可以按照&lt;strong&gt;自己规定的扩容大小进行扩容默认是2倍扩容&lt;/strong&gt;，扩容源码如下：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newCapacity&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; minCapacity) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// overflow-conscious code&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; oldCapacity &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elementData&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;//新容量大小 = 旧容量大小 + 增长倍数&amp;gt;0? 增长大小:旧容量;  意思是我们可以规定每次扩容多少容量，默认是两倍增长&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; newCapacity &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; oldCapacity &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; ((capacityIncrement &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;capacityIncrement &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; oldCapacity)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (newCapacity &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; minCapacity &lt;/span&gt;&lt;span&gt;&amp;lt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (minCapacity &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;// overflow&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OutOfMemoryError&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; minCapacity&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; (newCapacity &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;MAX_ARRAY_SIZE&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; newCapacity&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hugeCapacity&lt;/span&gt;&lt;span&gt;&lt;span&gt;(minCapacity)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Vector和ArrayList的区别：&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;两者底层都是使用数组实现的，ArrayList的扩容是1.5倍，Vector的扩容可以自己设置，默认是2倍。&lt;/li&gt;
&lt;li&gt;ArrayList是线程不安全的，Vector是线程安全的，因为Vector的主要方法上使用了synchronized修饰。&lt;/li&gt;
&lt;li&gt;ArrayList效率较高，Vector因线程同步，效率较低，已不推荐使用。如果想获取线程同步的ArrayList可以使用Collections工具类里面的同步方法synchronizedList()方法。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2.2.3 LinkedList实现类&lt;a href=&quot;#223-linkedlist实现类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LinkedList&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbstractSequentialList&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;List&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Deque&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cloneable&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; java.io.&lt;/span&gt;&lt;span&gt;Serializable&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; size &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; first&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; last&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;......&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; item&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; next&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; prev&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;prev&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; element;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; next;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;prev&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; prev;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;​		LinkedList是List接口的实现类，首先满足List的特征，&lt;strong&gt;元素有序、可重复&lt;/strong&gt;，LinkedList是&lt;strong&gt;线程不安全&lt;/strong&gt;的，底层使用的是&lt;strong&gt;双向链表&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;LinkedList的常用方法：&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; index&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; element) 将指定元素插入此列表中的指定位置。  &lt;/span&gt;&lt;span&gt;**********&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; e) 将指定的元素追加到此列表的末尾。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;addFirst&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 在此列表的开头插入指定的元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;addLast&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 将指定的元素追加到此列表的末尾。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; o) 如果此列表包含指定的元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;span&gt;*********&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 检索但不删除此列表的头部（第一个元素）。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; index) 返回此列表中指定位置的元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getFirst&lt;/span&gt;&lt;span&gt;() 返回此列表中的第一个元素。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getLast&lt;/span&gt;&lt;span&gt;() 返回此列表中的最后一个元素。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;indexOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt; o) 返回此列表中第一次出现的指定元素的索引，如果此列表不包含该元素，则返回&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lastIndexOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt; o) 返回此列表中指定元素最后一次出现的索引，如果此列表不包含该元素，则返回&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 将指定的元素添加为此列表的尾部（最后一个元素）。  &lt;/span&gt;&lt;span&gt;***************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offerFirst&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; e) 在此列表的前面插入指定的元素。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offerLast&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; e) 在此列表的末尾插入指定的元素。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peek&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 检索但不删除此列表的头部（第一个元素）。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peekFirst&lt;/span&gt;&lt;span&gt;() 检索但不删除此列表的第一个元素，如果此列表为空，则返回 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peekLast&lt;/span&gt;&lt;span&gt;() 检索但不删除此列表的最后一个元素，如果此列表为空，则返回 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;poll&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 检索并删除此列表的头部（第一个元素）。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pollFirst&lt;/span&gt;&lt;span&gt;() 检索并删除此列表的第一个元素，如果此列表为空，则返回 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pollLast&lt;/span&gt;&lt;span&gt;() 检索并删除此列表的最后一个元素，如果此列表为空，则返回 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pop&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 弹出此列表所代表的堆栈中的元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 将元素推送到此列表所表示的堆栈上。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 检索并删除此列表的头部（第一个元素）。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; index) 删除此列表中指定位置的元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt; o) 从该列表中删除指定元素的第一个匹配项（如果存在）。  &lt;/span&gt;&lt;span&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;removeFirst&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 从此列表中删除并返回第一个元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;removeLast&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 从此列表中删除并返回最后一个元素。  &lt;/span&gt;&lt;span&gt;**********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;() 返回此列表中的元素数。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;ArrayList和LinkedList的区别：&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;ArrayList地层使用的是数组，LinkedList底层使用的是双向链表&lt;/li&gt;
&lt;li&gt;在随机位置的元素的访问上面，ArrayList是优于LinkedList，因为数组存储时连续的，可以直接得到存储位置，而链表存储不连续，需要遍历查找。&lt;/li&gt;
&lt;li&gt;对于插入和删除，LinkedList要优于ArrayList，因为链表的插入和删除不需要移动元素，只需要修改链表前后的引用，而数组删除需要移动元素，插入需要重新计算容量大小，如果容量不够还需要扩容。&lt;/li&gt;
&lt;li&gt;LinkedList比ArrayList更占内存，因为链表存储前后节点的引用需要占用一部分内存。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2.2.4 有哪些线程安全的List？&lt;a href=&quot;#224-有哪些线程安全的list&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;Vector类：不建议使用&lt;/li&gt;
&lt;li&gt;Collections.synchronizedList(List引用)方法：每个方法带有同步锁，不是性能最优&lt;/li&gt;
&lt;li&gt;CopyOnWriteArrayList类：性能最优、对内存压力大、GC更频繁，无法保证数据实时性&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;CopyOnWriteArrayList是Java并发包里提供的并发类，简单来说它就是一个线程安全且读操作无锁的ArrayList。正如其名字一样，在写操作时会复制一份新的List，在新的List上完成写操作，然后再将原引用指向新的List。这样就保证了写操作的线程安全。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;CopyOnWriteArrayList允许线程并发访问读操作，这个时候是没有加锁限制的，性能较高。而写操作的时候，则首先将容器复制一份，然后在新的副本上执行写操作，这个时候写操作是上锁的。结束之后再将原容器的引用指向新容器。注意，在上锁执行写操作的过程中，如果有需要读操作，会作用在原容器上。因此上锁的写操作不会影响到并发访问的读操作。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;优点：读操作性能很高，因为无需任何同步措施，比较适用于读多写少的并发场景。在遍历传统的List时，若中途有别的线程对其进行修改，则会抛出ConcurrentModificationException异常。而CopyOnWriteArrayList由于其&quot;读写分离&quot;的思想，遍历和修改操作分别作用在不同的List容器，所以在使用迭代器进行遍历时候，也就不会抛ConcurrentModificationException异常了。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;缺点：一是内存占用问题，毕竟每次执行写操作都要将原容器拷贝一份，数据量大时，对内存压力较大，可能会引起频繁GC。二是无法保证实时性，Vector对于读写操作均加锁同步，可以保证读和写的强一致性。而CopyOnWriteArrayList由于其实现策略的原因，写和读分别作用在新老不同容器上，在写操作执行过程中，读不会阻塞但读取到的却是老容器的数据。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.3 Set接口&lt;a href=&quot;#23-set接口&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Set接口下有两个常用的接口实现类：HashSet、TreeSet&lt;/p&gt;&lt;p&gt;Set接口中定义的方法：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 如果指定的元素尚不存在，则将其添加到此集合（可选操作）。  &lt;/span&gt;&lt;span&gt;***************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; o) 如果此set包含指定的元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。  &lt;/span&gt;&lt;span&gt;*****************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isEmpty&lt;/span&gt;&lt;span&gt;() 如果此集合不包含任何元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 返回此集合中的元素数（基数）。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt; o) 如果存在，则从该集合中移除指定的元素（可选操作）。  &lt;/span&gt;&lt;span&gt;************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;section&gt;&lt;h4&gt;2.3.1 HashSet实现类&lt;a href=&quot;#231-hashset实现类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HashSet&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbstractSet&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Set&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cloneable&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; java.io.&lt;/span&gt;&lt;span&gt;Serializable&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt; serialVersionUID &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span&gt;5024744406713321676L&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HashMap&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; map&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;//HashSet的存储是用的HashMap的Key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; PRESENT &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HashSet&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;map &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HashMap&lt;/span&gt;&lt;span&gt;&amp;lt;&amp;gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​		HashSet是Set接口的实现类，首先满足Set集合的特征，&lt;strong&gt;元素无序&lt;/strong&gt;、&lt;strong&gt;不可重复&lt;/strong&gt;，HashSet是&lt;strong&gt;线程不安全&lt;/strong&gt;的。HashSet的底层使用的&lt;strong&gt;HashMap&lt;/strong&gt;，使用HashMap的key来存储元素，HashMap默认初始容量是16，扩容容量大小为2的幂（也就是2倍扩容）。&lt;/p&gt;&lt;p&gt;​		元素唯一性的保证是通过hashCode()和equals()方法实现的，往一个Set集合存储元素的步骤是：先使用hashCode()方法计算该元素的hash值，利用hash值找到元素存储的位置，判断该位置是否为空，如果为空，那么该元素可以直接插入，如果该位置有元素，那么就调用equals()方法判断是否是同一个元素，如果是，那么就是同一个对象，无需存储，如果不是，那么在HashMap中是使用的链地址法解决哈希冲突，将新的元素链接到当前hashCode值的链表后面。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;哈希冲突解决办法：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;1.开放地址法：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;（1）线性探测：按顺序决定值时，如果某数据的值已经存在，则在原来值的基础上往后加一个单位，直至不发生哈希冲突。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;（2）再平方探测：按顺序决定值时，如果某数据的值已经存在，则在原来值的基础上先加1的平方个单位，若仍然存在则减1的平方个单位。随之是2的平方，3的平方等等。直至不发生哈希冲突。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;（3）伪随机探测：按顺序决定值时，如果某数据已经存在，通过随机函数随机生成一个数，在原来值的基础上加上随机数，直至不发生哈希冲突。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;2.链地址法：对于相同的值，使用链表进行连接。使用数组存储每一个链表。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;3.再哈希法：对于冲突的哈希值再次进行哈希处理，直至没有哈希冲突。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;4.建立公共溢出区：建立公共溢出区存储所有哈希冲突的数据。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;HashSet的常用方法：&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 如果指定的元素尚不存在，则将其添加到此集合（可选操作）。  &lt;/span&gt;&lt;span&gt;************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; o) 如果此set包含指定的元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。  &lt;/span&gt;&lt;span&gt;**************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isEmpty&lt;/span&gt;&lt;span&gt;() 如果此集合不包含任何元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 返回此集合中的元素数（基数）。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt; o) 如果存在，则从该集合中移除指定的元素（可选操作）。  &lt;/span&gt;&lt;span&gt;*********&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2.3.2 TreeSet实现类&lt;a href=&quot;#232-treeset实现类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TreeSet&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbstractSet&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NavigableSet&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cloneable&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; java.io.&lt;/span&gt;&lt;span&gt;Serializable&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NavigableMap&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; m&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; PRESENT &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NavigableSet&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SortedSet&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SortedMap&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​		TreeSet是Set接口的实现类，首先满足Set集合的特征，&lt;strong&gt;元素无序&lt;/strong&gt;、&lt;strong&gt;不可重复&lt;/strong&gt;，TreeSet是&lt;strong&gt;线程不安全&lt;/strong&gt;的。TreeSet实现了NavigableSet接口，NavigableSet接口继承于SortedSet接口，TreeSet内部实现了比较器，所以&lt;strong&gt;存储元素有序&lt;/strong&gt;。底层基于&lt;strong&gt;TreeMap实现&lt;/strong&gt;， 元素唯一且已经排好序；唯一性同样需要重写hashCode和equals()方法 。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;TreeSet的常用方法：&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 如果指定的元素尚不存在，则将其添加到此集合（可选操作）。  &lt;/span&gt;&lt;span&gt;**************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; o) 如果此set包含指定的元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。  &lt;/span&gt;&lt;span&gt;****************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ceiling&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; e) 返回此set中大于或等于给定元素的 null元素，如果没有这样的元素，则 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;floor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; e) 返回此set中小于或等于给定元素的最大元素，如果没有这样的元素，则 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;higher&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; e) 返回此集合中的最小元素严格大于给定元素，如果没有这样的元素，则 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lower&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; e) 返回此集合中的最大元素严格小于给定元素，如果没有这样的元素，则 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isEmpty&lt;/span&gt;&lt;span&gt;() 如果此集合不包含任何元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;() 返回此集合中当前的第一个（最低）元素。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;last&lt;/span&gt;&lt;span&gt;() 返回此集合中当前的最后一个（最高）元素。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pollFirst&lt;/span&gt;&lt;span&gt;() 检索并删除第一个（最低）元素，如果此组为空，则返回 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pollLast&lt;/span&gt;&lt;span&gt;() 检索并删除最后一个（最高）元素，如果此集合为空，则返回 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 返回此集合中的元素数（基数）。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt; o) 如果存在，则从该集合中移除指定的元素（可选操作）。  &lt;/span&gt;&lt;span&gt;*********&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2.3.3 TreeSet和HashSet的区别&lt;a href=&quot;#233-treeset和hashset的区别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;TreeSet和HashSet都是Set接口下的实现类，都是元素无序且唯一，都是线程不安全的，区别是：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;HashSet是基于HashMap实现的（Node数组+链表+红黑树），TreeSet底层基于TreeMap实现的（红黑树）。&lt;/li&gt;
&lt;li&gt;HashSet是不能保证元素的排列顺序，而TreeSet支持自然排序、定值排序。&lt;/li&gt;
&lt;li&gt;HashSet可以存储null值，但是TreeSet不可以存null值。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.4 Queue接口&lt;a href=&quot;#24-queue接口&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Queue接口下有三种常用的接口实现类：ArrayDeque、LinkedList、PriorityQueue&lt;/p&gt;&lt;p&gt;Queue接口中定义的方法：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; e) 如果可以在不违反容量限制的情况下立即执行此操作，则将指定的元素插入此队列，成功时返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; ，如果当前没有空间，则抛出 IllegalStateException 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt;() 检索但不删除此队列的头部。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; e) 如果可以在不违反容量限制的情况下立即执行此操作，则将指定的元素插入此队列。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peek&lt;/span&gt;&lt;span&gt;() 检索但不删除此队列的头部，如果此队列为空，则返回 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;poll&lt;/span&gt;&lt;span&gt;() 检索并删除此队列的头部，如果此队列为空，则返回 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;() 检索并删除此队列的头部。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;section&gt;&lt;h4&gt;2.4.1 ArrayDeque实现类&lt;a href=&quot;#241-arraydeque实现类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ArrayDeque&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbstractCollection&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                           &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Deque&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cloneable&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Serializable&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;[] elements&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; head&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; tail&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; MAX_ARRAY_SIZE &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Integer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MAX_VALUE&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ArrayDeque&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elements &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;16&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;span&gt;//初始化容量是16&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;......&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newCapacity&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;needed&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;jump&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;oldCapacity&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;elements&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;, minCapacity;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; ((minCapacity &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; oldCapacity &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; needed) &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; MAX_ARRAY_SIZE &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (minCapacity &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IllegalStateException&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Sorry, deque too big&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Integer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MAX_VALUE&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (needed &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; jump)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; minCapacity;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; (oldCapacity &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; jump &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;MAX_ARRAY_SIZE&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&lt;span&gt; oldCapacity &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; jump&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; MAX_ARRAY_SIZE;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;​		底层是用&lt;strong&gt;循环数组&lt;/strong&gt;实现的双端队列，初始默认容量是&lt;strong&gt;16&lt;/strong&gt;，扩容是&lt;strong&gt;2倍&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​		普通数组只能快速在末尾添加元素，为了支持FIFO，从数组头快速取出元素，就需要使用循环数组：有队头队尾两个下标：弹出元素时，队头下标递增；加入元素时，如果已到数组空间的末尾，则将元素循环赋值到数组0，同时队尾下标指向0，再插入下一个元素则赋值到数组[1]，队尾下标指向1。如果队尾的下标追上队头，说明数组所有空间已用完，进行双倍的数组扩容。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;ArrayDeque的常用方法：&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 在此双端队列的末尾插入指定的元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;addFirst&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 在此双端队列的前面插入指定的元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;addLast&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 在此双端队列的末尾插入指定的元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; o) 如果此双端队列包含指定的元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。  &lt;/span&gt;&lt;span&gt;************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 检索但不删除此双端队列表示的队列的头部。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getFirst&lt;/span&gt;&lt;span&gt;() 检索但不删除此双端队列的第一个元素。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getLast&lt;/span&gt;&lt;span&gt;() 检索但不删除此双端队列的最后一个元素。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isEmpty&lt;/span&gt;&lt;span&gt;() 如果此双端队列不包含任何元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。 &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 在此双端队列的末尾插入指定的元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offerFirst&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; e) 在此双端队列的前面插入指定的元素。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offerLast&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; e) 在此双端队列的末尾插入指定的元素。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;peek&lt;/span&gt;&lt;span&gt;() 检索但不删除此双端队列表示的队列的头部，如果此双端队列为空，则返回 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。  &lt;/span&gt;&lt;span&gt;*******&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;poll&lt;/span&gt;&lt;span&gt;() 检索并删除此双端队列表示的队列的头部（换句话说，此双端队列的第一个元素），如果此双端队列为空，则返回 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;&lt;span&gt; 。 &lt;/span&gt;&lt;span&gt;******&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pop&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 从此双端队列表示的堆栈中弹出一个元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt; e) 将元素推送到此双端队列表示的堆栈上。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 检索并删除此双端队列表示的队列的头部。  &lt;/span&gt;&lt;span&gt;**********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;removeFirst&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 检索并删除此双端队列的第一个元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;removeLast&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 检索并删除此双端队列的最后一个元素。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 返回此双端队列中的元素数。  &lt;/span&gt;&lt;span&gt;***********************&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2.4.2 LinkedList实现类&lt;a href=&quot;#242-linkedlist实现类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​		LinkedList是Queue接口的实现类，LinkedList是&lt;strong&gt;线程不安全&lt;/strong&gt;的，底层使用的是&lt;strong&gt;双向链表&lt;/strong&gt;，类的具体定义见2.2.3&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2.4.3 PriorityQueue实现类&lt;a href=&quot;#243-priorityqueue实现类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PriorityQueue&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbstractQueue&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; java.io.&lt;/span&gt;&lt;span&gt;Serializable&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt; serialVersionUID &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span&gt;7720805057305804111L&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; DEFAULT_INITIAL_CAPACITY &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;11&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;[] queue&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// non-private to simplify nested class access&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; size&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Comparator&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;super&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; comparator&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; modCount&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// non-private to simplify nested class access&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PriorityQueue&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;(DEFAULT_INITIAL_CAPACITY, &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;......&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;grow&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;minCapacity&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;oldCapacity&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;queue&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// Double size if small; else grow by 50%&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newCapacity&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; oldCapacity &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; ((oldCapacity &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;64&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(oldCapacity &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(oldCapacity &lt;/span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// overflow-conscious code&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (newCapacity &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; MAX_ARRAY_SIZE &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;newCapacity &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hugeCapacity&lt;/span&gt;&lt;span&gt;(minCapacity);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;queue &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Arrays&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;copyOf&lt;/span&gt;&lt;span&gt;(queue, newCapacity);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;​		PriorityQueue是Queue接口的实现类，PriorityQueue是&lt;strong&gt;线程不安全&lt;/strong&gt;的，底层使用的是&lt;strong&gt;数组实现的二叉小根堆&lt;/strong&gt;，可以保证每次取出的值都是最小的，我们也可以在构造时传入比较器，改变排序方式。数组的初始容量是11，扩容的话，如果当前容量小于64，则按两倍扩容，如果大于64则按1.5倍扩容。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;PriorityQueue常用方法：&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; e) 将指定的元素插入此优先级队列。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt;() 检索但不删除此队列的头部。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; o) 如果此队列包含指定的元素，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;offer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; e) 将指定的元素插入此优先级队列。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;E&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;poll&lt;/span&gt;&lt;span&gt;() 检索并删除此队列的头部，如果此队列为空，则返回 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; o) 从此队列中删除指定元素的单个实例（如果存在）。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.5 Map接口&lt;a href=&quot;#25-map接口&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Map接口下有三种常用的接口实现类：HashMap、Hashtable、TreeMap&lt;/p&gt;&lt;p&gt;Map接口中定义的方法：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;containsKey&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; key) 如果此映射包含指定键的映射，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;containsValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; value) 如果此映射将一个或多个键映射到指定值，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Set&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;entrySet&lt;/span&gt;&lt;span&gt;() 返回此映射中包含的映射的Set视图。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; key) 返回指定键映射到的值，如果此映射不包含键的映射，则返回 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getOrDefault&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt; key&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&lt;span&gt; defaultValue) 返回指定键映射到的值&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;如果此映射不包含键的映射&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;则返回defaultValue&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isEmpty&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 如果此映射不包含键 &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 值映射，则返回 &lt;/span&gt;&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Set&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;keySet&lt;/span&gt;&lt;span&gt;() 返回此映射中包含的键的Set视图。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;put&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;&lt;span&gt; key&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt; value) 将指定的值与此映射中的指定键相关联（可选操作）。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; key) 如果存在，则从该映射中移除键的映射（可选操作）。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt; key&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; value) 仅当指定键当前映射到指定值时才删除该条目的条目。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;&lt;span&gt; key&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt; value) 仅当指定键当前映射到某个值时，才替换该条目的条目。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;&lt;span&gt; key&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&lt;span&gt; oldValue&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt; newValue) 仅当前映射到指定值时，才替换指定键的条目。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 返回此映射中键 &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 值映射的数量。&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;section&gt;&lt;h4&gt;2.5.1 HashMap实现类&lt;a href=&quot;#251-hashmap实现类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HashMap&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbstractMap&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cloneable&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Serializable&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt; serialVersionUID &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;362498820763181265L&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; DEFAULT_INITIAL_CAPACITY &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// aka 16  初始容量&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; MAXIMUM_CAPACITY &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt; DEFAULT_LOAD_FACTOR &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0.75f&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//默认装载因子&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; TREEIFY_THRESHOLD &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//链表树化的链表长度&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; UNTREEIFY_THRESHOLD &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//树转链表的树的节点数&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; MIN_TREEIFY_CAPACITY &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;64&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;//链表转树时，数组最低长度&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;//Node节点结构，我们的数据都存储在Node节点中&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; hash&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt; key&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; next&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hash&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;hash&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; hash;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; key;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; value;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; next;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;[] table&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;//Node数组，用于存储数据&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Set&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; entrySet&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; size&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//节点数目&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; modCount&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; threshold&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//扩容门限=装载因子*容量大小&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt; loadFactor&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;//装载因子&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;//构造方法&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HashMap&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;initialCapacity&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;loadFactor&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (initialCapacity &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IllegalArgumentException&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Illegal initial capacity: &quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                               &lt;/span&gt;&lt;/span&gt;&lt;span&gt;initialCapacity);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (initialCapacity &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; MAXIMUM_CAPACITY)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;initialCapacity &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; MAXIMUM_CAPACITY;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (loadFactor &lt;/span&gt;&lt;span&gt;&amp;lt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Float&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;isNaN&lt;/span&gt;&lt;span&gt;(loadFactor))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IllegalArgumentException&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Illegal load factor: &quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                               &lt;/span&gt;&lt;/span&gt;&lt;span&gt;loadFactor);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;loadFactor&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; loadFactor;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;threshold&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tableSizeFor&lt;/span&gt;&lt;span&gt;(initialCapacity);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;在jdk1.7中&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		HashMap底层采用的是Entry数组+链表的实现，即使用链表来处理哈希冲突，相同hashCode值的的节点都存在一个链表里，但是当某一个位置的冲突较多时，依次通过key值查找效率依然很低。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;HashMap&quot; loading=&quot;lazy&quot; width=&quot;922&quot; height=&quot;384&quot; src=&quot;/_astro/HashMap.BIMT5sGQ_1iyv0P.webp&quot; srcset=&quot;/_astro/HashMap.BIMT5sGQ_22EuvP.webp 640w, /_astro/HashMap.BIMT5sGQ_ZW8zUA.webp 750w, /_astro/HashMap.BIMT5sGQ_1QllwI.webp 828w, /_astro/HashMap.BIMT5sGQ_1iyv0P.webp 922w&quot; /&gt;&lt;figcaption&gt;HashMap&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;在jdk1.8中的优化&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	HashMap底层采用的是Node数组+链表+红黑树的实现，同样使用链地址法来 处理哈希冲突，只不过当链表长度达到8时，就会将链表转换成红黑树，大大减少了查找时间。当链表数组的容量超过初始容量的0.75时，再散列将链表数组扩大2倍，把原链表数组的搬移到新的数组中。&lt;/p&gt;&lt;p&gt;**总结：**HashMap底层采用的是Node数组+链表+红黑树的实现，可以存储null。初始容量是16，加载因子是0.75，当存储的容量达到0.75时扩容，扩容是两倍。HashMap是线程不安全的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;HashMap的扩容机制：&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;数组的初始容量为16，而容量是以2的次方扩充的，一是为了提高性能使用足够大的数组，二是为了能使用位运算代替取模预算(据说提升了5~8倍)。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;数组是否需要扩充是通过负载因子判断的，如果当前元素个数为数组容量的0.75倍时，就会扩充数组。这个0.75就是默认的负载因子，可由构造器传入。我们也可以设置大于1的负载因子，这样数组就不会扩充，牺牲性能，节省内存。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;为了解决碰撞，数组中的元素是单向链表类型。当链表长度到达一个阈值时（8），会将链表转换成红黑树提高性能。而当链表长度缩小到另一个阈值时（6），又会将红黑树转换回单向链表提高性能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于第三点补充说明，检查链表长度转换成红黑树之前，还会先检测当前数组数组是否到达一个阈值（64），如果没有到达这个容量，会放弃转换，先去扩充数组。所以上面也说了链表长度的阈值是7或8，因为会有一次放弃转换的操作。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;HashMap的put(key,value)和get(key)操作：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	get(key)操作：调用hashCode()根据key计算出hash值，从而找到对象存储位置，然后调用equals()方法在该位置上的链表一直判断，直到找到key相同的对象，返回value。&lt;/p&gt;&lt;p&gt;​	put(key,value)操作：先判断Node数组是否为null，如果为null则先扩容，扩容后调用hashCode()根据key计算出hash值，从而找到对象存储的位置，判断该位置是否为null，如果为null，直接插入，如果不为null，调用equals()挨个判断该hash值链接的链表或者红黑树，如果有key相同的则直接覆盖，没有相同的则插入链表或者红黑树，如果插入节点后链表长度达到8，那么要对链表进行树化操作。&lt;/p&gt;&lt;p&gt;​	树化操作：链表节点数达到8时，会调用树化的方法，先判断Node数组的长度有没有达到64，如果没有达到64，那么执行扩容，放弃树化，如果Node数组的长度达到64，那么执行树化操作，将链表转化成红黑树。&lt;/p&gt;&lt;p&gt;树化操作代码如下：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;treeifyBin&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;[] tab&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; hash) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; n&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; index&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Node&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; e&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (tab &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; (n &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tab&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; MIN_TREEIFY_CAPACITY)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;resize&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;//扩容&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; ((e &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; tab[index &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (n &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt; hash]) &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;TreeNode&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; hd &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; tl &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;TreeNode&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; p &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;replacementTreeNode&lt;/span&gt;&lt;span&gt;&lt;span&gt;(e&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (tl &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hd &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; p&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;prev&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; tl&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;tl&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; p&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tl &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; p&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt;&lt;span&gt; ((e &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; ((tab[index] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; hd) &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;hd&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;treeify&lt;/span&gt;&lt;span&gt;(tab);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;hashmap为什么在链表长度为8的时候变为红黑树?为什么是8？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		源码中的注释写的很清楚，因为树节点所占空间是普通节点的两倍，所以只有当节点足够多的时候，才会使用树节点。也就是说，节点少的时候，尽管时间复杂度上，红黑树比链表好一点，但是红黑树所占空间比较大，综合考虑，认为只能在节点太多的时候，红黑树占空间大这一劣势不太明显的时候，才会舍弃链表，使用红黑树。&lt;/p&gt;&lt;p&gt;​		源码上说，为了配合使用分布良好的hashCode，树节点很少使用。并且在理想状态下，受随机分布的hashCode影响，链表中的节点遵循泊松分布，而且根据统计，链表中节点数是8的概率已经接近千分之一，而且此时链表的性能已经很差了。所以在这种比较罕见和极端的情况下，才会把链表转变为红黑树。因为链表转换为红黑树也是需要消耗性能的，特殊情况特殊处理，为了挽回性能，权衡之下，才使用红黑树，提高性能。也就是大部分情况下，hashmap还是使用的链表，如果是理想的均匀分布，节点数不到8，hashmap就自动扩容了。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;HashMap的线程安全问题&lt;/strong&gt;：HashMap是线程不安全的，在多线程并发访问的时候会出现死循环、数据丢失、数据覆盖的问题。&lt;/p&gt;&lt;p&gt;​	在jdk1.7中，在多线程访问的情况下，&lt;strong&gt;并发执行put操作时，如果这时候刚好HashMap需要扩容&lt;/strong&gt;，两个线程同时对HashMap进行扩容，在内存中会存在两个HashMap的Node数组，如果一个线程对链表节点的处理结束但还没有重新设置HashMap的引用，另一个线程又对链表就行设置就会出现死循环的现象，这是由于链表插入采用的是头插法，会形成环，&lt;/p&gt;&lt;p&gt;​	在jdk1.8中使用的尾插法，解决了这个头插法产生的弊端，但是如果&lt;strong&gt;多线程访问需要对链表转换成树或者对树进行操作可能出现死循环&lt;/strong&gt;的问题。&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;数据覆盖&lt;/strong&gt;：假如说两个线程A、B都在进行put操作，并且由hash值算出来的数据下标都一样，这样就出现了碰撞，假设线程A在执行到的那一行由于时间片用完了，线程B能够正常的放进去（因为A还没有将数据放进去），但是当轮到线程A执行的时候，由于之前已经判断过了，此时会直接覆盖掉线程B存放的数据，导致数据覆盖还有一个地方就是++size这行代码，由于它不是原子操作，当A+1操作后还未写会主存，由于时间片用完，线程B拿到的还是原来的size，也就是说A、B都+1了，加了两次，实际上只有1次，从而造成线程的不安全。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;HashMap为什么采用红黑树而不是AVL树、B树或者B+树？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		平衡二叉树和红黑树的查找时间相差不多，时间复杂度都为O(lgn)，但是AVL树更加强调平衡，为了达到平衡多了很多树的调整操作。但实际上AVL树在查找密集型任务上更快：利用更好的平衡，树遍历平均更短。另一方面，插入和删除方面，AVL树速度较慢：需要更高的旋转次数才能在修改时正确地重新平衡数据结构。红黑树和AVL都是O（log n）查找，但平衡AVL树可能需要O（log n）旋转，而红黑树将需要最多两次旋转使其达到平衡（尽管可能需要检查O（log n）节点以确定旋转的位置）。旋转本身是O（1）操作，因为你只是移动指针。&lt;/p&gt;&lt;p&gt;​		B/B+树多用于外存上时，B/B+也被成为一个磁盘友好的数据结构。HashMap本来是Node数组+链表的形式，链表由于其查找慢的特点，所以需要被查找效率更高的树结构来替换。如果用B/B+树的话，在数据量不是很多的情况下，数据都会“挤在”一个结点里面，这个时候遍历效率就退化成了链表。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;HashMap的常用方法：&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;containsKey&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; key) 如果此映射包含指定键的映射，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;containsValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; value) 如果此映射将一个或多个键映射到指定值，则返回 &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Set&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;entrySet&lt;/span&gt;&lt;span&gt;() 返回此映射中包含的映射的Set视图。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; key) 返回指定键映射到的值，如果此映射不包含键的映射，则返回 &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isEmpty&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 如果此映射不包含键 &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 值映射，则返回 &lt;/span&gt;&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; 。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Set&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;keySet&lt;/span&gt;&lt;span&gt;() 返回此映射中包含的键的Set视图。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;put&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;&lt;span&gt; key&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt; value) 将指定的值与此映射中的指定键相关联。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; key) 从此映射中删除指定键的映射（如果存在）。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;&lt;span&gt;() 返回此映射中键 &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 值映射的数量。&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2.5.2 Hashtable实现类&lt;a href=&quot;#252-hashtable实现类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Hashtable&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dictionary&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cloneable&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; java.io.&lt;/span&gt;&lt;span&gt;Serializable&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;[] table&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; count&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; threshold&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt; loadFactor&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; modCount &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt; serialVersionUID &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1421746759512286392L&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Hashtable&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;11&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.75f&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​		Hashtable底层是使用的&lt;strong&gt;Entry数组+链表&lt;/strong&gt;的实现，初始容量是&lt;strong&gt;11&lt;/strong&gt;，默认加载因子是&lt;strong&gt;0.75&lt;/strong&gt;，扩容是原来容量的&lt;strong&gt;2倍加1&lt;/strong&gt;（&lt;strong&gt;新容量=旧容量 * 2 + 1&lt;/strong&gt;）。Hashtable是&lt;strong&gt;线程安全&lt;/strong&gt;的，因为在其主要的方法上都加了synchronized关键字进行同步。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Hashtable和HashMap的区别：&lt;/strong&gt;*&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Hashtable底层是使用的&lt;strong&gt;Entry数据+链表&lt;/strong&gt;的实现，初始容量是&lt;strong&gt;11&lt;/strong&gt;，扩容是原来容量的&lt;strong&gt;2倍加1&lt;/strong&gt;，HashMap底层采用的是&lt;strong&gt;Node数组+链表+红黑树&lt;/strong&gt;的实现，初始容量是&lt;strong&gt;16&lt;/strong&gt;，扩容是&lt;strong&gt;两倍&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hashtable是一个线程安全的Map实现，但HashMap是线程不安全的实现，所以HashMap比Hashtable的性能高一点。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hashtable不允许使用null作为key和value，如果试图把null值放进Hashtable中，将会引发空指针异常，但HashMap可以使用null作为key或value。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2.5.3 TreeMap实现类&lt;a href=&quot;#253-treemap实现类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TreeMap&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbstractMap&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NavigableMap&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Cloneable&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; java.io.&lt;/span&gt;&lt;span&gt;Serializable&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Comparator&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;super&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; comparator&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;V&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; root&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; size &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;transient&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; modCount &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​		TreeMap底层是使用的红黑树实现的，线程不安全，实现SortedMap，支持遍历时按元素的大小有序遍历。该集合最重要的特点就是可排序，该映射根据其键的自然顺序进行排序，或者根据创建映射时提供的 Comparator比较器进行排序，具体取决于使用的构造方法。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2.5.4 如何得到一个线程安全的Map？&lt;a href=&quot;#254-如何得到一个线程安全的map&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;使用Collections工具类，将线程不安全的Map包装成线程安全的Map； Collections.synchronizedMap(map);&lt;/li&gt;
&lt;li&gt;使用&lt;strong&gt;j&lt;/strong&gt;ava.&lt;strong&gt;u&lt;/strong&gt;til.&lt;strong&gt;c&lt;/strong&gt;oncurrent(&lt;strong&gt;JUC&lt;/strong&gt;)包下的Map，如ConcurrentHashMap；&lt;/li&gt;
&lt;li&gt;不建议使用Hashtable，虽然Hashtable是线程安全的，但是性能较差。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2.5.5 JUC下的ConcurrentHashMap介绍&lt;a href=&quot;#255-juc下的concurrenthashmap介绍&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;JDK 1.7中的实现：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		在 jdk 1.7 中，ConcurrentHashMap 是由 Segment 数据结构和 HashEntry 数组结构构成，采取分段锁来保证安全性。Segment 是 ReentrantLock 重入锁，在 ConcurrentHashMap 中扮演锁的角色，HashEntry 则用于存储键值对数据。一个 ConcurrentHashMap 里包含一个 Segment 数组，一个Segment 里包含一个 HashEntry 数组，Segment 的结构和 HashMap 类似，是一个数组和链表结构。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;ConcurrentHashMap-jdk1.7&quot; loading=&quot;lazy&quot; width=&quot;767&quot; height=&quot;423&quot; src=&quot;/_astro/ConcurrentHashMap-jdk1.7.B9iElmR7_1mV5Qi.webp&quot; srcset=&quot;/_astro/ConcurrentHashMap-jdk1.7.B9iElmR7_ZMqcSb.webp 640w, /_astro/ConcurrentHashMap-jdk1.7.B9iElmR7_oRH4D.webp 750w, /_astro/ConcurrentHashMap-jdk1.7.B9iElmR7_1mV5Qi.webp 767w&quot; /&gt;&lt;figcaption&gt;ConcurrentHashMap-jdk1.7&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;JDK 1.8中的实现：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		JDK1.8 的实现已经摒弃了 Segment 的概念，而是直接用 Node 数组+链表+红黑树的数据结构来实现，并发控制使用&lt;strong&gt;CAS+synchronized+volatile&lt;/strong&gt;来操作，整个看起来就像是优化过且线程安全的HashMap&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;ConcurrentHashMap-jdk1.8&quot; loading=&quot;lazy&quot; width=&quot;768&quot; height=&quot;261&quot; src=&quot;/_astro/ConcurrentHashMap-jdk1.8.DQCpOuor_24tQj2.webp&quot; srcset=&quot;/_astro/ConcurrentHashMap-jdk1.8.DQCpOuor_1MKA5V.webp 640w, /_astro/ConcurrentHashMap-jdk1.8.DQCpOuor_1H2nR8.webp 750w, /_astro/ConcurrentHashMap-jdk1.8.DQCpOuor_24tQj2.webp 768w&quot; /&gt;&lt;figcaption&gt;ConcurrentHashMap-jdk1.8&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;ConcurrentHashMap的put()和get()操作：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;get()操作：&lt;/strong&gt; ConcurrentHashMap 同样是通过Key的哈希值与数组长度取模确定该Key在数组中的索引。如果头节点的 hash 值小于 0，说明正在扩容或者该节点是红黑树。为了避免不太好的Key的hashCode设计，它通过如下方法计算得到Key的最终哈希值。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;spread&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; h) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; (h &lt;/span&gt;&lt;span&gt;^&lt;/span&gt;&lt;span&gt; (h &lt;/span&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;16&lt;/span&gt;&lt;span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt; HASH_BITS&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;**put()操作：**ConcurrentHashMap 是通过Key的哈希值与数组长度取模确定该Key在数组中的索引，第一步先判断数组是不是空，如果为空先进行数组初始化过程，然后根据索引查看当前位置是否为空，如果为空用使用 CAS 操作将这个新值放入这个位置，如果不为空，首先判断该位置的首个元素的hash 值如果等于 MOVED ，说明在扩容，帮助数据迁移     helpTransfer，否则获取synchronized 监视器锁，然后在该索引下的链表遍历，如果有key相等直接覆盖，如果都不相等，在链表的结尾添加该对象，然后根据链表的长度判断链表是否需要树化。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;static final int MOVED     = -1; // hash for forwarding nodes 扩容，正在转移结点&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;static final int TREEBIN   = -2; // hash for roots of trees   是红黑树节点&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;static final int RESERVED  = -3; // hash for transient reservations&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash 用于计算最终hash值&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;ConcurrentHashMap-jdk1.8-put&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;695&quot; src=&quot;/_astro/ConcurrentHashMap-jdk1.8-put.B9BCeYvM_Z2ioxgD.webp&quot; srcset=&quot;/_astro/ConcurrentHashMap-jdk1.8-put.B9BCeYvM_ZdaLmo.webp 640w, /_astro/ConcurrentHashMap-jdk1.8-put.B9BCeYvM_Z2gMUMF.webp 750w, /_astro/ConcurrentHashMap-jdk1.8-put.B9BCeYvM_csjil.webp 828w, /_astro/ConcurrentHashMap-jdk1.8-put.B9BCeYvM_Z2ioxgD.webp 979w&quot; /&gt;&lt;figcaption&gt;ConcurrentHashMap-jdk1.8-put&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;HashMap和ConcurrentHashMap的区别：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		HashMap是非线程安全的，这意味着不应该在多线程中对这些Map进行修改操作，否则会产生数据不一致的问题，甚至还会因为并发插入元素而导致链表成环，这样在查找时就会发生死循环，影响到整个应用程序。&lt;/p&gt;&lt;p&gt;​		Collections工具类可以将一个Map转换成线程安全的实现，其实也就是通过一个包装类，然后把所有功能都委托给传入的Map，而包装类是基于synchronized关键字来保证线程安全的（Hashtable也是基于synchronized关键字），底层使用的是互斥锁，性能与吞吐量比较低。&lt;/p&gt;&lt;p&gt;​		ConcurrentHashMap的实现细节远没有这么简单，因此性能也要高上许多。它没有使用一个全局锁来锁住自己，而是采用了减少锁粒度的方法，尽量减少因为竞争锁而导致的阻塞与冲突，而且ConcurrentHashMap的检索操作是不需要锁的。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;3、多线程&lt;a href=&quot;#3多线程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;进程：是操作系统资源分配的基本单位，比如内存、打开文件、网络IO，分配了独立的内存空间&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;线程：是操作系统资源调度的基本单位，cpu分配的基本单位&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;纤程：（协程）是用户态的线程，是线程中的线程，切换和调度不需要经过OS(操作系统)。；轻量级的线程 - 线程&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;串行：一个一个排队执行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;并行：是指两个或者多个事件在同一时刻发生；而并发是指两个或多个事件在同一时间间隔发生。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;并发：关键是你有处理多个任务的能力，不一定要同时。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;线程和进程的区别总结：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;1、调度：线程作为调度和分配的基本单位，进程作为拥有资源的基本单位；&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;2、并发性：不仅进程之间可以并发执行，同一个进程的多个线程之间也可并发执行；&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;3、拥有资源：进程是拥有资源的一个独立单位，线程不拥有系统资源，但可以访问隶属于进程的资源。进程所维护的是程序所包含的资源（静态资源），线程所维护的运行相关的资源（动态资源）。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;4、系统开销：在创建或撤消进程时，由于系统都要为之分配和回收资源，导致系统的开销明显大于创建或撤消线程时的开销。但是进程有独立的地址空间，一个进程崩溃后，在保护模式下不会对其它进程产生影响，而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量，但线程之间没有单独的地址空间，一个进程死掉就等于所有的线程死掉，所以多进程的程序要比多线程的程序健壮，但在进程切换时，耗费资源较大，效率要差一些。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;纤程的优势：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1.占有的资源少，为什么说他占有资源少呢？举例：操作系统要启一个线程前后要为这个线程配的内存数据大概有1M，而纤程大概是4K&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;2.由于纤程非常的轻量级，所以切换比较简单&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;3.可以同时被启动很多个（10万个都没问题）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;进程间的通信方式：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1.管道&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;2.信号量&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;3.共享内存&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;4.套接字socket&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;线程间的通信方式：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1.消息队列&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;2.使用信号量&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;3.互斥&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;4.非阻塞CAS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;section&gt;&lt;h3&gt;3.1 怎么创建一个线程？&lt;a href=&quot;#31-怎么创建一个线程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;3.1.1 继承Thread&lt;a href=&quot;#311-继承thread&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;通过Thread继承类来创建并启动线程的步骤：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;定义Thread类的子类，重写run()方法，（run()方法里面试线程执行的任务）run()方法无返回值。&lt;/li&gt;
&lt;li&gt;创建Thread子类的实例对象&lt;/li&gt;
&lt;li&gt;调用Thread子类实例的start()方法启动线程。&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MyThread&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Thread&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;(){&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;//线程执行体&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;//使用MyThread&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Thread&lt;/span&gt;&lt;span&gt; t &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MyThread&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;&lt;span&gt;();&lt;/span&gt;&lt;span&gt;                      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3.1.2 实现Runnable接口&lt;a href=&quot;#312-实现runnable接口&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;定义一个Runnable接口的实现类，重写run()方法。run()方法无返回值。&lt;/li&gt;
&lt;li&gt;创建一个Runnable接口实现类的实例对象&lt;/li&gt;
&lt;li&gt;将Runnable接口实现类的实例对象作为Target用来创建一个Thread对象&lt;/li&gt;
&lt;li&gt;调用Thread对象的start()方法启动线程。&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MyRunnable&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Runnable&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;//线程执行体&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;//使用MyRunnable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;MyRunnable&lt;/span&gt;&lt;span&gt; myRunnable &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MyRunable&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Thread&lt;/span&gt;&lt;span&gt; t &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Thread&lt;/span&gt;&lt;span&gt;&lt;span&gt;(myRunnable)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;&lt;span&gt;();&lt;/span&gt;&lt;span&gt;                              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3.1.3 实现Callable接口&lt;a href=&quot;#313-实现callable接口&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;定义一个Callable接口的实现类，重写cal()方法。其中call()方法是有返回值的。&lt;/li&gt;
&lt;li&gt;创建Callable接口实现类的实例对象&lt;/li&gt;
&lt;li&gt;将Callable接口实现类的实例对象作为参数，创建一个Futuretask实例对象，FutureTask封装了call()方法的返回值。&lt;/li&gt;
&lt;li&gt;将FutureTask的实例对象作为Target用来创建一个Thread对象&lt;/li&gt;
&lt;li&gt;调用Thread对象的start()方法启动线程。&lt;/li&gt;
&lt;li&gt;调用FutureTask实例对象的get()方法获取子线程执行结束的返回值&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MyCallable&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Callable&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;call&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;throws&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Exception&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;//线程执行体&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; 线程执行完需要返回的值;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;//使用MyCallable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;MyCallable&lt;/span&gt;&lt;span&gt; myCallable &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MyCallable&lt;/span&gt;&lt;span&gt;()     &lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;FutureTask futureTask &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FutureTask&lt;/span&gt;&lt;span&gt;&lt;span&gt;(myCallable)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Thread&lt;/span&gt;&lt;span&gt; t &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Thread&lt;/span&gt;&lt;span&gt;&lt;span&gt;(futureTask)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;&lt;span&gt;();&lt;/span&gt;&lt;span&gt;                                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;线程执行完返回值&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span&gt;futureTask&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;span&gt;//6.执行get()会抛出异常InterruptedException,ExecutionException,需要使用try catch处理&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3.1.4 使用线程池创建线程&lt;a href=&quot;#314-使用线程池创建线程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​		从Java 5开始，Java内建支持线程池。Java 5新增了一个Executors工厂类来产生线程池，该工厂类包含如下几个静态工厂方法来创建线程池。创建出来的线程池，都是通过ThreadPoolExecutor类来实现的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;newCachedThreadPool()&lt;/strong&gt;：创建一个具有缓存功能的线程池，系统根据需要创建线程，这些线程将会被缓存在线程池中。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;newFixedThreadPool(int nThreads)&lt;/strong&gt;：创建一个可重用的、具有固定线程数的线程池。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;newSingleThreadExecutor()&lt;/strong&gt;：创建一个只有单线程的线程池，它相当于调用newFixedThreadPool()方法时传入参数为1。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;newScheduledThreadPool(int corePoolSize)&lt;/strong&gt;：创建具有指定线程数的线程池，它可以在指定延迟后执行线程任务。corePoolSize指池中所保存的线程数，即使线程是空闲的也被保存在线程池内。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;newSingleThreadScheduledExecutor()&lt;/strong&gt;：创建只有一个线程的线程池，它可以在指定延迟后执行线程任务。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;ExecutorService newWorkStealingPool(int parallelism)&lt;/strong&gt;：创建持有足够的线程的线程池来支持给定的并行级别，该方法还会使用多个队列来减少竞争。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;ExecutorService newWorkStealingPool()&lt;/strong&gt;：该方法是前一个方法的简化版本。如果当前机器有4个CPU，则目标并行级别被设置为4，也就是相当于为前一个方法传入4作为参数。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;//使用ThreadPoolExecutor&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span&gt; threadPoolExecutor &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;1L&lt;/span&gt;&lt;span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TimeUnit&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;SECONDS&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LinkedBlockingQueue&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Runnable&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt;//队列&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;&lt;span&gt; ThreadPoolExecutor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;CallerRunsPolicy&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;span&gt;//线程创建工厂&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbortPolicy&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;//拒绝策略&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;//创建一个线程池&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Runnable&lt;/span&gt;&lt;span&gt; worker &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MyRunnable&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;i)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;threadPoolExecutor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(worker);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;threadPoolExecutor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;shutdown&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;/span&gt;&lt;span&gt;threadPoolExecutor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;isTerminated&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;){}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Finished all threads&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;//使用Executors工厂类中的方法来创建线程，Executors内部其实也是调用的ThreadPoolExecutor&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ExecutorService&lt;/span&gt;&lt;span&gt; executorService &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Executors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;newFixedThreadPool&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Runnable&lt;/span&gt;&lt;span&gt; worker &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MyRunnable&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;i)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;executorService&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(worker);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;executorService&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;shutdown&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;**线程池的参数：**ThreadPoolExecutor括号中的参数含义&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;corePoolSize（核心工作线程数）&lt;/strong&gt;：当向线程池提交一个任务时，若线程池已创建的线程数小于corePoolSize，即便此时存在空闲线程，也会通过创建一个新线程来执行该任务，直到已创建的线程数大于或等于corePoolSize时。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;maximumPoolSize（最大线程数）&lt;/strong&gt;：线程池所允许的最大线程个数。当队列满了，且已创建的线程数小于maximumPoolSize，则线程池会创建新的线程来执行任务。另外，对于无界队列，可忽略该参数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;keepAliveTime（多余线程存活时间）&lt;/strong&gt;：当线程池中线程数大于核心线程数时，线程的空闲时间如果超过线程存活时间，那么这个线程就会被销毁，直到线程池中的线程数小于等于核心线程数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;workQueue（队列）&lt;/strong&gt;：用于传输和保存等待执行任务的阻塞队列。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;threadFactory（线程创建工厂）&lt;/strong&gt;：用于创建新线程。threadFactory创建的线程也是采用newThread()方式，threadFactory创建的线程名都具有统一的风格：pool-m-thread-n（m为线程池的编号，n为线程池内的线程编号）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;handler（拒绝策略）&lt;/strong&gt;：当线程池和队列都满了，再加入线程会执行此策略。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;为什么使用线程池？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	系统启动一个新线程的成本是比较高的，因为它涉及与操作系统交互。在这种情形下，使用线程池可以很好地提高性能，尤其是当程序中需要创建大量生存期很短暂的线程时，更应该考虑使用线程池。与数据库连接池类似的是，线程池在系统启动时即创建大量空闲的线程，程序将一个Runnable对象或Callable对象传给线程池，线程池就会启动一个空闲的线程来执行它们的run()或call()方法，当run()或call()方法执行结束后，该线程并不会死亡，而是再次返回线程池中成为空闲状态，等待执行下一个Runnable对象的run()或call()方法。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;线程池的工作流程？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;线程池工作流程&quot; loading=&quot;lazy&quot; width=&quot;1586&quot; height=&quot;490&quot; src=&quot;/_astro/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B.CueeuCXe_2eMeRC.webp&quot; srcset=&quot;/_astro/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B.CueeuCXe_Z1DIRAl.webp 640w, /_astro/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B.CueeuCXe_Z2a0yKV.webp 750w, /_astro/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B.CueeuCXe_16Hn1j.webp 828w, /_astro/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B.CueeuCXe_2lWezj.webp 1080w, /_astro/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B.CueeuCXe_Z1qDurF.webp 1280w, /_astro/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B.CueeuCXe_2eMeRC.webp 1586w&quot; /&gt;&lt;figcaption&gt;线程池工作流程&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;判断&lt;strong&gt;核心线程池是否已满&lt;/strong&gt;，没满则创建一个新的工作线程来执行任务。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;判断&lt;strong&gt;任务队列是否已满&lt;/strong&gt;，没满则将新提交的任务添加在工作队列。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;判断&lt;strong&gt;整个线程池是否已满&lt;/strong&gt;，没满则创建一个新的工作线程来执行任务，已满则执行饱和（拒绝）策略。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;线程池的拒绝策略：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize，如果还有任务到来就会采取任务拒绝策略，通常有以下四种策略：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;AbortPolicy：&lt;strong&gt;丢弃任务并抛出&lt;/strong&gt;RejectedExecutionException&lt;strong&gt;异常&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DiscardPolicy：也是&lt;strong&gt;丢弃任务&lt;/strong&gt;，但是&lt;strong&gt;不抛出异常&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DiscardOldestPolicy：&lt;strong&gt;丢弃队列最前面的任务&lt;/strong&gt;，然后&lt;strong&gt;重新尝试&lt;/strong&gt;执行任务（重复该过程）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CallerRunsPolicy：由&lt;strong&gt;调用线程处理&lt;/strong&gt;该任务。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.2 Thread中的常用方法&lt;a href=&quot;#32-thread中的常用方法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;构造方法：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Thread()//无参构造&lt;/p&gt;&lt;p&gt;Thread(String name)//传入线程名&lt;/p&gt;&lt;p&gt;Thread(Runnable target)//使用Runnable接口构造&lt;/p&gt;&lt;p&gt;Thread(Runnable target, String name)//使用Runnable接口和传入一个线程名&lt;/p&gt;&lt;p&gt;&lt;strong&gt;静态方法：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;currentThread()：&lt;strong&gt;返回当前正在执行的线程&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;interrupted()：返回当前执行的线程&lt;strong&gt;是否已经被中断&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;sleep(long millis)：使当前执行的线程&lt;strong&gt;睡眠多少毫秒数&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;yield()：使当前执行的线程&lt;strong&gt;自愿暂时放弃对处理器的使用权&lt;/strong&gt;并允许其他线程执行；回到就绪状态&lt;/p&gt;&lt;p&gt;&lt;strong&gt;实例方法：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;getId()：返回该线程的&lt;strong&gt;id&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;getName()：返回该线程的&lt;strong&gt;名字&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;getPriority()：返回该线程的&lt;strong&gt;优先级&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;interrupt()：使该线程&lt;strong&gt;中断&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;isInterrupted()：返回该线程&lt;strong&gt;是否被中断&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;isAlive()：返回该线程&lt;strong&gt;是否处于活动状态&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;isDaemon()：返回该线程&lt;strong&gt;是否是守护线程&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;setDaemon(boolean on)：将该线程标记为守护线程或用户线程，如果不标记默认是非守护线程；&lt;/p&gt;&lt;p&gt;setName(String name)：&lt;strong&gt;设置该线程的名字&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;setPriority(int newPriority)：&lt;strong&gt;改变该线程的优先级&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;join()：&lt;strong&gt;等待该线程终止&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;join(long millis)：等待该线程终止,至多等待多少毫秒数。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.3 线程的生命周期&lt;a href=&quot;#33-线程的生命周期&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;​	在线程的生命周期中，它要经过新建（New）、就绪（Ready）、运行（Running）、阻塞（Blocked）和死亡（Dead）5种状态。尤其是当线程启动以后，它不可能一直“霸占”着CPU独自运行，所以CPU需要在多条线程之间切换，于是线程状态也会多次在运行、就绪之间切换。&lt;/p&gt;&lt;p&gt;​	当程序&lt;strong&gt;使用new关键字创建了一个线程&lt;/strong&gt;之后，该线程就处于&lt;strong&gt;新建状态&lt;/strong&gt;，此时它和其他的Java对象一样，仅仅由Java虚拟机为其分配内存，并初始化其成员变量的值。此时的线程对象没有表现出任何线程的动态特征，程序也不会执行线程的线程执行体。&lt;/p&gt;&lt;p&gt;​	当线程对象&lt;strong&gt;调用了start()方法&lt;/strong&gt;之后，该线程处于&lt;strong&gt;就绪状态&lt;/strong&gt;，Java虚拟机会为其创建方法调用栈和程序计数器，处于这个状态中的线程并没有开始运行，只是表示该线程可以运行了。至于该线程何时开始运行，取决于JVM里线程调度器的调度。&lt;/p&gt;&lt;p&gt;​	如果处于就绪状态的线程&lt;strong&gt;获得了CPU&lt;/strong&gt;，开始执行run()方法的线程执行体，则该线程处于&lt;strong&gt;运行状态&lt;/strong&gt;，如果计算机只有一个CPU，那么在任何时刻只有一个线程处于运行状态。当然，在一个多处理器的机器上，将会有多个线程并行执行；当线程数大于处理器数时，依然会存在多个线程在同一个CPU上轮换的现象。&lt;/p&gt;&lt;p&gt;​	当一个线程开始运行后，它不可能一直处于运行状态，线程在运行过程中需要被中断，目的是使其他线程获得执行的机会，线程调度的细节取决于底层平台所采用的策略。对于采用抢占式策略的系统而言，系统会给每个可执行的线程一个小时间段来处理任务。当该时间段用完后，系统就会剥夺该线程所占用的资源，让其他线程获得执行的机会。当&lt;strong&gt;发生如下情况&lt;/strong&gt;时，线程将会进入&lt;strong&gt;阻塞状态&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;​	1. 线程**调用sleep()**方法主动放弃所占用的处理器资源。&lt;/p&gt;&lt;p&gt;​	2. 线程&lt;strong&gt;调用了一个阻塞式IO方法&lt;/strong&gt;，在该方法返回之前，该线程被阻塞。&lt;/p&gt;&lt;p&gt;​	3. 线程&lt;strong&gt;试图获得一个同步监视器&lt;/strong&gt;，但该同步监视器正被其他线程所持有。&lt;/p&gt;&lt;p&gt;​	4. 线程在&lt;strong&gt;等待某个通知&lt;/strong&gt;（notify）。&lt;/p&gt;&lt;p&gt;​	5. 程序&lt;strong&gt;调用了线程的suspend()方法&lt;/strong&gt;将该线程挂起。但这个方法容易导致死锁，所以应该尽量避免使用该方法。&lt;/p&gt;&lt;p&gt;针对上面几种情况，当发生如下特定的情况时可以&lt;strong&gt;解除上面的阻塞&lt;/strong&gt;，让该线程重新进入&lt;strong&gt;就绪状态&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;​	1. 调用sleep()方法的线程&lt;strong&gt;经过了指定时间&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​	2. 线程调用的阻塞式&lt;strong&gt;IO方法已经返回&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​	3. 线程&lt;strong&gt;成功地获得了&lt;/strong&gt;试图取得的&lt;strong&gt;同步监视器&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​	4. 线程正在等待某个通知时，&lt;strong&gt;其他线程发出了一个通知&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​	5. 处于挂起状态的线程被&lt;strong&gt;调用了resume()恢复方法&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;线程会以如下三种方式结束，结束后就处于&lt;strong&gt;死亡状态&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;​		1. run()或call()方法执行完成，&lt;strong&gt;线程正常结束&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​		2. 线程&lt;strong&gt;抛出一个未捕获的Exception或Error&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​		3. 直接&lt;strong&gt;调用该线程的stop()方法&lt;/strong&gt;来结束该线程，该方法容易导致死锁，通常不推荐使用。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.4 线程同步&lt;a href=&quot;#34-线程同步&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;使用synchronized&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用Lock&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用volatile&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用ThreadLocal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用原子变量&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;final修饰的变量：不可变的，即使共享也不会产生线程安全问题，因为不可修改。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;section&gt;&lt;h4&gt;3.4.1 synchronized&lt;a href=&quot;#341-synchronized&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;synchronized是什么？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		synchronized 是Java的&lt;strong&gt;关键字&lt;/strong&gt;，其作用是&lt;strong&gt;对同步的代码加锁&lt;/strong&gt;，使得在&lt;strong&gt;同一时间只能有一个线程&lt;/strong&gt;进入代码，从而达到同步的目的。synchronized 可以同步方法，也可以同步代码块。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;使用synchronized 同步代码块：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	synchronized 同步代码块的时候底层是通过monitorenter、monitorexit指令来实现的。因为每个对象都是一个monitor（监视器锁），当这个锁被占用的时候，整个对象就处于锁定状态，当一个线程执行monitorenter指令尝试去获取对象锁的时候，如果monitor的进入数为0，则该线程进入monitor，然后将进入数设置为1，该线程即为monitor的所有者。如果线程已经占有该monitor，只是重新进入，则进入monitor的进入数加1。如果其他线程已经占用了monitor，则该线程进入阻塞状态，直到monitor的进入数为0，再重新尝试获取monitor的所有权。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;使用synchronized 同步方法：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	当方法调用时，调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置，如果设置了，执行线程将先获取monitor，获取成功之后才能执行方法体，方法执行完后再释放monitor。在方法执行期间，其他任何线程都无法再获得同一个monitor对象。&lt;/p&gt;&lt;p&gt;synchronized&lt;strong&gt;可以修饰静态方法&lt;/strong&gt;，但&lt;strong&gt;不能修饰静态代码块&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​	当修饰静态方法时，监视器锁（monitor）便是对象的Class实例，因为Class数据存在于方法区，因此静态方法锁相当于该类的一个全局锁。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;线程之间的通信&lt;/strong&gt;：wait()、notify()、notifyAll()&lt;/p&gt;&lt;p&gt;​		如果线程之间采用synchronized来保证线程安全，则可以利用wait()、notify()、notifyAll()来实现线程通信。这三个方法都不是Thread类中所声明的方法，而是Object类中声明的方法。原因是每个对象都拥有锁，所以让当前线程等待某个对象的锁，当然应该通过这个对象来操作。并且因为当前线程可能会等待多个线程的锁，如果通过线程来操作，就非常复杂了。&lt;/p&gt;&lt;p&gt;wait()方法可以让当前线程释放对象锁并进入阻塞状态。&lt;/p&gt;&lt;p&gt;notify()方法用于唤醒一个正在等待相应对象锁的线程，使其进入就绪队列，以便在当前线程释放锁后竞争锁，进而得到CPU的执行。&lt;/p&gt;&lt;p&gt;notifyAll()用于唤醒所有正在等待相应对象锁的线程，使它们进入就绪队列，以便在当前线程释放锁后竞争锁，进而得到CPU的执行。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;注：每个锁对象都有两个队列，一个是就绪队列，一个是阻塞队列。就绪队列存储了已就绪（将要竞争锁）的线程，阻塞队列存储了被阻塞的线程。当一个阻塞线程被唤醒后，才会进入就绪队列，进而等待CPU的调度。反之，当一个线程被wait后，就会进入阻塞队列，等待被唤醒。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;synchronized锁升级：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		锁的状态有四种：无锁、偏向锁、轻量级锁、重量级锁。并且四种状态会随着竞争的情况逐渐升级，而且是不可逆的过程，即不可降级，这四种锁的级别由低到高依次是：无锁、偏向锁，轻量级锁，重量级锁。如下图所示：&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;锁升级&quot; loading=&quot;lazy&quot; width=&quot;1550&quot; height=&quot;904&quot; src=&quot;/_astro/%E9%94%81%E5%8D%87%E7%BA%A7.B2tZiX7h_1SVws.webp&quot; srcset=&quot;/_astro/%E9%94%81%E5%8D%87%E7%BA%A7.B2tZiX7h_Z14AhBc.webp 640w, /_astro/%E9%94%81%E5%8D%87%E7%BA%A7.B2tZiX7h_l0Ec5.webp 750w, /_astro/%E9%94%81%E5%8D%87%E7%BA%A7.B2tZiX7h_Z29bx9T.webp 828w, /_astro/%E9%94%81%E5%8D%87%E7%BA%A7.B2tZiX7h_2lQXLl.webp 1080w, /_astro/%E9%94%81%E5%8D%87%E7%BA%A7.B2tZiX7h_oLehH.webp 1280w, /_astro/%E9%94%81%E5%8D%87%E7%BA%A7.B2tZiX7h_1SVws.webp 1550w&quot; /&gt;&lt;figcaption&gt;锁升级&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;无锁&lt;/p&gt;
&lt;p&gt;无锁是指没有对资源进行锁定，所有的线程都能访问并修改同一个资源，但同时只有一个线程能修改成功。无锁的特点是&lt;strong&gt;修改操作会在循环内进行&lt;/strong&gt;，线程会&lt;strong&gt;不断的尝试修改共享资源&lt;/strong&gt;。如果没有冲突就修改成功并退出，否则就会继续循环尝试。如果有多个线程修改同一个值，必定会有一个线程能修改成功，而其他修改失败的线程会&lt;strong&gt;不断重试直到修改成功&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;偏向锁&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;初次执行到synchronized代码块&lt;/strong&gt;的时候，&lt;strong&gt;锁对象变成偏向锁&lt;/strong&gt;（&lt;strong&gt;通过CAS修改对象头里的锁标志位&lt;/strong&gt;），字面意思是“偏向于第一个获得它的线程”的锁。执行完同步代码块后，线程并&lt;strong&gt;不会主动释放偏向锁&lt;/strong&gt;。当第二次到达同步代码块时，线程会判断此时持有锁的线程是否就是自己（持有锁的线程ID也在对象头里），如果是则正常往下执行。由于之前没有释放锁，这里也就不需要重新加锁。如果自始至终使用锁的线程只有一个，很明显偏向锁几乎没有额外开销，性能极高。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;轻量级锁&lt;/p&gt;
&lt;p&gt;轻量级锁是指当&lt;strong&gt;锁是偏向锁的时候&lt;/strong&gt;，却&lt;strong&gt;被另外的线程所访问&lt;/strong&gt;，此时偏向锁就会&lt;strong&gt;升级为轻量级锁&lt;/strong&gt;，其他线程会通过&lt;strong&gt;自旋的形式&lt;/strong&gt;尝试获取锁，线程不会阻塞，从而提高性能。&lt;/p&gt;
&lt;p&gt;长时间的自旋操作是非常消耗资源的，一个线程持有锁，其他线程就只能在原地空耗CPU，执行不了任何有效的任务，这种现象叫做忙等（busy-waiting）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;重量级锁&lt;/p&gt;
&lt;p&gt;忙等是有限度的（有个计数器记录自旋次数，默认允许循环10次，可以通过虚拟机参数更改）。如果锁竞争情况严重，某个达到最大自旋次数的线程，会将轻量级锁升级为重量级锁（依然是CAS修改锁标志位，但不修改持有锁的线程ID）。当后续线程尝试获取锁时，发现被占用的锁是重量级锁，则直接将自己挂起（而不是忙等），等待将来被唤醒。&lt;/p&gt;
&lt;p&gt;重量级锁是指&lt;strong&gt;当有一个线程获取锁&lt;/strong&gt;之后，&lt;strong&gt;其余所有等待获取该锁的线程都会处于阻塞状态&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;扩展：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	synchronized 用的锁是存在Java对象头里的，那么什么是对象头呢？我们以 Hotspot 虚拟机为例进行说明，Hopspot 对象头主要包括两部分数据：Mark Word（标记字段） 和 Klass Pointer（类型指针）。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- Mark Word：默认存储对象的HashCode，分代年龄和锁标志位信息。这些信息都是与对象自身定义无关的数据，所以Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。它会根据对象的状态复用自己的存储空间，也就是在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Klass Point：对象指向它的类元数据的指针，虚拟机通过这个指针来确定这个对象是哪个类的实例。&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;一个对象在内存中的存储布局可以划分为三部分：对象头、实例数据、对齐填充&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;对象头：包括MarkWord和类型指针&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;实例数据：对象真正存储的有效信息。无论是从父类继承下来的还是子类中定义的字段都必须定义&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;对齐填充：不是必然存在，仅是占位符。（由于HotSpot虚拟机的自动管理系统要求起始地址必须是8字节的整数倍，所以任何对象的大小都必须是8的整数倍）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​	那么，synchronized 具体是存在对象头哪里呢？答案是：存在锁对象的对象头的Mark Word中，那么MarkWord在对象头中到底长什么样，它到底存储了什么呢？&lt;/p&gt;&lt;p&gt;​	在64位的虚拟机中：&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;64位虚拟机对象头&quot; loading=&quot;lazy&quot; width=&quot;1578&quot; height=&quot;512&quot; src=&quot;/_astro/64%E4%BD%8D%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%AF%B9%E8%B1%A1%E5%A4%B4.BCglI7Pf_wE5SE.webp&quot; srcset=&quot;/_astro/64%E4%BD%8D%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%AF%B9%E8%B1%A1%E5%A4%B4.BCglI7Pf_FrBdU.webp 640w, /_astro/64%E4%BD%8D%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%AF%B9%E8%B1%A1%E5%A4%B4.BCglI7Pf_vvTWa.webp 750w, /_astro/64%E4%BD%8D%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%AF%B9%E8%B1%A1%E5%A4%B4.BCglI7Pf_Z28M20n.webp 828w, /_astro/64%E4%BD%8D%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%AF%B9%E8%B1%A1%E5%A4%B4.BCglI7Pf_1gjDMa.webp 1080w, /_astro/64%E4%BD%8D%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%AF%B9%E8%B1%A1%E5%A4%B4.BCglI7Pf_Z2bNcFU.webp 1280w, /_astro/64%E4%BD%8D%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%AF%B9%E8%B1%A1%E5%A4%B4.BCglI7Pf_wE5SE.webp 1578w&quot; /&gt;&lt;figcaption&gt;64位虚拟机对象头&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3.4.2 Lock锁&lt;a href=&quot;#342-lock锁&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;synchronized的缺陷&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		一个代码块被synchronized修饰了，当一个线程获取了对应的锁，并执行该代码块时，其他线程便只能一直等待，等待获取锁的线程释放锁，而这里获取锁的线程释放锁只会有两种情况：1）获取锁的线程执行完了该代码块，然后线程释放对锁的占有；2）线程执行发生异常，此时JVM会让线程自动释放锁。&lt;/p&gt;&lt;p&gt;缺陷情况：&lt;/p&gt;&lt;p&gt;​	1. 某个已经获得锁的线程由于要等待IO或者其他原因（比如调用sleep方法）被阻塞了，但是又没有释放锁，其他想要获取锁的线程便只能一直阻塞，直到获得锁的线程释放锁了才能进入到同步代码块。&lt;/p&gt;&lt;p&gt;​	2. 当有多个线程读写文件时，读操作和写操作会发生冲突现象，写操作和写操作会发生冲突现象，但是读操作和读操作不会发生冲突现象。如果多个线程都只是进行读操作，当一个线程在进行读操作时，其他线程只能等待无法进行读操作。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Lock是一个接口，里面定义了获取锁，释放锁等相关的抽象方法。&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Lock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;2     &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lock&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;span&gt;//用来获取锁的&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;3     &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lockInterruptibly&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;throws&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;InterruptedException&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;4     &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tryLock&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;5     &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tryLock&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;time&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TimeUnit&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unit&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;throws&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;InterruptedException&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;6     &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unlock&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;7     &lt;/span&gt;&lt;span&gt;Condition&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newCondition&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;lock()方法是用来获取锁的，是我们平时使用最多的一个方法，如果锁已被其他线程获取，则进行等待。我们之前说过了，使用lock获取锁的时候，&lt;strong&gt;必须手动释放锁&lt;/strong&gt;，并且在&lt;strong&gt;发生异常的时候&lt;/strong&gt;，&lt;strong&gt;不会自动释放锁&lt;/strong&gt;。因此一般来说，使用&lt;strong&gt;Lock必须在try{}catch{}块中进行&lt;/strong&gt;，并且&lt;strong&gt;将释放锁的操作放在finally块中进行&lt;/strong&gt;，以&lt;strong&gt;保证锁一定被被释放&lt;/strong&gt;，&lt;strong&gt;防止死锁的发生&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;tryLock()方法是有返回值的，它表示用来&lt;strong&gt;尝试获取锁&lt;/strong&gt;，如果获取成功，则返回true，如果获取失败（即锁已被其他线程获取），则返回false，也就说这个方法无论如何都会立  即返回。在拿不到锁时不会一直在那等待。&lt;/li&gt;
&lt;li&gt;tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的，只不过区别在于这个方法在**拿不到锁时会等待一定的时间，**在时间期限之内如果还拿不到锁，就返回false。   如果如果一开始拿到锁或者在等待期间内拿到了锁，则返回true。&lt;/li&gt;
&lt;li&gt;lockInterruptibly()方法比较特殊，当通过这个方法去获取锁时，如果&lt;strong&gt;线程正在等待获取锁&lt;/strong&gt;，则这个线程能够&lt;strong&gt;响应中断&lt;/strong&gt;，即中断线程的等待状态。也就使说，当两个线程同时通 过lock.lockInterruptibly()想获取某个锁时，假若此时线程A获取到了锁，而线程B只有在等待，那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;​	Lock只负责获取锁和释放锁，如果需要实现类似wait()/notify()这种等待/通知的机制，还需要&lt;strong&gt;借助Condition接口&lt;/strong&gt;。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Condition接口提供了最基本的实现等待/通知机制的API，但是它比wait()/notify()更强大，这里先介绍一些它最基本的API。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;await()：让当前线程进入WAITING状态，同时释放锁，类似wait()的作用。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;signal()：通知某个处于WAITING状态的线程，可以继续获取锁执行。类似notify()的作用。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;signalAll()：通知所有处于WAITING状态的线程，开始争抢锁然后执行。类似notifyAll()的作用。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​		从ReentrantLock.newCondition()方法的实现可以看出来，其实这个Condition是每次都new出来的，所以是一个Lock可以存在多个Condition的。类似于一个说存在多个条件，各个条件管控自己的await()和signal()状态，相互之间并不影响。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;ReentrantLock：可重入锁&lt;/strong&gt;：Lock的实现类&lt;/p&gt;&lt;p&gt;何为可重入锁？&lt;/p&gt;&lt;p&gt;​	当一个线程获得了当前实例的锁，并进入方法A，这个线程在没有释放这把锁的时候，可以再次进入方法A。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ReentrantLock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Lock&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; java.io.&lt;/span&gt;&lt;span&gt;Serializable&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt; serialVersionUID &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;7373984872572414699L&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/** Synchronizer providing all implementation mechanics */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sync&lt;/span&gt;&lt;span&gt; sync&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;abstract&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sync&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbstractQueuedSynchronizer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt; serialVersionUID &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span&gt;5179523762034025860L&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;....&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NonfairSync&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sync&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt; serialVersionUID &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;7316153563782823691L&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tryAcquire&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;acquires&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nonfairTryAcquire&lt;/span&gt;&lt;span&gt;(acquires);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FairSync&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sync&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt; serialVersionUID &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span&gt;3000897897090466540L&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;​		ReentrantLock 是&lt;strong&gt;基于 AQS 实现的&lt;/strong&gt;， AQS 即 AbstractQueuedSynchronizer （抽象队列同步器）的缩写，这个内部提供了一个FIFO队列。该队列是一个双向链表，就是用来实现线程的并发访问控制。&lt;/p&gt;&lt;p&gt;​	AQS使用一个整型的volatile变量state来维护同步状态，这个volatile变量是实现ReentrantLock的关键&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;state初始化为0，表示未锁定状态.A线程lock时，会调用tryAcquire独占该锁并将state+1。此后，其他线程再tryAcquire时就会失败，直到A线程unlock到state=0（即释放锁）为止，其它线程才有机会获取该锁。当然，释放锁之前，A线程自己是可以重复获取此锁的（state会累加），这就是可重入的概念。但要注意，获取多少次就要释放多么次，这样才能保证state是能回到零态的。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;AQS提供了两种功能：独占和共享&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;独占&lt;/strong&gt;：就是在lock.lock()之后的代码在同一时刻只能有一个线程来执行，其余的线程将会被阻塞，直到该线程执行了lock.unlock()。&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;共享&lt;/strong&gt;：就是读锁与读锁可以共享，读锁与写锁不可以共享（排他），写锁与写锁不可以共享（排他）&lt;/p&gt;&lt;p&gt;**对于ReentrantLock，有两种获取锁的模式：公平锁和非公平锁。**构造方法（不带参数 和带参数 true： 公平锁； false: 非公平锁）&lt;/p&gt;&lt;p&gt;​		ReentrantLock 的公平锁和非公平锁都委托了 AbstractQueuedSynchronizer#acquire 去请求获取&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;公平锁：FairSync&lt;/strong&gt;：公平锁的实现机理在于每次有线程来抢占锁的时候，都会检查一遍有没有等待队列，如果有，将当前线程加入到等待队列。&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;非公平锁：NonfairSync&lt;/strong&gt;：与公平锁的区别在于新晋获取锁的进程会有多次机会去抢占锁，被加入了等待队列后则跟公平锁没有区别。&lt;/p&gt;&lt;p&gt;​		总结：从这里我们也能看出公平锁和非公平锁的区别：公平锁的新入线程不能直接获取锁，必须去排队（除非没有任何竞争发生） 。 而非公平锁新入的线程则可以先尝试获取锁，如果失败了再排队。&lt;/p&gt;&lt;p&gt;​		公平锁和非公平锁在说的获取上都使用到了 volatile 关键字修饰的state字段， 这是保证多线程环境下锁的获取与否的核心。但是当并发情况下多个线程都读取到 state == 0 时，则必须用到CAS技术，一门CPU的原子锁技术，可通过CPU对共享变量加锁的形式，实现数据变更的原子操作。volatile 和 CAS的结合是并发抢占的关键。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;说一说synchronized与Lock的区别&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;synchronized是Java关键字，在JVM层面实现加锁和解锁；Lock是一个接口，在代码层面实现加锁和解锁。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;synchronized可以用在代码块上、方法上；Lock只能写在代码里。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;synchronized在代码执行完或出现异常时自动释放锁；Lock不会自动释放锁，需要在finally中显示释放锁。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;synchronized会导致线程拿不到锁一直等待；Lock可以设置获取锁失败的超时时间。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;synchronized无法得知是否获取锁成功；Lock则可以通过tryLock得知加锁是否成功。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;synchronized锁可重入、不可中断、非公平；Lock锁可重入、可中断、可公平/不公平，并可以细分读写锁以提高效率。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;悲观锁与乐观锁：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;悲观锁&lt;/strong&gt;：总是假设最坏的情况，每次去拿数据的时候都认为别人会修改，所以每次在拿数据的时候都会上锁，这样别人想拿这个数据就会阻塞直到它拿到锁。Java中悲观锁是通过synchronized关键字或Lock接口来实现的。&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;乐观锁&lt;/strong&gt;：顾名思义，就是很乐观，每次去拿数据的时候都认为别人不会修改，所以不会上锁，但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。乐观锁适用于多读的应用类型，这样可以提高吞吐量。在JDK1.5 中新增 java.util.concurrent (JUC)就是建立在CAS之上的。相对于对于synchronized 这种阻塞算法，CAS是非阻塞算法的一种常见实现。所以JUC在性能上有了很大的提升。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;CAS操作：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;CAS,compare and swap的缩写，中文翻译成比较并交换。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CAS 操作包含三个操作数 —— 内存位置（V）、预期原值（A）和新值(B)。 如果内存位置的值与预期原值相匹配，那么处理器会自动将该位置值更新为新值 。否则，处理器不做任何操作。无论哪种情况，它都会在 CAS 指令之前返回该 位置的值。（在 CAS 的一些特殊情况下将仅返回 CAS 是否成功，而不提取当前 值。）CAS 有效地说明了“我认为位置 V 应该包含值 A；如果包含该值，则将 B 放到这个位置；否则，不要更改该位置，只告诉我这个位置现在的值即可。”&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;通常将 CAS 用于同步的方式是从地址 V 读取值 A，执行多步计算来获得新值 B，然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改，则 CAS 操作成功。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;类似于 CAS 的指令允许算法执行读-修改-写操作，而无需害怕其他线程同时 修改变量，因为如果其他线程修改变量，那么 CAS 会检测它（并失败），算法 可以对该操作重新计算。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3.4.3 volatile关键字&lt;a href=&quot;#343-volatile关键字&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​		首先描述一下Java内存模型JMM：JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是整个计算机虚拟模型，所以JMM是隶属于JVM的。从抽象的角度来看，JMM定义了线程和主内存之间的抽象关系：线程之间的共享变量存储在主内存（Main Memory）中，每个线程都有一个私有的本地内存（Local Memory），本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念，并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;JMM1&quot; loading=&quot;lazy&quot; width=&quot;579&quot; height=&quot;251&quot; src=&quot;/_astro/JMM1.oSqiMzYD_Z1Ai7LA.webp&quot; srcset=&quot;/_astro/JMM1.oSqiMzYD_Z1Ai7LA.webp 579w&quot; /&gt;&lt;figcaption&gt;JMM1&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;​	volatile关键字修饰的变量具有以下两个特征：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;线程可见性&lt;/strong&gt;：当写一个volatile变量时，JMM会把该&lt;strong&gt;线程本地内存中的变量&lt;/strong&gt;强制&lt;strong&gt;刷新到主内存&lt;/strong&gt;中去，这个写会操作会&lt;strong&gt;导致其他线程中的volatile变量缓存无效&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;禁止指令重排&lt;/strong&gt;：使用volatile关键字修饰共享变量可以禁止指令重排序，volatile禁止指令重排序有一些规则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当程序执行到volatile变量的读操作或者写操作时，在其&lt;strong&gt;前面的操作的更改肯定全部已经进行&lt;/strong&gt;，且结果已经对后面的操作可见，在其&lt;strong&gt;后面的操作肯定还没有进行&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;在进行指令优化时，&lt;strong&gt;不能将对volatile变量访问的语句放在其后面执行&lt;/strong&gt;，&lt;strong&gt;也不能把volatile变量后面的语句放到其前面执行&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;即执行到volatile变量时，其&lt;strong&gt;前面的所有语句都执行完，后面所有语句都未执行&lt;/strong&gt;。且前面语句的结果对volatile变量及其后面语句可见。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;底层实现原理：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	在JVM底层volatile是采用“&lt;strong&gt;内存屏障&lt;/strong&gt;”来实现的。观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现，加入volatile关键字时，会多出一个lock前缀指令，lock前缀指令实际上相当于一个内存屏障。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3.4.4 ThreadLocal&lt;a href=&quot;#344-threadlocal&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	博客详解：&lt;a href=&quot;https://www.cnblogs.com/micrari/p/6790229.html&quot; target=&quot;_blank&quot;&gt;https://www.cnblogs.com/micrari/p/6790229.html&lt;/a&gt;&lt;/p&gt;&lt;p&gt;​	使用**ThreadLocal解决线程局部变量统一定义问题，**多线程数据不能共享。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;JMM&quot; loading=&quot;lazy&quot; width=&quot;1389&quot; height=&quot;702&quot; src=&quot;/_astro/JMM.yRKYotxB_1erMtW.webp&quot; srcset=&quot;/_astro/JMM.yRKYotxB_Rny2l.webp 640w, /_astro/JMM.yRKYotxB_Z2iVWn.webp 750w, /_astro/JMM.yRKYotxB_1FdEJ4.webp 828w, /_astro/JMM.yRKYotxB_Uuz6Q.webp 1080w, /_astro/JMM.yRKYotxB_Z7tbJh.webp 1280w, /_astro/JMM.yRKYotxB_1erMtW.webp 1389w&quot; /&gt;&lt;figcaption&gt;JMM&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;​	ThreadLocal顾名思义是线程私有的局部变量存储容器，可以理解成每个线程都有自己专属的存储容器，它用来存储线程私有变量，其实它只是一个外壳，内部真正存取是一个Map。每个线程可以通过set() 和 get() 存取变量，多线程间无法访问各自的局部变量，相当于在每个线程间建立了一个隔板。只要线程处于活动状态，它所对应的ThreadLocal实例就是可访问的，线程被终止后，它的所有实例将被垃圾收集。总之记住一句话：ThreadLocal存储的变量属于当前线程。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;实现原理：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		Thread类中有个变量threadLocals，它的类型为ThreadLocal中的一个内部类ThreadLocalMap，这个类没有实现map接口，就是一个普通的Java类，但是实现的类似map的功能。每个线程都有自己的一个ThreadLocalMap，ThreadLocalMap是一个数组的数据结构存储数据，每个元素是一个Entry，entry的key是ThreadLocal的弱引用，也就是当前变量的副本，value就是set的值。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ThreadLocal&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ThreadLocalMap&lt;/span&gt;&lt;span&gt; threadLocals &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ThreadLocalMap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WeakReference&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ThreadLocal&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;//弱引用，垃圾回收器扫描到就会被回收&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ThreadLocal&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;k&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;super&lt;/span&gt;&lt;span&gt;(k);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;value &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; v;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; INITIAL_CAPACITY &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;16&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;//Entry数组初始容量为16，扩容是2的幂&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt;[] table&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; size &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; threshold&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// Default to 0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setThreshold&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;threshold &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; len &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nextIndex&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span&gt;//下一个索引&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; ((i &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; len) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prevIndex&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span&gt;//上一个索引&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; ((i &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; len &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;ThreadLocalMap&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ThreadLocal&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;firstKey&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;firstValue&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span&gt;//构造函数&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;table &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt;[INITIAL_CAPACITY];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;firstKey&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;threadLocalHashCode&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt; (INITIAL_CAPACITY &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;table[i] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt;(firstKey, firstValue);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;size &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;setThreshold&lt;/span&gt;&lt;span&gt;(INITIAL_CAPACITY);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;​	从源码中我们可以看出Entry数组&lt;strong&gt;初始容量为16&lt;/strong&gt;，&lt;strong&gt;扩容门限是容量的2/3，达到门限时扩容&lt;/strong&gt;，Entry数组可以获得下一个索引和上一个索引，在&lt;strong&gt;逻辑上是一个循环数组&lt;/strong&gt;，在ThreadLocalMap中you&lt;code&gt;int i = firstKey.threadLocalHashCode &amp;amp; (INITIAL_CAPACITY - 1);&lt;/code&gt;ThreadLocal类中有一个被final修饰的类型为int的threadLocalHashCode，它在该ThreadLocal被构造的时候就会生成，相当于一个ThreadLocal的ID，而它的值来源于&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 生成hash code间隙为这个魔数，可以让生成出来的值或者说ThreadLocal的ID较为均匀地分布在2的幂大小的数组中。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; HASH_INCREMENT &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0x61c88647&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nextHashCode&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nextHashCode&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getAndAdd&lt;/span&gt;&lt;span&gt;(HASH_INCREMENT);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​	可以说在ThreadLocalMap中，形如&lt;code&gt;key.threadLocalHashCode &amp;amp; (table.length - 1)&lt;/code&gt;（其中key为一个ThreadLocal实例）这样的代码片段实质上就是在&lt;strong&gt;求一个ThreadLocal实例的哈希值&lt;/strong&gt;，只是在源码实现中没有将其抽为一个公用函数。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rehash&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;expungeStaleEntries&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// Use lower threshold for doubling to avoid hysteresis&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (size &lt;/span&gt;&lt;span&gt;&amp;gt;=&lt;/span&gt;&lt;span&gt; threshold &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; threshold &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;resize&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;resize&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt;[] oldTab &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; table&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; oldLen &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;oldTab&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; newLen &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; oldLen &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt;[] newTab &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt;&lt;span&gt;[newLen]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; count &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;Entry&lt;/span&gt;&lt;span&gt; e &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; oldTab) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (e &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;ThreadLocal&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; k &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (k &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// Help the GC&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; h &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;k&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;threadLocalHashCode&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt; (newLen &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt;&lt;span&gt; (newTab[h] &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;h &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nextIndex&lt;/span&gt;&lt;span&gt;&lt;span&gt;(h&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; newLen)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;newTab[h] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; e&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;span&gt;++;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;setThreshold&lt;/span&gt;&lt;span&gt;&lt;span&gt;(newLen)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;size &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; count&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;table &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; newTab&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;​		数组长度达到总长度的3/4时，扩容，&lt;strong&gt;扩容倍数为两倍&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;为什么Entry的key是ThreadLocal的弱引用？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		因为如果这里使用普通的key-value形式来定义存储结构，实质上就会造成节点的生命周期与线程强绑定，只要线程没有销毁，那么节点在GC分析中一直处于可达状态，没办法被回收，而程序本身也无法判断是否可以清理节点。弱引用是Java中四档引用的第三档，比软引用更加弱一些，如果一个对象没有强引用链可达，那么一般活不过下一次GC。当某个ThreadLocal已经没有强引用可达，则随着它被垃圾回收，在ThreadLocalMap里对应的Entry的键值会失效，这为ThreadLocalMap本身的垃圾清理提供了便利。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;ThreadLocal中的内存泄露问题：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	ThreadLocalMap中的Entry的key使用的是ThreadLocal对象的弱引用，在没有其他地方对ThreadLoca依赖， ThreadLocalMap中的ThreadLocal对象就会被回收掉，但是对应的value不会被回收，这个时候Map中就可能存在 key为null但是value不为null的项，这需要实际的时候使用完毕及时调用remove方法避免内存泄漏。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;java的四种引用类型：强软弱虚&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;a. 强引用(Strongly Reference)：强引用是最传统的引用的定义，是指在程序代码中普遍存在的引用赋值，即类似“Object obj = new Object()”这种引用关系。无论任何情况，只要强引用关系还存在，垃圾回收器就永远不会回收掉被引用的对象。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;b. 软引用（Soft Reference）：软引用是用来描述一些还有用，但非必须的对象。只被软引用关联着的对象，在系统将要发生内存溢出异常前，会把这些对象列进回收范围之中进行第二次回收，如果这次回收还没有足够的内存，才会抛出你内存溢出异常。SoftReference类实现软引用。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;c. 弱引用(Weak Reference)：弱引用也是用来描述那些非必须对象，但是它的强度比软引用更弱一点，被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作，无论当前内存是否足够，都会回收掉只被弱引用关联的对象。WeakReference类实现弱引用&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;d. 虚引用(Phantom Reference)：虚引用也称为幽灵引用或者幻影引用，他是最弱的一种引用关系。一个对象是否有虚引用的存在，完全不会对其生存时间构成影响，也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。PhantomReference类实现虚引用。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;ThreadLocal的哈希冲突采用线性探测法解决&lt;/strong&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3.4.5 原子变量&lt;a href=&quot;#345-原子变量&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​		&lt;strong&gt;原子变量&lt;/strong&gt;最主要的一个&lt;strong&gt;特点&lt;/strong&gt;就是&lt;strong&gt;所有的操作都是原子的&lt;/strong&gt;，synchronized关键字也可以做到对变量的原子操作。只是synchronized的成本相对较高，需要获取锁对象，释放锁对象，如果不能获取到锁，还需要阻塞在阻塞队列上进行等待。而如果单单只是为了解决对变量的原子操作，建议使用原子变量。&lt;/p&gt;&lt;p&gt;​		在java的util.concurrent.atomic包中提供了创建了原子类型变量的工具类，使用该类可以简化线程同步。例如AtomicInteger 可以用原子方式更新int的值，可用在应用程序中（如以原子方式增加的计数器），但不能用于替换Integer。可扩展Number，允许那些处理机遇数字类的工具和实用工具进行统一访问。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;JUC包下含有的：&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;原子更新&lt;/strong&gt;：Java从JDK1.5开始提供了java.util.concurrent.atomic包，方便程序员在多线程环 境下，无锁的进行原子操作。在Atomic包里一共有12个类，四种原子更新方式，分别是原子更新基本类型，原子更新 数组，原子更新引用和原子更新字段。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;锁和条件变量&lt;/strong&gt;：java.util.concurrent.locks包下包含了同步器的框架 AbstractQueuedSynchronizer，基于AQS构建的Lock以及与Lock配合可以实现等待/通知模式的Condition。JUC 下的大多数工具类用到了Lock和Condition来实现并发。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;线程池&lt;/strong&gt;：涉及到的类比如：Executor、Executors、ThreadPoolExector、 AbstractExecutorService、Future、Callable、ScheduledThreadPoolExecutor等等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;阻塞队列&lt;/strong&gt;：涉及到的类比如：ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、LinkedBlockingDeque等等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;并发容器&lt;/strong&gt;：涉及到的类比如：ConcurrentHashMap、CopyOnWriteArrayList、ConcurrentLinkedQueue、CopyOnWriteArraySet等等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;同步器&lt;/strong&gt;：剩下的是一些在并发编程中时常会用到的工具类，主要用来协助线程同步。比如：CountDownLatch、CyclicBarrier、Exchanger、Semaphore、FutureTask等等。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;4、JVM（java虚拟机）&lt;a href=&quot;#4jvmjava虚拟机&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;java程序的执行过程：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		概括来说，写好的 Java &lt;strong&gt;源代码&lt;/strong&gt;文件&lt;strong&gt;经过 Java 编译器&lt;/strong&gt;编译成&lt;strong&gt;字节码文件&lt;/strong&gt;后，通过&lt;strong&gt;类加载器&lt;/strong&gt;加载到&lt;strong&gt;内存&lt;/strong&gt;中，才能被&lt;strong&gt;实例化&lt;/strong&gt;，然后到 &lt;strong&gt;Java 虚拟机&lt;/strong&gt;中&lt;strong&gt;解释执行&lt;/strong&gt;，最后通过&lt;strong&gt;操作系统&lt;/strong&gt;操作 CPU 执行获取结果。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;普通对象的实例化过程：&lt;/strong&gt;（不包括数组和Class对象 ）&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;普通对象的创建过程(不包括数组和Class对象)：当java虚拟机&lt;strong&gt;遇到一条字节码new指令&lt;/strong&gt;时，首先将去检查这个指令的参数是否能够在常量池中定位到一个类的符号引用，并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有，那么先执行官相应的&lt;strong&gt;类加载&lt;/strong&gt;过程。&lt;/li&gt;
&lt;li&gt;类加载检查通过之后，接下来虚拟机将为&lt;strong&gt;新生对象分配内存&lt;/strong&gt;。对象所需内存的大小在类加载完成后便可完全确定，为对象分配空间的任务实际上便等同于把一块确定大小的内存块从java堆中划分出来。(分配空间使用指针碰撞，但是指针碰撞存在线程安全问题，解决线程安全问题有两种方案：一是对分配内存空间的动作进行同步处理，实际上虚拟机是采用CAS配上失败重试的方式保证更新操作的原子性；另外一种是把内存分配的动作按照线程划分在不同的空间之中进行，即为每个线程在java堆中预先分配内存，称为本地线程分配缓冲(TLAB ), 哪个线程要分配内存，就是在哪个线程的本地缓冲区中分配，只有本地缓冲区用完了，分配新的缓存区时才需要同步锁定。)&lt;/li&gt;
&lt;li&gt;内存分配完成之后，虚拟机必须将分配到的内存空间（但不包括对象头）都&lt;strong&gt;初始化为零值&lt;/strong&gt;，如果使用了TLAB的话，这一项工作也可以提前至TLAB分配时顺便进行。&lt;/li&gt;
&lt;li&gt;java虚拟机还要&lt;strong&gt;对对象头进行设置&lt;/strong&gt;，例如这个对象是哪个类的实例，如何才能找到类的元数据信息，对象的哈希码，对象的GC分代年龄等信息。&lt;/li&gt;
&lt;li&gt;上面三步完成从虚拟机角度，一个新的对象已经产生，但是从java程序来看，对象创建才刚刚开始，构造函数还没有执行，即Class文件中的**()方法还没有执行**，所有字段都是默认的0值，对象需要的其他资源和状态信息也还没有按照预定的意图构造好。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;​	() 方法中的代码执行顺序为：父类变量初始化、父类代码块、父类构造器、子类变量初始化、子类代码块、子类构造器。&lt;/p&gt;&lt;p&gt;​		数组类而言，数组类本身不通过类加载器创建，它是由Java虚拟机直接在内存中动态构造出来的。但是数组类里面的元素可能需要使用类加载器加载。&lt;/p&gt;&lt;p&gt;**对象在内存中的布局：**见3.4.1 锁升级的扩展部分&lt;/p&gt;&lt;section&gt;&lt;h3&gt;4.1 JVM的内存结构&lt;a href=&quot;#41-jvm的内存结构&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;​	JVM 主要由四大部分组成：ClassLoader（&lt;strong&gt;类加载器&lt;/strong&gt;），Runtime Data Area（&lt;strong&gt;运行时数据区&lt;/strong&gt;，内存分区），Execution Engine（&lt;strong&gt;执行引擎&lt;/strong&gt;），Native Interface（&lt;strong&gt;本地库接口&lt;/strong&gt;），下图可以大致描述 JVM 的结构。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;JVM内存结构&quot; loading=&quot;lazy&quot; width=&quot;624&quot; height=&quot;624&quot; src=&quot;/_astro/JVM%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.FVVjGW7p_Z1EhmY5.webp&quot; srcset=&quot;/_astro/JVM%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.FVVjGW7p_Z1EhmY5.webp 624w&quot; /&gt;&lt;figcaption&gt;JVM内存结构&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;​	JVM 是执行 Java 程序的虚拟计算机系统，那我们来看看执行过程：首先需要准备好编译好的 &lt;strong&gt;Java 字节码文件（即class文件，是一串二进制字节流&lt;/strong&gt;），计算机要运行程序需要先通过一定方式（类加载器）将 class 文件加载到内存中（运行时数据区），但是字节码文件是JVM定义的一套指令集规范，并不能直接交给底层操作系统去执行，因此需要特定的命令解释器（执行引擎）将字节码翻译成特定的操作系统指令集交给 CPU 去执行，这个过程中会需要调用到一些不同语言为 Java 提供的接口（例如驱动、地图制作等），这就用到了本地 Native 接口（本地库接口）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;ClassLoader&lt;/strong&gt;：负责加载字节码文件即 class 文件，class 文件在文件开头有特定的文件标示，并且ClassLoader 只负责class 文件的加载，至于它是否可以运行，则由 Execution Engine 决定。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Runtime Data Area&lt;/strong&gt;：是存放数据的，分为五部分：Stack（虚拟机栈），Heap（堆），MethodArea（方法区），PC Register（程序计数器），Native Method Stack（本地方法栈）。几乎所有的关于 Java 内存方面的问题，都是集中在这块。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Execution Engine&lt;/strong&gt;：执行引擎，也叫 Interpreter。Class 文件被加载后，会把指令和数据信息放入内存中，Execution Engine 则负责把这些命令解释给操作系统，即将 JVM 指令集翻译为操作系统指令集。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Native Interface&lt;/strong&gt;：负责调用本地接口的。他的作用是调用不同语言的接口给 JAVA 用，他会在Native Method Stack 中记录对应的本地方法，然后调用该方法时就通过 Execution Engine 加载对应的本地 lib。原本多用于一些专业领域，如JAVA驱动，地图制作引擎等，现在关于这种本地方法接口的调用已经被类似于Socket通信，WebService等方式取代。&lt;/p&gt;&lt;section&gt;&lt;h4&gt;4.1.1 运行时数据区&lt;a href=&quot;#411-运行时数据区&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;程序计数器&lt;/strong&gt;：线程私有&lt;/p&gt;&lt;p&gt;​		程序计数器（Program Counter Register）是一块较小的内存空间，它&lt;strong&gt;可以看作是当前线程所执行的字节码的行号指示器&lt;/strong&gt;。在Java虚拟机的概念模型里，字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令，它是程序控制流的指示器，分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;虚拟机栈&lt;/strong&gt;：线程私有&lt;/p&gt;&lt;p&gt;​		与程序计数器一样，Java虚拟机栈（Java Virtual Machine Stack）也是&lt;strong&gt;线程私有的&lt;/strong&gt;，它的&lt;strong&gt;生命周期与线程相同&lt;/strong&gt;。虚拟机栈描述的是Java方法执行的线程内存模型：&lt;strong&gt;每个方法被执行的时候，Java虚拟机都会同步创建一个栈帧[插图]（Stack Frame）用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程，就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;本地方法栈&lt;/strong&gt;：线程私有&lt;/p&gt;&lt;p&gt;​		本地方法栈（Native Method Stacks）与虚拟机栈所发挥的作用是非常相似的，其区别只是虚拟机栈为虚拟机执行Java方法（也就是字节码）服务，而本地方法栈则是&lt;strong&gt;为虚拟机使用到的本地（Native）方法服务&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;堆&lt;/strong&gt;：线程共享&lt;/p&gt;&lt;p&gt;​		对于Java应用程序来说，Java堆（Java Heap）是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域，&lt;strong&gt;在虚拟机启动时创建&lt;/strong&gt;。此内存区域的唯一目的就是&lt;strong&gt;存放对象实例&lt;/strong&gt;，Java世界里“几乎”所有的对象实例都在这里分配内存。Java堆既可以被实现成固定大小的，也可以是可扩展的，不过当前主流的Java虚拟机都是按照可扩展来实现的（通过参数**-Xmx和-Xms**设定）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;方法区&lt;/strong&gt;：线程共享&lt;/p&gt;&lt;p&gt;​		方法区（Method Area）与Java堆一样，是各个线程共享的内存区域，它用于存储已被虚拟机加载的&lt;strong&gt;类型信息&lt;/strong&gt;、&lt;strong&gt;常量&lt;/strong&gt;、&lt;strong&gt;静态变量&lt;/strong&gt;、即时编译器编译后的代码缓存等数据。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;运行时常量池&lt;/strong&gt;：线程共享&lt;/p&gt;&lt;p&gt;​		运行时常量池（Runtime Constant Pool）是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外，还有一项信息是常量池表（Constant Pool Table），用于存放编译期生成的各种字面量与符号引用，这部分内容将在类加载后存放到方法区的运行时常量池中。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;直接内存&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;​		直接内存（Direct Memory）并不是虚拟机运行时数据区的一部分，也不是《Java虚拟机规范》中定义的内存区域。&lt;/p&gt;&lt;p&gt;相关问题：&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;局部变量存储在哪：虚拟机栈中&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;类存放在哪：方法区中&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;元空间在本地内存&lt;/strong&gt;&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;4.2 三层类加载器和双亲委派机制&lt;a href=&quot;#42-三层类加载器和双亲委派机制&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;4.2.1 类的生命周期&lt;a href=&quot;#421-类的生命周期&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	一个类型从被加载到虚拟机内存中开始，到卸载出内存为止，它的整个生命周期将会经历加载（Loading）、验证（Verification）、准备（Preparation）、解析（Resolution）、初始化（Initialization）、使用（Using）和卸载（Unloading）七个阶段，其中验证、准备、解析三个部分统称为连接（Linking）。这七个阶段的发生顺序如下图所示。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;类加载过程&quot; loading=&quot;lazy&quot; width=&quot;1928&quot; height=&quot;540&quot; src=&quot;/_astro/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B.DVJ3jvNU_12NhB9.webp&quot; srcset=&quot;/_astro/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B.DVJ3jvNU_Z1Xv02G.webp 640w, /_astro/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B.DVJ3jvNU_Z1I550q.webp 750w, /_astro/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B.DVJ3jvNU_ZOSuoD.webp 828w, /_astro/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B.DVJ3jvNU_ZqcM8m.webp 1080w, /_astro/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B.DVJ3jvNU_1ivyFb.webp 1280w, /_astro/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B.DVJ3jvNU_Z2rFbe9.webp 1668w, /_astro/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B.DVJ3jvNU_12NhB9.webp 1928w&quot; /&gt;&lt;figcaption&gt;类加载过程&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;​		其中，加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的，类型的加载必须按照这种顺序按部就班的开始，而解析阶段则不一定，它在某些情况下可以在初始化阶段之后再开始，这是为了支持Java语言的运行时绑定特性（也称动态绑定或晚期绑定）。&lt;/p&gt;&lt;p&gt;一、加载&lt;/p&gt;&lt;p&gt;​		“加载”（Loading）阶段是整个“类加载”（Class Loading）过程中的一个阶段，在加载阶段，Java虚拟机需要完成以下三件事情：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;通过一个类的全限定名来获取定义此类的&lt;strong&gt;二进制字节流&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将这个字节流所代表的&lt;strong&gt;静态存储结构转化为方法区的运行时数据结构&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在内存中生成一个代表这个类的java.lang.Class对象，作为方法区这个类的&lt;strong&gt;各种数据的访问入口&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;二、验证&lt;/p&gt;&lt;p&gt;​		验证是连接阶段的第一步，这一阶段的目的是确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求，保证这些信息被当作代码运行后不会危害虚拟机自身的安全。验证阶段大致上会完成下面四个阶段的检验动作：文件格式验证、元数据验证、字节码验证和符号引用验证。&lt;/p&gt;&lt;p&gt;三、准备&lt;/p&gt;&lt;p&gt;​		准备阶段是正式为类中定义的变量（即静态变量，被static修饰的变量）分配内存并设置类变量初始值的阶段。在方法区中分配。&lt;/p&gt;&lt;p&gt;四、解析&lt;/p&gt;&lt;p&gt;​		解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程，&lt;/p&gt;&lt;p&gt;​		符号引用（Symbolic References）：符号引用以一组符号来描述所引用的目标，符号可以是任何形式的字面量，只要使用时能无歧义地定位到目标即可。&lt;/p&gt;&lt;p&gt;​		直接引用（Direct References）：直接引用是可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。&lt;/p&gt;&lt;p&gt;五、初始化&lt;/p&gt;&lt;p&gt;​		进行准备阶段时，变量已经赋过一次系统要求的初始零值，而在初始化阶段，则会根据程序员通过程序编码制定的主观计划去初始化类变量和其他资源。&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;只有六种情况需要对类进行初始化&lt;/strong&gt;:&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;遇到new、getstatic、putstatic、或invokestatic这四条字节码指令时，如果类型没哟进行过初始化，则需要先触发其初始化阶段。能够生成四条指令的典型Java场景有：
&lt;ol&gt;
&lt;li&gt;使用new关键字实例化对象的时候&lt;/li&gt;
&lt;li&gt;读取或设置一个类型的静态字段的时候&lt;/li&gt;
&lt;li&gt;调用一个类型的静态方法的时候&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;使用java.lang.reflect包下的方法对类型进行反射调用的时候，如果类型没有进行过初始化，则需要先触发初始化&lt;/li&gt;
&lt;li&gt;当初始化类的时候，如果发现父类还没有进行过初始化，则需要先触发其父类的初始化。&lt;/li&gt;
&lt;li&gt;当虚拟机启动时，用户需要指定一个要执行的主类（包含main()方法的那个类），虚拟机会先初始化这个主类&lt;/li&gt;
&lt;li&gt;使用JDK7加入的动态语言支持&lt;/li&gt;
&lt;li&gt;当一个接口中定义了JDK8中新加入的默认方法&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;4.2.2 三层类加载器&lt;a href=&quot;#422-三层类加载器&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	&lt;strong&gt;类与类加载器：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		类加载器虽然只用于实现类的加载动作，但它在Java程序中起到的作用确远超类加载阶段。对于任意一个类，都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性，每一个类加载器都有一个独立的类命名空间。比较两个类是否相等是需要两个类在同一个类加载器的前提下才有意义，否则即使这两个类来源于同一个Class文件，被同一个Java虚拟机加载，只要加载他们的类加载器不同，那么这两个类就必定不同。&lt;/p&gt;&lt;p&gt;​	自JDK1.2以来，Java一直保持着三层类加载器、双亲委派的类加载架构。对于这个时期的Java应用，绝大多数Java程序都会使用到以下3个系统提供的类加载器来进行加载。&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;启动类加载器&lt;/strong&gt;（Bootstrap Class Loader）：这个类加载器负责加载存放在&amp;lt;JAVA_HOME&amp;gt;\lib目录，或者被-Xbootclasspath参数所指定的路径中存放的，而且是Java虚拟机能够识别的（按照文件名识别，如rt.jar、tools.jar，名字不符合的类库即使放在lib目录中也不会被加载）类库加载到虚拟机的内存中。启动类加载器无法被Java程序直接引用，用户在编写自定义类加载器时，如果需要把加载请求委派给引导类加载器去处理，那直接使用null代替即可。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;扩展类加载器&lt;/strong&gt;（Extension Class Loader）：这个类加载器是在类sun.misc.Launcher$ExtClassLoader中以Java代码的形式实现的。它负责加载&amp;lt;JAVA_HOME&amp;gt;\lib\ext目录中，或者被java.ext.dirs系统变量所指定的路径中所有的类库。根据“扩展类加载器”这个名称，就可以推断出这是一种Java系统类库的扩展机制，JDK的开发团队允许用户将具有通用性的类库放置在ext目录里以扩展Java SE的功能，在JDK 9之后，这种扩展机制被模块化带来的天然的扩展能力所取代。由于扩展类加载器是由Java代码实现的，开发者可以直接在程序中使用扩展类加载器来加载Class文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;应用程序类加载器&lt;/strong&gt;（Application Class Loader）：这个类加载器由sun.misc.Launcher$AppClassLoader来实现。由于应用程序类加载器是ClassLoader类中的getSystem-ClassLoader()方法的返回值，所以有些场合中也称它为“&lt;strong&gt;系统类加载器&lt;/strong&gt;”。它负责加载用户类路径（ClassPath）上所有的类库，开发者同样可以直接在代码中使用这个类加载器。如果应用程序中没有自定义过自己的类加载器，一般情况下这个就是程序中默认的类加载器。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;三层类加载器&quot; loading=&quot;lazy&quot; width=&quot;1589&quot; height=&quot;1276&quot; src=&quot;/_astro/%E4%B8%89%E5%B1%82%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8.C5109fqK_2rR5XE.webp&quot; srcset=&quot;/_astro/%E4%B8%89%E5%B1%82%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8.C5109fqK_26140y.webp 640w, /_astro/%E4%B8%89%E5%B1%82%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8.C5109fqK_Uq6dy.webp 750w, /_astro/%E4%B8%89%E5%B1%82%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8.C5109fqK_pWQp0.webp 828w, /_astro/%E4%B8%89%E5%B1%82%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8.C5109fqK_12HFOW.webp 1080w, /_astro/%E4%B8%89%E5%B1%82%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8.C5109fqK_Fe6v9.webp 1280w, /_astro/%E4%B8%89%E5%B1%82%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8.C5109fqK_2rR5XE.webp 1589w&quot; /&gt;&lt;figcaption&gt;三层类加载器&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;4.2.3 双亲委派机制&lt;a href=&quot;#423-双亲委派机制&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	双亲委派模型的工作过程是：如果一个类加载器收到了类加载的请求，它&lt;strong&gt;首先不会自己去尝试加载这个类&lt;/strong&gt;，而是把这个&lt;strong&gt;请求委派给父类加载器去完成&lt;/strong&gt;，每一个层次的类加载器都是如此，因此&lt;strong&gt;所有的加载请求最终都应该传送到最顶层的启动类加载器中&lt;/strong&gt;，&lt;strong&gt;只有当父加载器反馈自己无法完成这个加载请求&lt;/strong&gt;（它的搜索范围中没有找到所需的类）时，&lt;strong&gt;子加载器才会尝试自己去完成加载&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​&lt;/p&gt;&lt;p&gt;&lt;strong&gt;为什么需要使用双亲委派机制：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	使用双亲委派模型来组织类加载器之间的关系，一个显而易见的好处就是Java中的类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object，它存放在rt.jar之中，无论哪一个类加载器要加载这个类，最终都是委派给处于模型最顶端的启动类加载器进行加载，因此Object类在程序的各种类加载器环境中都能够保证是同一个类。反之，如果没有使用双亲委派模型，都由各个类加载器自行去加载的话，如果用户自己也编写了一个名为java.lang.Object的类，并放在程序的ClassPath中，那系统中就会出现多个不同的Object类，Java类型体系中最基础的行为也就无从保证，应用程序将会变得一片混乱。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;双亲委派机制&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;1166&quot; src=&quot;/_astro/%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6.dVmJXAN2_2wVWvW.webp&quot; srcset=&quot;/_astro/%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6.dVmJXAN2_uk0SJ.webp 640w, /_astro/%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6.dVmJXAN2_mcAaJ.webp 750w, /_astro/%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6.dVmJXAN2_ZkIU8u.webp 828w, /_astro/%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6.dVmJXAN2_ZVMu1A.webp 1080w, /_astro/%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6.dVmJXAN2_2wVWvW.webp 1200w&quot; /&gt;&lt;figcaption&gt;双亲委派机制&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;4.2.4 双亲委派机制的破坏&lt;a href=&quot;#424-双亲委派机制的破坏&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;重写loadClass方法 破坏双亲委派机制&lt;/li&gt;
&lt;li&gt;使用线程上下文类加载器&lt;/li&gt;
&lt;li&gt;代码热替换、模块热部署&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;4.2.5 如何实现一个热加载和热部署&lt;a href=&quot;#425-如何实现一个热加载和热部署&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;什么是热加载？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;热加载&lt;/strong&gt;是指可以在不重启服务的情况下让更改的代码生效，&lt;strong&gt;热加载&lt;/strong&gt;可以显著的提升开发以及调试的效率，它是基于 Java 的类加载器实现的，但是由于热加载的不安全性，一般不会用于正式的生产环境。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;热加载与热部署的区别&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		首先，不管是&lt;strong&gt;热加载&lt;/strong&gt;还是热部署，都可以在不重启服务的情况下编译/部署项目，都是基于 Java 的类加载器实现的。&lt;/p&gt;&lt;p&gt;​	在部署方式上：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;热部署是在服务器运行时&lt;strong&gt;重新部署&lt;/strong&gt;项目。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;热加载是在运行时&lt;strong&gt;重新加载 class&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;在实现原理上：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;热部署是直接重新&lt;strong&gt;加载整个应用&lt;/strong&gt;，耗时相对较高。&lt;/li&gt;
&lt;li&gt;热加载是在运行时&lt;strong&gt;重新加载 class&lt;/strong&gt;，后台会启动一个线程不断检测你的类是否改变。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;在使用场景上：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;热部署更多的是在&lt;strong&gt;生产环境&lt;/strong&gt;使用。&lt;/li&gt;
&lt;li&gt;热加载则更多的是在&lt;strong&gt;开发环境&lt;/strong&gt;上使用。线上由于安全性问题不会使用，难以监控。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;4.2.6 如何实现类的热加载&lt;a href=&quot;#426-如何实现类的热加载&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;分析：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		Java 程序在运行的时候，首先会把 class 类文件加载到 JVM 中，而类的加载过程又有五个阶段，五个阶段中只有&lt;strong&gt;加载阶段&lt;/strong&gt;用户可以进行自定义处理，所以我们如果能在程序代码更改且重新编译后，让运行的进程可以实时获取到新编译后的 class 文件，然后重新进行加载的话，那么理论上就可以实现一个简单的 &lt;strong&gt;Java 热加载&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​	所以我们可以得出实现思路：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;实现自己的类加载器。&lt;/li&gt;
&lt;li&gt;从自己的类加载器中加载要热加载的类。&lt;/li&gt;
&lt;li&gt;不断轮询要热加载的类 class 文件是否有更新。&lt;/li&gt;
&lt;li&gt;如果有更新，重新加载。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;自定义类加载器&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;​		设计 Java 虚拟机的团队把类的加载阶段放到的 JVM 的外部实现（ 通过一个类的全限定名来获取描述此类的二进制字节流 ）。这样就可以让程序自己决定如果获取到类信息。而实现这个加载动作的代码模块，我们就称之为 “类加载器”。&lt;/p&gt;&lt;p&gt;​		在 Java 中，类加载器也就是 &lt;code&gt;java.lang.ClassLoader&lt;/code&gt;. 所以如果我们想要自己实现一个类加载器，就需要继承 &lt;code&gt;ClassLoader&lt;/code&gt; 然后重写里面 &lt;code&gt;findClass&lt;/code&gt;的方法，同时因为类加载器是 &lt;code&gt;双亲委派模型&lt;/code&gt;实现（也就说。除了一个最顶层的类加载器之外，每个类加载器都要有父加载器，而加载时，会先询问父加载器能否加载，如果父加载器不能加载，则会自己尝试加载）所以我们还需要指定父加载器。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;定义要类型热加载的类&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;​	假设某个接口（BaseManager.java）下的某个方法（logic）要进行热加载处理。&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;首先定义接口信息。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;写一个这个接口的实现类。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;后面我们要做的就是让这个类可以通过我们的 MyClassLoader 进行自定义加载。类的&lt;strong&gt;热加载&lt;/strong&gt;应当只有在类的信息被更改然后重新编译之后进行重新加载。所以为了不意义的重复加载，我们需要判断 class 是否进行了更新，所以我们需要记录 class 类的修改时间，以及对应的类信息。&lt;/p&gt;
&lt;p&gt;所以编译一个类用来记录某个类对应的某个类加载器以及上次加载的 class 的修改时间。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;热加载获取类信息&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;​	轮询检查 class 文件是不是被更新过，所以每次调用要热加载的类时，我们都要进行检查类是否被更新然后决定要不要重新加载。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;4.3 垃圾回收&lt;a href=&quot;#43-垃圾回收&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;哪些内存需要回收？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​			在Java内存运行时区域的各个部分中，&lt;strong&gt;堆和方法区&lt;/strong&gt;这两个区域则有着很显著的不确定性：一个接口的多个实现类需要的内存可能会不一样，一个方法所执行的不同条件分支所需要的内存也可能不一样，只有处于运行期间，我们才能知道程序究竟会创建哪些对象，创建多少个对象，这部分内存的分配和回收是动态的。垃圾收集器所关注的正是这部分内存该如何管理，我们平时所说的内存分配与回收也仅仅特指这一部分内存。&lt;/p&gt;&lt;section&gt;&lt;h4&gt;4.3.1 对象的存活&lt;a href=&quot;#431-对象的存活&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	判断一个对象是不是垃圾，有如下两种方法：引用计数算法和可达性分析，主流的JVM用的是可达性分析&lt;/p&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;引用计数算法：&lt;/strong&gt;&lt;a href=&quot;#引用计数算法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;​		在对象中添加一个引用计数器，每当有一个地方引用它时，计数器值就加一；当引用失效时，计数器值就减一；任何时刻计数器为零的对象就是不可能再被使用的。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;可达性分析：&lt;/strong&gt;&lt;a href=&quot;#可达性分析&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;​		这个算法的基本思路就是通过一系列称为“GC Roots”的根对象作为起始节点集，从这些节点开始，根据引用关系向下搜索，搜索过程所走过的路径称为“引用链”（Reference Chain），如果某个对象到GC Roots间没有任何引用链相连，或者用图论的话来说就是从GC Roots到这个对象不可达时，则证明此对象是不可能再被使用的。&lt;/p&gt;&lt;p&gt;在Java技术体系里面，固定可作为&lt;strong&gt;GC Roots的对象&lt;/strong&gt;包括以下几种：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在&lt;strong&gt;虚拟机栈（栈帧中的本地变量表）中引用的对象&lt;/strong&gt;，譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在&lt;strong&gt;方法区中类静态属性引用的对象&lt;/strong&gt;，譬如Java类的引用类型静态变量。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在&lt;strong&gt;方法区中常量引用的对象&lt;/strong&gt;，譬如字符串常量池（String Table）里的引用。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在&lt;strong&gt;本地方法栈中JNI&lt;/strong&gt;（即通常所说的Native方法）引用的对象。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Java虚拟机内部的引用&lt;/strong&gt;，如基本数据类型对应的Class对象，一些常驻的异常对象（比如NullPointExcepiton、OutOfMemoryError）等，还有系统类加载器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;所有被&lt;strong&gt;同步锁（synchronized关键字）持有的对象&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;反映Java虚拟机内部情况&lt;/strong&gt;的JMXBean、JVMTI中注册的回调、本地代码缓存等。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;安全点&lt;/strong&gt;：GC Roots需要暂停线程的，那么有的线程正在执行的话必须强制到达安全点之后才能暂停。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;安全区域&lt;/strong&gt;：有些被sleep或blocked的线程没办法进去安全点，所以就引入了安全区域。安全区域是指确保在某一段代码片段，引用关系不会发生变化，因此在这个区域的任意地方开始垃圾回收都是安全的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;记忆集与卡表&lt;/strong&gt;：记录从非收集区域指向收集区域的指针集合的抽象数据结构。卡表是记忆集的一种实现方式，卡表使用写屏障来维护数据。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;并发的可达性分析&lt;/strong&gt;：使用&lt;strong&gt;三色标记&lt;/strong&gt;。白色没有访问过，黑色代表，访问过，无序重新扫描，灰色代表，访问过，但还有引用没有被扫描。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;GC Roots不可达的对象是否一定回收？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		即使在可达性分析中判定为不可达的对象，也不是必须要回收的，一个对象判断是否需要真正的回收最多经历过两次标记：如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链，那么将会被第一次标记，随后要进行一次筛选，筛选的条件是此对象是否有必要执行finalize()方法。加入对象没有finalize()方法，或者finalize()方法已经被虚拟机调用过，那么虚拟机将这两种情况都认为没必要执行，垃圾直接回收。&lt;/p&gt;&lt;p&gt;​		如果这个对象被判定为有必要执行finalize()方法，那么该对象将会被放置在一个队列中，并在稍后由一条虚拟机自动创建的、低调度优先级的Finalizer线程去执行finalize()方法。finalize()方法是对象逃脱死亡命运的最后一次机会，收集器会对队列中的对象进行第二次标记，如果对象在finalize()方法中拯救了自己（只要重新与引用链上的任何一个对象建立关联即可），那么第二次标记时将会被移出即将回收的集合；如果对象这时候还没有逃脱，那么就真的要被回收了。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;回收方法区：&lt;/strong&gt;&lt;a href=&quot;#回收方法区&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;​		方法区的垃圾收集主要回收两部分内容：&lt;strong&gt;废弃的常量&lt;/strong&gt;和&lt;strong&gt;不再使用的类型&lt;/strong&gt;。回收废弃常量与回收Java堆中的对象非常类似。举个常量池中字面量回收的例子，假如一个字符串“java”曾经进入常量池中，但是当前系统又没有任何一个字符串对象的值是“java”，换句话说，已经没有任何字符串对象引用常量池中的“java”常量，且虚拟机中也没有其他地方引用这个字面量。如果在这时发生内存回收，而且垃圾收集器判断确有必要的话，这个“java”常量就将会被系统清理出常量池。常量池中其他类（接口）、方法、字段的符号引用也与此类似。&lt;/p&gt;&lt;p&gt;判定一个常量是否“废弃”还是相对简单，而要判定一个&lt;strong&gt;类型&lt;/strong&gt;是否属于“&lt;strong&gt;不再被使用的类&lt;/strong&gt;”的条件就比较苛刻了。需要同时满足下面三个条件：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;该&lt;strong&gt;类所有的实例都已经被回收&lt;/strong&gt;，也就是Java堆中不存在该类及其任何派生子类的实例。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;加载该&lt;strong&gt;类的类加载器已经被回收&lt;/strong&gt;，这个条件除非是经过精心设计的可替换类加载器的场景，如OSGi、JSP的重加载等，否则通常是很难达成的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;该类对应的&lt;strong&gt;java.lang.Class对象没有在任何地方被引用&lt;/strong&gt;，无法在任何地方通过反射访问该类的方法。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;4.3.2 垃圾回收算法&lt;a href=&quot;#432-垃圾回收算法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;分代收集理论：&lt;/strong&gt;&lt;a href=&quot;#分代收集理论&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;弱分代假说&lt;/strong&gt;：绝大多数对象都是朝生夕灭的&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;强分代假说&lt;/strong&gt;：熬过越多次垃圾收集过程的对象就越难以消亡。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;跨代引用假说&lt;/strong&gt;（Intergenerational Reference Hypothesis）：跨代引用相对于同代引用来说仅占极少数。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;所以根据分代理论一般将内存分为新生代与老年代，新生代的对象大都朝生夕灭，老年代的对象难以消亡。&lt;/p&gt;&lt;p&gt;下面介绍三种垃圾回收算法：&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;标记-清除算法&lt;/strong&gt;：&lt;a href=&quot;#标记-清除算法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;​		算法分为两个阶段：标记和清除；首先标记出所有需要回收的对象，在标记完成后，统一回收掉所有被标记的对象；也可以反过来，标记存活的对象，统一回收所有未被标记的对象。标记过程就是对象是否属于垃圾的判定过程&lt;/p&gt;&lt;p&gt;&lt;strong&gt;缺点&lt;/strong&gt;：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;执行效率不稳定。如果大量对象都需要回收，必须进行大量标记和清除动作&lt;/li&gt;
&lt;li&gt;内存空间的碎片化。标记清除之后会产生大量不连续的内存碎片。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;标记-复制算法&lt;/strong&gt;：&lt;a href=&quot;#标记-复制算法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;​		“半区复制”:它将可用内存按容量划分为大小相等的两块，每次只使用其中一块。当这一块的内存用完了，就将还存活的对象复制到另一块上面，然后再把已使用过内存空间一次性清理掉。&lt;/p&gt;&lt;p&gt;缺点：如果内存中多数对象都是存活的，这种算法将会产生大量的内存复制开销，内存缩小为原来的一半，空间浪费。&lt;/p&gt;&lt;p&gt;优点：多数对象都是可回收的情况，算法需要复制的就占少数的存活对象；分配内存时不用考虑有空间碎片的复杂情况。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;标记-整理算法&lt;/strong&gt;：&lt;a href=&quot;#标记-整理算法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;​		标记过程仍然与标记-清除算法相同，但是后续步骤不是直接对可回收对象进行清理，而是让所有存活的对象都向内存空间的一端移动，然后直接清理掉边界以外的内存。&lt;/p&gt;&lt;p&gt;​		标记-复制算法在对象存活率较高时，就需要进行较多的复制操作，效率将会降低；更关键的是，如果不想浪费50%的空间，就需要额外的空间进行分配担保，以应对被使用的内存中所有对象都100%存活的极端情况，所以在老年代中一般不能直接选用这种算法。&lt;/p&gt;&lt;p&gt;​		移动存活对象，尤其在老年代这种每次回收都有大量对象存活的区域，移动存活对象并更新所有应用这些对象的地方将会是一种极为负重的操作，而且这种对象移动操作必须全程暂停用户应用程序(STW The World)才能进行。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;STW:&lt;strong&gt;Stop The World&lt;/strong&gt;：产生情况&lt;a href=&quot;#stwstop-the-world产生情况&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ol&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;标记-整理算法中移动存活对象&lt;/li&gt;
&lt;li&gt;GC Roots根结点枚举&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;4.3.3 垃圾回收器&lt;a href=&quot;#433-垃圾回收器&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;垃圾回收器&quot; loading=&quot;lazy&quot; width=&quot;628&quot; height=&quot;436&quot; src=&quot;/_astro/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8.jAs0cnsd_eYphK.webp&quot; srcset=&quot;/_astro/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8.jAs0cnsd_eYphK.webp 628w&quot; /&gt;&lt;figcaption&gt;垃圾回收器&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;新生代：Serial、ParNew、Parallel Scavenge  都是基于标记-复制算法&lt;/p&gt;&lt;p&gt;老年代：CMS（标记-清除）、Serial Old（标记-整理）、Parallel Old  （标记-整理）&lt;/p&gt;&lt;p&gt;不分代：G1&lt;/p&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;JVM中一次完整的GC流程&lt;/strong&gt;：&lt;a href=&quot;#jvm中一次完整的gc流程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ol&gt;
&lt;li&gt;新生代的典型垃圾回收器是 ParNew 垃圾回收器，它按照8:1:1 将新生代分成 Eden 区，以及两个 Survivor 区，某一时刻，我们创建的对象将 Eden 区全部挤满，这个对象就是挤满新生代的最后一个对象。此时，Minor GC 就触发了。&lt;/li&gt;
&lt;li&gt;在正式的Minor GC之前，JVM首先检查老年代的剩余空间是不是比新生代的对象大还是小。（为什么要判断？因为假如Minor GC后Survivor 区放不下剩余对象，这些对象就要进入到老年代，所以要检查老年代是否够用）
&lt;ol&gt;
&lt;li&gt;老年代&lt;strong&gt;剩余空间比新生代对象大&lt;/strong&gt;，直接&lt;strong&gt;Minor GC&lt;/strong&gt;结束。&lt;/li&gt;
&lt;li&gt;老年代&lt;strong&gt;剩余空间比新生代对象小&lt;/strong&gt;，这时候要看&lt;strong&gt;是否启用了老年代空间分配担保规则&lt;/strong&gt;
&lt;ol&gt;
&lt;li&gt;如果&lt;strong&gt;开启了&lt;/strong&gt; 老年代分配规则
&lt;ol&gt;
&lt;li&gt;老年代中剩余空间大小，&lt;strong&gt;大于历次Minor GC&lt;/strong&gt;之后剩余对象的大小，进行 Minor GC结束；&lt;/li&gt;
&lt;li&gt;老年代中剩余空间大小，&lt;strong&gt;小于历次Minor GC&lt;/strong&gt;之后剩余对象的大小，进行Full GC，把老年代空出来再检查。如果放的下那么进行Minor GC，如果放不下就抛OOM异常&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;没有开启&lt;/strong&gt;老年代分配规则，直接Full GC，之后如果放的下那么进行Minor GC，如果放不下就抛OOM异常&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;完整GC流程&quot; loading=&quot;lazy&quot; width=&quot;2064&quot; height=&quot;922&quot; src=&quot;/_astro/%E5%AE%8C%E6%95%B4GC%E6%B5%81%E7%A8%8B.DpaxAUjY_Uo5l3.webp&quot; srcset=&quot;/_astro/%E5%AE%8C%E6%95%B4GC%E6%B5%81%E7%A8%8B.DpaxAUjY_1zaWmW.webp 640w, /_astro/%E5%AE%8C%E6%95%B4GC%E6%B5%81%E7%A8%8B.DpaxAUjY_Z1xPbC3.webp 750w, /_astro/%E5%AE%8C%E6%95%B4GC%E6%B5%81%E7%A8%8B.DpaxAUjY_Z23FoIK.webp 828w, /_astro/%E5%AE%8C%E6%95%B4GC%E6%B5%81%E7%A8%8B.DpaxAUjY_10OCLx.webp 1080w, /_astro/%E5%AE%8C%E6%95%B4GC%E6%B5%81%E7%A8%8B.DpaxAUjY_Z1e2Okw.webp 1280w, /_astro/%E5%AE%8C%E6%95%B4GC%E6%B5%81%E7%A8%8B.DpaxAUjY_Z1XVxz0.webp 1668w, /_astro/%E5%AE%8C%E6%95%B4GC%E6%B5%81%E7%A8%8B.DpaxAUjY_Zqd8eY.webp 2048w, /_astro/%E5%AE%8C%E6%95%B4GC%E6%B5%81%E7%A8%8B.DpaxAUjY_Uo5l3.webp 2064w&quot; /&gt;&lt;figcaption&gt;完整GC流程&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;Full GC会导致什么？&lt;/strong&gt;&lt;a href=&quot;#full-gc会导致什么&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;Full GC会“Stop The World”，即在GC期间全程暂停用户的应用程序。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;对象如何晋升到老年代&lt;/strong&gt;？&lt;a href=&quot;#对象如何晋升到老年代&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;​	虚拟机给&lt;strong&gt;每个对象定义了一个对象年龄（Age）计数器&lt;/strong&gt;，存储在对象头中。对象通常在Eden区里诞生，如果&lt;strong&gt;经过第一次MinorGC&lt;/strong&gt;后仍然存活，并且能被Survivor容纳的话，该对象会被&lt;strong&gt;移动到Survivor空间&lt;/strong&gt;中，并且&lt;strong&gt;将其对象年龄设为1岁&lt;/strong&gt;。对象&lt;strong&gt;在Survivor区中每熬过一次MinorGC，年龄就增加1岁&lt;/strong&gt;，当它的年龄&lt;strong&gt;增加到一定程度（默认为15）&lt;/strong&gt;，就会被&lt;strong&gt;晋升到老年代&lt;/strong&gt;中。对象晋升老年代的年龄阈值，可以通过参数-XX：MaxTenuringThreshold设置。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;为什么要设置两个Survivor区域?&lt;/strong&gt;&lt;a href=&quot;#为什么要设置两个survivor区域&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;​	设置两个 Survivor 区最大的好处就是解决内存碎片化。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;一个Survivor 区的缺陷&lt;/strong&gt;：我们先假设一下，Survivor 只有一个区域会怎样。Minor GC 执行后，Eden 区被清空了，存活的对象放到了 Survivor 区，而之前 Survivor 区中的对象，可能也有一些是需要被清除的。问题来了，这时候我们怎么清除它们？在这种场景下，我们只能&lt;strong&gt;标记清除&lt;/strong&gt;，而我们知道标记清除最大的问题就是&lt;strong&gt;内存碎片&lt;/strong&gt;，在新生代这种经常会消亡的区域，采用标记清除必然会让内存产生严重的碎片化。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;两个Survivor 区的好处&lt;/strong&gt;：有 2 个区域，可以使用标记复制算法，所以每次 Minor GC，会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次Minor GC 时，From 与 To 职责兑换，这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域，以此反复。这种机制最大的好处就是，整个过程中，永远有一个 Survivor space 是空的，另一个非空的 Survivor space 是无碎片的&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;CMS垃圾收集器&lt;/strong&gt;&lt;a href=&quot;#cms垃圾收集器&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;CMS（Concurrent Mark Sweep）收集器是一种以获取最短回收停顿时间为目标的收集器。从名字上就可以看出CMS收集器是基于标记-清除算法实现的，它的运作过程分为四个步骤，包括：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;初始标记（CMS initial mark）；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;并发标记（CMS concurrent mark）；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;重新标记（CMS remark）；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;并发清除（CMS concurrent sweep）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;​      其中&lt;strong&gt;初始标记、重新标记&lt;/strong&gt;这两个步骤仍然需要“&lt;strong&gt;Stop The World&lt;/strong&gt;”。&lt;strong&gt;初始标记仅仅只是标记一下GC Roots能直接关联到的对象&lt;/strong&gt;，速度很快；&lt;strong&gt;并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程&lt;/strong&gt;，这个过程耗时较长但是不需要停顿用户线程，可以与垃圾收集线程一起并发运行；而&lt;strong&gt;重新标记阶段则是为了修正并发标记期间，因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录&lt;/strong&gt;，这个阶段的停顿时间通常会比初始标记阶段稍长一些，但也远比并发标记阶段的时间短；最后是&lt;strong&gt;并发清除阶段，清理删除掉标记阶段判断的已经死亡的对象&lt;/strong&gt;，由于不需要移动存活对象，所以这个阶段也是可以与用户线程同时并发的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;CMS缺点：&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;首先，CMS收集器对&lt;strong&gt;处理器资源非常敏感&lt;/strong&gt;。在并发阶段，它虽然不会导致用户线程停顿，但却会因为占用了一部分线程（或者说处理器的计算能力）而导致应用程序变慢，降低总吞吐量。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;然后，由于CMS收集器&lt;strong&gt;无法处理“浮动垃圾”&lt;/strong&gt;（Floating Garbage），有可能出现“Con-current ModeFailure”失败进而导致另一次完全“Stop TheWorld”的Full GC的产生。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;还有最后一个缺点，CMS是一款&lt;strong&gt;基于“标记-清除”算法&lt;/strong&gt;实现的收集器，这意味着收集结束时会有&lt;strong&gt;大量空间碎片&lt;/strong&gt;产生。空间碎片过多时，将会给大对象分配带来很大麻烦，往往会出现老年代还有很多剩余空间，但就是无法找到足够大的连续空间来分配当前对象，而不得不提前触发一次Full GC的情况。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;G1垃圾收集器：（Garbage First）&lt;/strong&gt;&lt;a href=&quot;#g1垃圾收集器garbage-first&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;​		Garbage First（简称G1）收集器是一款主要面向服务端应用的垃圾收集器。面向堆内存任何部分来组成回收集进行回收， 不再是要么面向新生代、要么面向老年代、要么面向整个Java堆，衡量标准不再是属于哪个分代，而是哪块内存中存放的 垃圾数量最多，回收收益最大，这就是G1收集器的Mixed GC模式。&lt;/p&gt;&lt;p&gt;​		虽然G1也仍是遵循分代收集理论设计的，但其堆内存的布局与其他收集器有非常明显的差异：G1不再坚持固定大小以及固定数量的分代区域划分，而是把连续的Java堆划分为多个大小相等的独立区域（Region），每一个Region都可以根据需要，扮演新生代的Eden空间、Survivor空间，或者老年代空间。收集器能够对扮演不同角色的Region采用不同的策略去处理，这样无论是新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;G1堆内存结构&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;466&quot; src=&quot;/_astro/G1%E5%A0%86%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.Dsh-Y2hF_Z80N6Y.webp&quot; srcset=&quot;/_astro/G1%E5%A0%86%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.Dsh-Y2hF_Z2n6w08.webp 640w, /_astro/G1%E5%A0%86%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.Dsh-Y2hF_i5zT5.webp 750w, /_astro/G1%E5%A0%86%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.Dsh-Y2hF_XI9ja.webp 828w, /_astro/G1%E5%A0%86%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.Dsh-Y2hF_2wLTSx.webp 1080w, /_astro/G1%E5%A0%86%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.Dsh-Y2hF_Z80N6Y.webp 1200w&quot; /&gt;&lt;figcaption&gt;G1堆内存结构&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;​		Region中还有一类特殊的Humongous区域，专门用来存储大对象。G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象。&lt;/p&gt;&lt;p&gt;​		虽然G1仍然保留新生代和老年代的概念，但新生代和老年代不再是固定的了，它们都是一系列区域（不需要连续）的动态集合。G1收集器之所以能建立可预测的停顿时间模型，是因为它&lt;strong&gt;将Region作为单次回收的最小单元&lt;/strong&gt;，即每次收集到的内存空间都是Region大小的整数倍，这样可以有计划地避免在整个Java堆中进行全区域的垃圾收集。更具体的处理思路是让G1收集器去跟踪各个Region里面的垃圾堆积的“价值”大小，价值即回收所获得的空间大小以及回收所需时间的经验值，然后在后台维护一个优先级列表，每次根据用户设定允许的收集停顿时间（使用参数-XX：MaxGCPauseMillis指定，默认值是200毫秒），&lt;strong&gt;优先处理回收价值收益最大的那些Region&lt;/strong&gt;，这也就是“Garbage First”名字的由来。这种使用Region划分内存空间，以及具有优先级的区域回收方式，保证了G1收集器在有限的时间内获取尽可能高的收集效率。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;G1的回收步骤：&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;初始标记&lt;/strong&gt;：仅仅标记GC Roots能直接关联到的对象。修改TAMS指针的值让下一阶段用户线程并发运行时能够正确的分配对象。需要停顿用户线程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;并发标记&lt;/strong&gt;：从GC Roots开始进行可达性分析，找出需要回收的对象，可与用户线程并发。扫描完成后还需要重新处理SATB记录下在并发时有引用变动的对象。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最终标记&lt;/strong&gt;：短暂的暂停用户线程，用于处理并发阶段发生结束后遗留下来的最后那少量的 SATB记录。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;筛选回收&lt;/strong&gt;：负责更新Region的统计数据，对各个Region的回收价值和成本进行排序，根据用户所期望的停顿时间来制定回收计划，可以自由选择任意多个Region构成回收集，然后决定回收的那一部分Region的存活对象复制到空的Region中，再清除整个旧的Region，这里涉及到存活对象的移动，所以需要暂停用户线程。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;4.4 内存泄露和内存溢出&lt;a href=&quot;#44-内存泄露和内存溢出&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;内存泄漏&lt;/strong&gt;（memory leak）：内存泄漏指程序运行过程中分配内存给临时变量，用完之后却没有被GC回收，始终占用着内存，既不能被使用也不能分配给其他程序，于是就发生了内存泄漏。根本原因是长生命周期的对象持有短生命周期对象的引用，尽管短生命周期的对象已经不再需要，但由于长生命周期对象持有它的引用而导致不能被回收。&lt;/p&gt;&lt;p&gt;产生情况：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;单例造成的内存泄漏：由于单例的静态特性使得其生命周期和应用的生命周期一样长，如果一个对象已经不再需要使用了，而单例对象还持有该对象的引用，就会使得该对象不能被正常回收，从而导致了内存泄漏。&lt;/li&gt;
&lt;li&gt;非静态内部类创建静态实例造成的内存泄漏&lt;/li&gt;
&lt;li&gt;Handler造成的内存泄漏&lt;/li&gt;
&lt;li&gt;线程造成的内存泄漏&lt;/li&gt;
&lt;li&gt;资源未关闭造成的内存泄漏&lt;/li&gt;
&lt;li&gt;集合容器中的内存泄露&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;避免内存泄漏的几点建议：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;尽早释放无用对象的引用。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;避免在循环中创建对象。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用字符串处理时避免使用String，应使用StringBuffer。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;尽量少使用静态变量，因为静态变量存放在永久代，基本不参与垃圾回收&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;内存溢出&lt;/strong&gt;（out of memory）：简单地说内存溢出就是指程序运行过程中申请的内存大于系统能够提供的内存，导致无法申请到足够的内存，于是就发生了内存溢出。&lt;/p&gt;&lt;p&gt;引起内存溢出的原因有很多种，常见的有以下几种：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;内存中加载的数据量过于庞大，如一次从数据库取出过多数据；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;集合类中有对对象的引用，使用完后未清空，使得JVM不能回收；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;代码中存在死循环或循环产生过多重复的对象实体；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用的第三方软件中的BUG；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;启动参数内存值设定的过小。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;内存溢出的解决方案：&lt;/p&gt;&lt;p&gt;第一步，修改JVM启动参数，直接增加内存。&lt;/p&gt;&lt;p&gt;第二步，检查错误日志，查看“OutOfMemory”错误前是否有其它异常或错误。&lt;/p&gt;&lt;p&gt;第三步，对代码进行走查和分析，找出可能发生内存溢出的位置。&lt;/p&gt;&lt;p&gt;第四步，使用内存查看工具动态查看内存使用情况。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;5、IO&lt;a href=&quot;#5io&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;5.1 NIO实现原理&lt;a href=&quot;#51-nio实现原理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;​		Java的NIO主要由三个核心部分组成：Channel、Buffer、Selector。&lt;/p&gt;&lt;p&gt;​		基本上，所有的IO在NIO中都从一个Channel开始，数据可以从Channel读到Buffer中，也可以从Buffer写到Channel中。Channel有好几种类型，其中比较常用的有FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel等，这些通道涵盖了UDP和TCP网络IO以及文件IO。&lt;/p&gt;&lt;p&gt;​		Buffer本质上是一块可以写入数据，然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象，并提供了一组方法，用来方便的访问该块内存。Java NIO里关键的Buffer实现有CharBuffer、ByteBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。这些Buffer覆盖了你能通过IO发送的基本数据类型，即byte、short、int、long、float、double、char。&lt;/p&gt;&lt;p&gt;​		Buffer对象包含三个重要的属性，分别是capacity、position、limit，其中position和limit的含义取决于Buffer处在读模式还是写模式。但不管Buffer处在什么模式，capacity的含义总是一样的。&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;capacity：作为一个内存块，Buffer有个固定的最大值，就是capacity。Buffer只能写capacity个数据，一旦Buffer满了，需要将其清空才能继续写数据往里写数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;position：当写数据到Buffer中时，position表示当前的位置。初始的position值为0。当一个数据写到Buffer后， position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity–1。当读取数据时，也是从某个特定位置读。当将Buffer从写模式切换到读模式，position会被重置为0。当从Buffer的position处读取数据时，position向前移动到下一个可读的位置。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;limit：在写模式下，Buffer的limit表示最多能往Buffer里写多少数据，此时limit等于capacity。当切换Buffer到读模式时， limit表示你最多能读到多少数据，此时limit会被设置成写模式下的position值。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;​	三个属性之间的关系，如下图所示：&lt;/p&gt;&lt;img src=&quot;./Acti1/NIO_1.png&quot; alt=&quot;NIO_1&quot; /&gt;&lt;p&gt;​		Selector允许单线程处理多个 Channel，如果你的应用打开了多个连接（通道），但每个连接的流量都很低，使用Selector就会很方便。要使用Selector，得向Selector注册Channel，然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回，线程就可以处理这些事件，事件例如有新连接进来，数据接收等。&lt;/p&gt;&lt;p&gt;​		这是在一个单线程中使用一个Selector处理3个Channel的图示：&lt;/p&gt;&lt;img src=&quot;./Acti1/NIO_2.png&quot; alt=&quot;NIO_2&quot; /&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;5.2 序列化与反序列化&lt;a href=&quot;#52-序列化与反序列化&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;​		序列化机制可以将对象转换成字节序列，这些字节序列可以保存在磁盘上，也可以在网络中传输，并允许程序将这些字节序列再次恢复成原来的对象。其中，对象的序列化（Serialize），是指将一个Java对象写入IO流中，对象的反序列化（Deserialize），则是指从IO流中恢复该Java对象。&lt;/p&gt;&lt;p&gt;​		若对象要支持序列化机制，则它的类需要实现Serializable接口，该接口是一个标记接口，它没有提供任何方法，只是标明该类是可以序列化的，Java的很多类已经实现了Serializable接口，如包装类、String、Date等。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;数据库&lt;a href=&quot;#数据库&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;section&gt;&lt;h2&gt;0、JDBC&lt;a href=&quot;#0jdbc&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;0.1 JDBC编程六步（需要背会）&lt;a href=&quot;#01-jdbc编程六步需要背会&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;​    第一步：注册驱动（告诉java程序，即将要连接是哪个品牌的数据库）&lt;/p&gt;&lt;p&gt;​    第二步：获取连接（表示JVM的进程很数据库进程之间的通道打开了，这属于进程之间的通信，重量级的，使用完之后一定要关闭）&lt;/p&gt;&lt;p&gt;​    第三步：获取数据库操作对象（专门执行SQL语句的对象）&lt;/p&gt;&lt;p&gt;​    第四步：执行SQL语句（DQL、DML）&lt;/p&gt;&lt;p&gt;​    第五步：处理查询结果（只有当第四步执行的是select语句的时候，才有这第五步的查询结果集）&lt;/p&gt;&lt;p&gt;​    第六步：释放资源（使用完资源之后，一定要关闭资源，java和数据库属于进程之间的通信，一定要关闭）&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;0.2 代码实现&lt;a href=&quot;#02-代码实现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;[] args) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Connection&lt;/span&gt;&lt;span&gt; conn &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;PreparedStatement&lt;/span&gt;&lt;span&gt; ps &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;Class&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;forName&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;com.mysql.cj.jdbc.Driver&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//注册驱动&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; url &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;jdbc:mysql://localhost:3306/cjm?useUnicode=true&amp;amp;characterEncoding=utf8&amp;amp;serverTimezone=GMT&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; userName &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;root&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; password &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;960915&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;conn &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DriverManager&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getConnection&lt;/span&gt;&lt;span&gt;&lt;span&gt;(url,userName,password);&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//获取连接（根据url、用户名、密码连接）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; sql&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;insert into users(userName,password,sex,email)&quot;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&quot;values(?,?,?,?)&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ps &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;conn&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;prepareStatement&lt;/span&gt;&lt;span&gt;&lt;span&gt;(sql);&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//获取数据库操作对象 PreparedStatement（可以预编译） Statement（不会预编译，存在SQL注入风险）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;ps&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&quot;allen&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//将SQL中占位符的参数替换成值，按照顺序一个一个设置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;ps&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&quot;123&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;ps&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&quot;男&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;ps&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&quot;allen@163.com&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; result &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ps&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;executeUpdate&lt;/span&gt;&lt;span&gt;&lt;span&gt;();&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//执行SQL&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;//处理查询结果（如果有的话）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;ClassNotFoundException&lt;/span&gt;&lt;span&gt; | &lt;/span&gt;&lt;span&gt;SQLException&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;printStackTrace&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;finally&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (ps &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;ps&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;close&lt;/span&gt;&lt;span&gt;&lt;span&gt;();&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//释放资源 先释放PreparedStatement&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;SQLException&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;throwables&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;throwables&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;printStackTrace&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (conn &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;conn&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;close&lt;/span&gt;&lt;span&gt;&lt;span&gt;();&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;//释放资源 再释放Connection&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;SQLException&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;throwables&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;throwables&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;printStackTrace&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;1、mysql (关系型数据库)&lt;a href=&quot;#1mysql-关系型数据库&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;1.1、sql语句&lt;a href=&quot;#11sql语句&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;DQL：（数据查询语言）查询语句，凡是select语句都是&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;DML：（数据操作语言）&lt;/span&gt;&lt;span&gt;insert&lt;/span&gt;&lt;span&gt;、&lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt;、&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;，对表当中的数据进行增删改&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;DDL：（数据定义语言）&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;、&lt;/span&gt;&lt;span&gt;drop&lt;/span&gt;&lt;span&gt;、alter对表进行增删改&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;TCL：（事务控制语言）commit提交事务，rollback回滚事务&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;DCL：（数据控制语言）grant授权，revoke撤销权限等&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;查询语句：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;select&lt;/span&gt;&lt;span&gt; 字段1，字段2。。。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; 表名&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; 条件1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;group by&lt;/span&gt;&lt;span&gt; 字段名   (分组)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;having&lt;/span&gt;&lt;span&gt; 条件       (分组条件)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;order by&lt;/span&gt;&lt;span&gt; 字段名   (排序)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;插入语句：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;insert into&lt;/span&gt;&lt;span&gt; 表名(字段名1,字段名2,字段名3..... ) &lt;/span&gt;&lt;span&gt;values&lt;/span&gt;&lt;span&gt;(值1, 值2 ,值3....);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;修改语句：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt; 表名 &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;&lt;span&gt; 字段名1 &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 值1，字段名2 &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 值2 . . .&lt;/span&gt;&lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; 条件;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;删除语句：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; 表名 &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; 条件 ;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;建表语句：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;table&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;表名&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;字段名1  数据类型  约束 ,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;字段名2  数据类型  约束 ,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;字段名3  数据类型  约束 ,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;字段名4  数据类型  约束 ,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.........&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;) ;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;删表语句：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;drop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;table&lt;/span&gt;&lt;span&gt; 表名；&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;section&gt;&lt;h4&gt;1.1.1 SQL执行顺序&lt;a href=&quot;#111-sql执行顺序&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;SQL&quot; loading=&quot;lazy&quot; width=&quot;517&quot; height=&quot;217&quot; src=&quot;/_astro/SQL.7SdmoA3Y_1TJdN7.webp&quot; srcset=&quot;/_astro/SQL.7SdmoA3Y_1TJdN7.webp 517w&quot; /&gt;&lt;figcaption&gt;SQL&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;一条SQL查询语句在MySQL中如何执行的？&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;先检查该语句&lt;code&gt;是否有权限&lt;/code&gt;，如果没有权限，直接返回错误信息，如果有权限会先查询缓存(MySQL8.0 版本以前)。&lt;/li&gt;
&lt;li&gt;如果没有缓存，分析器进行&lt;code&gt;词法分析&lt;/code&gt;，提取 sql 语句中 select 等关键元素，然后判断 sql 语句是否有语法错误，比如关键词是否正确等等。&lt;/li&gt;
&lt;li&gt;最后优化器确定执行方案进行权限校验，如果没有权限就直接返回错误信息，如果有权限就会&lt;code&gt;调用数据库引擎接口&lt;/code&gt;，返回执行结果。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.1.2 MySql分页语法&lt;a href=&quot;#112-mysql分页语法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;在MySQL中，SELECT语句默认返回所有匹配的行，它们可能是指定表中的每个行。为了返回第一行或前几行，可使用LIMIT子句，以实现分页查询。LIMIT子句的语法如下：&lt;strong&gt;LIMIT  偏移量  每页大小&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;-- 在所有的查询结果中，返回前5行记录。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; prod_name &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; products &lt;/span&gt;&lt;span&gt;LIMIT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;-- 在所有的查询结果中，从第5行开始，返回5行记录。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; prod_name &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; products &lt;/span&gt;&lt;span&gt;LIMIT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;优化LIMIT分页&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在偏移量非常大的时候，例如 LIMIT 10000,20 这样的查询，这时MySQL需要查询10020条记录然后只返回最后20条，前面的10000条记录都将被抛弃，这样的代价是非常高的。如果所有的页面被访问的频率都相同，那么这样的查询平均需要访问半个表的数据。要优化这种查询，要么是在页面中限制分页的数量，要么是优化大偏移量的性能。&lt;/p&gt;&lt;p&gt;优化此类分页查询的一个最简单的办法就是尽可能地使用索引覆盖扫描，而不是查询所有的列，然后根据需要做一次关联操作再返回所需的列。对于偏移量很大的时候，这样做的效率会提升非常大。考虑下面的查询：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; film_id,&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sakila&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;film&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ORDER BY&lt;/span&gt;&lt;span&gt; title &lt;/span&gt;&lt;span&gt;LIMIT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;如果这个表非常大，那么这个查询最好改写成下面的样子：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;film&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;film_id&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;film&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sakila&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;film&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;INNER JOIN&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; film_id &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sakila&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;film&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ORDER BY&lt;/span&gt;&lt;span&gt; title &lt;/span&gt;&lt;span&gt;LIMIT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; lim &lt;/span&gt;&lt;span&gt;USING&lt;/span&gt;&lt;span&gt;(film_id);//要求using()指定的列在两个表中均存在，并使用之用于join的条件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这里的“延迟关联”将大大提升查询效率，它让MySQL扫描尽可能少的页面，获取需要访问的记录后再根据关联列回原表查询需要的所有列。这个技术也可以用于优化关联查询中的LIMIT子句。&lt;/p&gt;&lt;p&gt;有时候也可以将LIMIT查询转换为已知位置的查询，让MySQL通过范围扫描获得对应的结果。例如，如果在一个位置列上有索引，并且预先计算出了边界值，上面的查询就可以改写为：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; film_id,&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;skila&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;film&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; position &lt;/span&gt;&lt;span&gt;BETWEEN&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AND&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;54&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ORDER BY&lt;/span&gt;&lt;span&gt; position;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;LIMIT和OFFSET的问题，其实是OFFSET的问题，它会导致MySQL扫描大量不需要的行然后再抛弃掉。如果可以使用书签记录上次取数的位置，那么下次就可以直接从该书签记录的位置开始扫描，这样就可以避免使用OFFSET。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.1.3 聚合函数&lt;a href=&quot;#113-聚合函数&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;常用的聚合函数有&lt;strong&gt;COUNT()、AVG()、SUM()、MAX()、MIN()&lt;/strong&gt;，下面以MySQL为例，说明这些函数的作用。&lt;/p&gt;&lt;p&gt;&lt;u&gt;&lt;strong&gt;COUNT()函数：&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;&lt;p&gt;COUNT()函数统计数据表中包含的记录行的总数，或者根据查询结果返回列中包含的数据行数，它有两种用法：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;**COUNT(*)**计算表中总的行数，不管某列是否有数值或者为空值。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;count(*)在innodb引擎和myisam引擎下有什么区别？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;myisam保存表的总行数,因此count(*),很快会返回表的总行数&lt;/p&gt;
&lt;p&gt;innodb查询count(*),mysql会对表进行全表或全索引扫描来确定行数&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;COUNT(字段名)&lt;strong&gt;计算指定列下总的行数，计算时将&lt;/strong&gt;忽略空值&lt;/strong&gt;的行。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;COUNT()函数可以与GROUP BY一起使用来计算每个分组的总和。&lt;/p&gt;&lt;p&gt;&lt;u&gt;&lt;strong&gt;AVG()函数()：&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;&lt;p&gt;AVG()函数通过计算返回的行数和每一行数据的和，求得指定列数据的平均值。&lt;/p&gt;&lt;p&gt;AVG()函数可以与GROUP BY一起使用，来计算每个分组的平均值。&lt;/p&gt;&lt;p&gt;&lt;u&gt;&lt;strong&gt;SUM()函数：&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;&lt;p&gt;SUM()是一个求总和的函数，返回指定列值的总和。&lt;/p&gt;&lt;p&gt;SUM()可以与GROUP BY一起使用，来计算每个分组的总和。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;MAX()函数：&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;MAX()返回指定列中的最大值。&lt;/p&gt;&lt;p&gt;MAX()也可以和GROUP BY关键字一起使用，求每个分组中的最大值。&lt;/p&gt;&lt;p&gt;MAX()函数不仅适用于查找数值类型，也可应用于字符类型。&lt;/p&gt;&lt;p&gt;&lt;u&gt;&lt;strong&gt;MIN()函数：&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;&lt;p&gt;MIN()返回查询列中的最小值。&lt;/p&gt;&lt;p&gt;MIN()也可以和GROUP BY关键字一起使用，求出每个分组中的最小值。&lt;/p&gt;&lt;p&gt;MIN()函数与MAX()函数类似，不仅适用于查找数值类型，也可应用于字符类型。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;where和having的区别？&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1. WHERE是一个约束声明，使用WHERE约束来自数据库的数据，WHERE是在结果返回之前起作用的，WHERE中不能使用聚合函数。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;2. HAVING是一个过滤声明，是在查询返回结果集以后对查询结果进行的过滤操作，在HAVING中可以使用聚合函数。另一方面，HAVING子句中不能使用除了分组字段和聚合函数之外的其他字段。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;从性能的角度来说，HAVING子句中如果使用了分组字段作为过滤条件，应该替换成WHERE子句。因为WHERE可以在执行分组操作和计算聚合函数之前过滤掉不需要的数据，性能会更好。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.1.4 表连接&lt;a href=&quot;#114-表连接&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;表与表之间常用的关联方式有两种：内连接、外连接，下面以MySQL为例来说明这两种连接方式。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;内连接：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;内连接通过INNER JOIN来实现，它将返回两张表中满足连接条件的数据，不满足条件的数据不会查询出来。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;外连接：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;外连接通过OUTER JOIN来实现，它会返回两张表中满足连接条件的数据，同时返回不满足连接条件的数据。外连接有两种形式：左外连接（LEFT OUTER JOIN）、右外连接（RIGHT OUTER JOIN）。&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;**左外连接：**可以简称为左连接（LEFT JOIN），它会返回左表中的所有记录和右表中满足连接条件的记录。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;**右外连接：**可以简称为右连接（RIGHT JOIN），它会返回右表中的所有记录和左表中满足连接条件的记录。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;除此之外，还有一种常见的连接方式：&lt;strong&gt;等值连接&lt;/strong&gt;。这种连接是通过WHERE子句中的条件，将两张表连接在一起，它的实际效果等同于内连接。出于语义清晰的考虑，一般更建议使用内连接，而不是等值连接。&lt;/p&gt;&lt;p&gt;实际上，外连接还有一种形式：&lt;strong&gt;完全外连接&lt;/strong&gt;（FULL OUTER JOIN），但&lt;strong&gt;MySQL不支持&lt;/strong&gt;这种形式&lt;/p&gt;&lt;p&gt;以上是从语法上来说明表与表之间关联的实现方式，而从表的关系上来说，比较常见的关联关系有：一对多关联、多对多关联、自关联。&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;一对多关联：这种关联形式最为常见，一般是两张表具有主从关系，并且以主表的主键关联从表的外键来实现这种关联关系。另外，以从表的角度来看，它们是具有多对一关系的，所以不再赘述多对一关联了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;多对多关联：这种关联关系比较复杂，如果两张表具有多对多的关系，那么它们之间需要有一张中间表来作为衔接，以实现这种关联关系。这个中间表要设计两列，分别存储那两张表的主键。因此，这两张表中的任何一方，都与中间表形成了一对多关系，从而在这个中间表上建立起了多对多关系。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;自关联：自关联就是一张表自己与自己相关联，为了避免表名的冲突，需要在关联时通过别名将它们当做两张表来看待。一般在表中数据具有层级（树状）时，可以采用自关联一次性查询出多层级的数据。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;如何将一张表的部分数据更新到另一张表上？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;可以采用&lt;strong&gt;关联更新&lt;/strong&gt;的方式，将一张表的部分数据，更新到另一张表内。参考如下代码：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt; b &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;col&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;col&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; a,b &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;;         #等值连接&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt; b &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;&lt;span&gt; col&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;col&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; b &lt;/span&gt;&lt;span&gt;inner join&lt;/span&gt;&lt;span&gt; a &lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;;   #内连接&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt; b &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;col&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;col&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; b &lt;/span&gt;&lt;span&gt;left Join&lt;/span&gt;&lt;span&gt; a &lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;;#外连接&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.1.5 谈谈对SQL注入的理解&lt;a href=&quot;#115-谈谈对sql注入的理解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	SQL注入的原理是将SQL代码伪装到输入参数中，传递到服务器解析并执行的一种攻击手法。也就是说，在一些对SERVER端发起的请求参数中植入一些SQL代码，SERVER端在执行SQL操作时，会拼接对应参数，同时也将一些SQL注入攻击的“SQL”拼接起来，导致会执行一些预期之外的操作。&lt;/p&gt;&lt;p&gt;举个例子：&lt;/p&gt;&lt;p&gt;​	比如我们的登录功能，其登录界面包括用户名和密码输入框以及提交按钮，登录时需要输入用户名和密码，然后提交。此时调用接口/user/login/ 加上参数username、password，首先连接数据库，然后后台对请求参数中携带的用户名、密码进行参数校验，即SQL的查询过程。假设正确的用户名和密码为ls和123456，输入正确的用户名和密码、提交，相当于调用了以下的SQL语句。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; * &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; user &lt;/span&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt;&lt;span&gt; username &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;ls&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AND&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;password&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;123456&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​	SQL中会将#及—以后的字符串当做注释处理，如果我们使用 ’ or 1=1 # 作为用户名参数，那么服务端构建的SQL语句就如下：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;select&lt;/span&gt;&lt;span&gt; * &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; user &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt;&lt;span&gt; username&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;or&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; #&lt;/span&gt;&lt;span&gt;&apos; and password=&apos;&lt;/span&gt;&lt;span&gt;123456&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;​	而#会忽略后面的语句，而1=1属于常等型条件，因此这个SQL将查询出所有的登录用户。其实上面的SQL注入只是在参数层面做了些手脚，如果是引入了一些功能性的SQL那就更危险了，比如上面的登录功能，如果用户名使用这个 ’ or 1=1;delete * from users; # ，那么在”;“之后相当于是另外一条新的SQL，这个SQL是删除全表，是非常危险的操作，因此SQL注入这种还是需要特别注意的。&lt;/p&gt;&lt;p&gt;如何&lt;strong&gt;解决SQL注入&lt;/strong&gt;：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;严格的参数校验&lt;/strong&gt;：参数校验就没得说了，在一些不该有特殊字符的参数中提前进行特殊字符校验即可。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;SQL预编译&lt;/strong&gt;：在知道了SQL注入的原理之后，我们同样也了解到MySQL有预编译的功能，指的是在服务器启动时，MySQL Client把SQL语句的模板（变量采用占位符进行占位）发送给MySQL服务器，MySQL服务器对SQL语句的模板进行编译，编译之后根据语句的优化分析对相应的索引进行优化，在最终绑定参数时把相应的参数传送给MySQL服务器，直接进行执行，节省了SQL查询时间，以及MySQL服务器的资源，达到一次编译、多次执行的目的，除此之外，还可以防止SQL注入。具体是怎样防止SQL注入的呢？实际上当将绑定的参数传到MySQL服务器，MySQL服务器对参数进行编译，即填充到相应的占位符的过程中，做了转义操作。我们常用的JDBC就有预编译功能，不仅提升性能，而且防止SQL注入。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.2、索引&lt;a href=&quot;#12索引&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;1.2.1 说一说对MySql索引的理解&lt;a href=&quot;#121-说一说对mysql索引的理解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;什么是索引？&lt;/strong&gt;&lt;a href=&quot;#什么是索引&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;​		索引是帮助MySQL高效获取数据的数据结构。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;索引能干什么?&lt;/strong&gt;&lt;a href=&quot;#索引能干什么&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;​		索引非常关键，尤其是当表中的数据量越来越大时，索引对于性能的影响愈发重要。索引能够轻易将查询性能提高好几个数量级，总的来说就是可以明显的提高查询效率。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;索引的分类？&lt;/strong&gt;&lt;a href=&quot;#索引的分类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;从&lt;strong&gt;存储结构&lt;/strong&gt;上来划分：B+Tree索引，Hash索引，full-index全文索引。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从&lt;strong&gt;应用层次&lt;/strong&gt;来分：普通索引，唯一索引，组合索引&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;根据&lt;strong&gt;数据的物理顺序与键值的逻辑&lt;/strong&gt;（索引）&lt;strong&gt;顺序&lt;/strong&gt;关系：聚集索引，非聚集索引。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;全文索引&lt;/strong&gt;：基于相似度的查询，在大数据量的情况下比like快很多。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;普通索引&lt;/strong&gt;：即一个索引只包含单个列，一个表可以有多个单列索引&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;唯一索引&lt;/strong&gt;：索引列的值必须唯一，但允许有空值&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;组合索引&lt;/strong&gt;：多列值组成一个索引，专门用于组合搜索，其效率大于索引合并，最左前缀原则，最左匹配？走索引：不走索引&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;最左前缀原则，就是最左优先，在创建多列索引时，要根据业务需求，where 子句中使用最频繁的一列放在最左边。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;当我们创建一个组合索引的时候，如 (a1,a2,a3)，相当于创建了（a1）、(a1,a2)和(a1,a2,a3)三个索引，这就是最左匹配原则。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;聚簇索引(聚集索引)&lt;/strong&gt;：并不是一种单独的索引类型，而是一种数据存储方式。具体细节取决于不同的实现，InnoDB的聚簇索引其实就是在同一个结构中保存了B+Tree索引和数据行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;非聚簇索引&lt;/strong&gt;：不是聚簇索引，就是非聚簇索引&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;聚簇索引是根据主键创建的一棵B+树，聚簇索引的叶子节点存放了表中的所有记录。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;辅助索引是根据索引键创建的一棵B+树，与聚簇索引不同的是，其叶子节点仅存放索引键值，以及该索引键值指向的主键。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;也就是说，如果通过辅助索引来查找数据，那么当找到辅助索引的叶子节点后，很有可能还需要根据主键值查找聚簇索引来得到数据，这种查找方式又被称为书签查找。因为辅助索引不包含行记录的所有数据，这就意味着每页可以存放更多的键值，因此其高度一般都要小于聚簇索引。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;索引的底层实现？&lt;/strong&gt;&lt;a href=&quot;#索引的底层实现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;​		mysql默认存储引擎InnoDB只显式支持B+Tree索引，对于频繁访问的表，innodb会透明建立自适应hash索引，即在B+树索引基础上建立hash索引，可以显著提高查找效率，对于客户端是透明的，不可控制的，隐式的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;哈希索引&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;​		基于哈希表实现，只有精确匹配索引所有列的查询才有效，对于每一行数据，存储引擎都会对所有的索引列计算一个哈希码（hash code），并且Hash索引将所有的哈希码存储在索引中，同时在索引表中保存指向每个数据行的指针。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;&lt;strong&gt;为什么官方建议使用自增长主键作为索引&lt;/strong&gt;？&lt;a href=&quot;#为什么官方建议使用自增长主键作为索引&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;​		结合B+Tree的特点，自增主键是连续的，在插入过程中尽量减少页分裂，即使要进行页分裂，也只会分裂很少一部分。并且能减少数据的移动，每次插入都是插入到最后。总之就是减少分裂和移动的频率。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.2.2 B-树、B+树索引&lt;a href=&quot;#122-b-树b树索引&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;介绍：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		我们在MySQL中的数据一般是放在磁盘中的，读取数据的时候肯定会有访问磁盘的操作，磁盘中有两个机械运动的部分，分别是盘片旋转和磁臂移动。盘片旋转就是我们市面上所提到的多少转每分钟，而磁盘移动则是在盘片旋转到指定位置以后，移动磁臂后开始进行数据的读写。那么这就存在一个定位到磁盘中的块的过程，而定位是磁盘的存取中花费时间比较大的一块，毕竟机械运动花费的时候要远远大于电子运动的时间。当大规模数据存储到磁盘中的时候，显然定位是一个非常花费时间的过程，但是我们可以通过B树进行优化，提高磁盘读取时定位的效率。&lt;/p&gt;&lt;p&gt;​		为什么B类树可以进行优化呢？我们可以根据B类树的特点，构造一个多阶的B类树，然后在尽量多的在结点上存储相关的信息，保证层数尽量的少，以便后面我们可以更快的找到信息，磁盘的I/O操作也少一些，而且B类树是平衡树，每个结点到叶子结点的高度都是相同，这也保证了每个查询是稳定的。&lt;/p&gt;&lt;p&gt;​		总的来说，B/B+树是为了磁盘或其它存储设备而设计的一种平衡多路查找树(相对于二叉，B树每个内节点有多个分支)，与红黑树相比，在相同的的节点的情况下，一颗B/B+树的高度远远小于红黑树的高度(在下面B/B+树的性能分析中会提到)。B/B+树上操作的时间通常由存取磁盘的时间和CPU计算时间这两部分构成，而CPU的速度非常快，所以B树的操作效率取决于访问磁盘的次数，关键字总数相同的情况下B树的高度越小，磁盘I/O所花的时间越少。&lt;/p&gt;&lt;p&gt;​		注意B-树就是B树，-只是一个符号。&lt;/p&gt;&lt;p&gt;**B-树索引：&lt;strong&gt;是一种多路平衡查找树，他的&lt;/strong&gt;每一个节点最多包含M个孩子，M就是B树的阶。**M的大小取决于磁盘页的大小。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;m阶B树：&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;树中&lt;strong&gt;每个结点至多有m个子结点（即M阶）；&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;若根结点不是叶子结点,则至少有2个子结点；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;除根结点和叶子结点外,其它每个结点至少有ceil(m/2)个子结点；&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;注：[ceil(m / 2)]个子结点（其中ceil(x)是一个取上限的函数）；&lt;/p&gt;&lt;p&gt;即&lt;strong&gt;中间节点最少有ceil(m/2)个子结点。&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;所有叶子结点都出现在同一层&lt;/strong&gt;，叶子结点不包含任何关键字信息；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;有k个子结点的非终端结点恰好包含有k-1个关键字(单节点里元素).&lt;/p&gt;
&lt;p&gt;每个节点中元素个数n必须满足： [ceil(m / 2)-1]&amp;lt;= n &amp;lt;= m-1。(&lt;strong&gt;即M阶树单节点最多有M-1个元素&lt;/strong&gt;)&lt;/p&gt;
&lt;p&gt;**每个结点中关键字从小到大排列，**并且当该结点的孩子是非叶子结点时，该k-1个关键字正好是k个孩子包含的关键字的值域的分划.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;​		&lt;strong&gt;B树不管叶子节点还是非叶子节点，都会保存数据&lt;/strong&gt;，这样导致在非叶子节点中能保存的指针数量变少（有些资料也称为扇出），指针少的情况下要保存大量数据，只能增加树的高度，导致IO操作变多，查询性能变低；&lt;/p&gt;&lt;p&gt;&lt;strong&gt;B+Tree索引&lt;/strong&gt;：（MySql的InnoDB引擎默认的索引）&lt;/p&gt;&lt;p&gt;​		是B-Tree的改进版本，同时也是数据库索引索引所采用的存储结构。&lt;strong&gt;数据都在叶子节点上&lt;/strong&gt;，并且&lt;strong&gt;增加了顺序访问指针&lt;/strong&gt;，每个叶子节点都指向相邻的叶子节点的地址。相比B-Tree来说，进行范围查找时只需要查找两个节点，进行遍历即可。而B-Tree需要获取所有节点，相比之下B+Tree效率更高。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;为什么说B+树比B树更适合数据库索引？&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;1、 B+树的磁盘读写代价更低：B+树的内部节点并没有指向关键字具体信息的指针，因此其内部节点相对B树更小，如果把所有同一内部节点的关键字存放在同一盘块中，那么盘块所能容纳的关键字数量也越多，一次性读入内存的需要查找的关键字也就越多，相对IO读写次数就降低了。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;2、B+树的查询效率更加稳定：由于非终结点并不是最终指向文件内容的结点，而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同，导致每一个数据的查询效率相当。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;3、由于B+树的数据都存储在叶子结点中，分支结点均为索引，方便扫库，只需要扫一遍叶子结点即可，但是B树因为其分支结点同样存储着数据，我们要找到具体的数据，需要进行一次中序遍历按序来扫，所以B+树更加适合在区间查询的情况，所以通常B+树用于数据库索引。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;B树在提高了IO性能的同时并没有解决元素遍历的效率低下的问题，正是为了解决这个问题，B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的，而B树不支持这样的操作或者说效率太低。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;InnoDB索引和MyISAM索引的区别：&lt;/strong&gt;（Mysql的不同引擎）&lt;/p&gt;&lt;p&gt;&lt;strong&gt;MyISAM引擎&lt;/strong&gt;使用B+Tree作为索引结构，叶节点的data域&lt;strong&gt;存放的是数据记录的地址&lt;/strong&gt;。在MyISAM中，主索引和辅助索引（Secondary key）在结构上没有任何区别，只是主索引要求key是唯一的，而辅助索引的key可以重复。&lt;/p&gt;&lt;p&gt;第一个重大区别是InnoDB的数据文件本身就是索引文件。从上文知道，MyISAM&lt;strong&gt;索引文件和数据文件是分离&lt;/strong&gt;的，索引文件仅保存数据记录的地址。而在InnoDB中，表数据文件本身就是按B+Tree组织的一个索引结构，这棵树的&lt;strong&gt;叶节点data域保存了完整的数据记录&lt;/strong&gt;。这个索引的key是数据表的主键，因此InnoDB表数据文件本身就是主索引。&lt;/p&gt;&lt;p&gt;第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录&lt;strong&gt;主键的值而不是地址&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;为什么索引结构默认使用B+Tree，而不是Hash，二叉树，红黑树？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;B-tree&lt;/strong&gt;：B+ 树非叶子节点上是不存储数据的，仅存储键值，而B树节点中不仅存储键值，也会存储数据。innodb中页的默认大小是16KB，如果不存储数据，那么就会存储更多的键值，相应的树的阶数（节点的子节点树）就会更大，树就会更矮更胖，如此一来我们查找数据进行磁盘的IO次数有会再次减少，数据查询的效率也会更快。&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;Hash&lt;/strong&gt;：虽然可以快速定位，但是没有顺序，IO复杂度高。&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;二叉树&lt;/strong&gt;：如果二叉树特殊化为一个链表，相当于全表扫描。平衡二叉树相比于二叉查找树来说，查找效率更稳定，总体的查找速度也更快。&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;平衡二叉树&lt;/strong&gt;：我们知道，在内存比在磁盘的数据，查询效率快得多。如果树这种数据结构作为索引，那我们每查找一次数据就需要从磁盘中读取一个节点，也就是我们说的一个磁盘块，但是平衡二叉树可是每个节点只存储一个键值和数据的，如果是B树，可以存储更多的节点数据，树的高度也会降低，因此读取磁盘的次数就降下来啦，查询效率就快啦。&lt;/p&gt;&lt;p&gt;​		&lt;strong&gt;红黑树&lt;/strong&gt;：树的高度随着数据量增加而增加，IO代价高&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.2.3 哈希索引&lt;a href=&quot;#123-哈希索引&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​		基于哈希表实现，只有匹配所有列的查询才有效。对于每一行数据，存储引擎都会对所有索引列计算一个哈希码，哈希码是一个较小的值，不同键值的行计算出的哈希码也不一样。哈希索引将所有的哈希码存储在索引中，同时保存指向每个数据行的指针。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Hash索引和B树索引有什么区别？&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;1. B+ 树可以进行范围查询，Hash 索引不能。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;2. B+ 树支持联合索引的最左侧原则，Hash 索引不支持。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;3. B+ 树支持 order by 排序，Hash 索引不支持。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;4. Hash 索引在等值查询上比 B+ 树效率更高。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;5. hash索引虽然在等值查询上较快，但是不稳定，性能不可预测，当某个键值存在大量重复的时候，发生hash碰撞，此时效率可能极差。而B+树的查询效率比较稳定，对于所有的查询都是从根节点到叶子节点，且树的高度较低。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;6. B+ 树使用 like 进行模糊查询的时候，like 后面（比如%开头）的话可以起到优化的作用，Hash 索引根本无法进行模糊查询&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.2.4 全文索引&lt;a href=&quot;#124-全文索引&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​		通过数值比较、范围过滤等就可以完成绝大多数我们需要的查询，但是，如果希望通过关键字的匹配来进行查询过滤，那么就需要基于相似度的查询，而不是原来的精确数值比较。全文索引就是为这种场景设计的。&lt;/p&gt;&lt;p&gt;​		like + % 在文本比较少时是合适的，但是对于大量的文本数据检索，是不可想象的。全文索引在大量的数据面前，能比 like + % 快 N 倍，速度不是一个数量级，但是全文索引可能存在精度问题。&lt;/p&gt;&lt;p&gt;​	开始之前，先说一下全文索引的版本、存储引擎、数据类型的支持情况&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- MySQL 5.6 以前的版本，只有 MyISAM 存储引擎支持全文索引；&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;MySQL 5.6 及以后的版本，MyISAM 和 InnoDB 存储引擎均支持全文索引;&lt;/li&gt;
&lt;li&gt;只有字段的数据类型为 char、varchar、text 及其系列才可以建全文索引。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.2.5 MySQL怎么判断要不要加索引？&lt;a href=&quot;#125-mysql怎么判断要不要加索引&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;当唯一性是某种数据本身的特征时，指定唯一索引。使用唯一索引需能确保定义的列的数据完整性，以提高查询速度。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在频繁进行排序或分组（即进行group by或order by操作）的列上建立索引，如果待排序的列有多个，可以在这些列上建立组合索引。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.2.6  只要创建了索引，就一定会走索引吗？&lt;a href=&quot;#126--只要创建了索引就一定会走索引吗&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	不一定。&lt;/p&gt;&lt;p&gt;​	比如，在使用组合索引的时候，如果没有遵从“最左前缀”的原则进行搜索，则索引是不起作用的。举例，假设在id、name、age字段上已经成功建立了一个名为MultiIdx的组合索引。索引行中按id、name、age的顺序存放，索引可以搜索id、（id,name）、（id, name, age）字段组合。如果列不构成索引最左面的前缀，那么MySQL不能使用局部索引，如（age）或者（name,age）组合则不能使用该索引查询。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.2.7  所有的字段都适合创建索引吗？&lt;a href=&quot;#127--所有的字段都适合创建索引吗&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;不是。&lt;/p&gt;&lt;p&gt;下列几种情况，是不适合创建索引的：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;频繁更新的字段不适合建立索引；&lt;/li&gt;
&lt;li&gt;where条件中用不到的字段不适合建立索引；&lt;/li&gt;
&lt;li&gt;数据比较少的表不需要建索引；&lt;/li&gt;
&lt;li&gt;数据重复且分布比较均匀的的字段不适合建索引，例如性别、真假值；&lt;/li&gt;
&lt;li&gt;参与列计算的列不适合建索引。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.2.8 索引失效&lt;a href=&quot;#128-索引失效&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	&lt;strong&gt;索引失效情况&lt;/strong&gt;：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;使用组合索引时，没有遵循“最左前缀”原则；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在索引列上做操作，例如计算、函数、类型转换，会导致索引失效而转向全表扫描；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用 select * 覆盖索引；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MySQL在使用不等于（!=或者&amp;lt;&amp;gt;）的时候无法使用索引会导致全表扫描；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;LIKE以通配符开头（%abc）MySQL索引会失效变成全表扫描的操作；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;字符串不加单引号会导致索引失效（可能发生了索引列的隐式转换）；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用or，用它来连接时会索引失效。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;​	可以采用以下几种方式，来&lt;strong&gt;避免索引失效：&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;使用组合索引时，需要遵循“最左前缀”原则；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不在索引列上做任何操作，例如计算、函数、类型转换，会导致索引失效而转向全表扫描；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;尽量使用覆盖索引（之访问索引列的查询），减少 select * 覆盖索引能减少回表次数；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MySQL在使用不等于（!=或者&amp;lt;&amp;gt;）的时候无法使用索引会导致全表扫描；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;LIKE以通配符开头（%abc）MySQL索引会失效变成全表扫描的操作；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;字符串不加单引号会导致索引失效（可能发生了索引列的隐式转换）；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;少用or，用它来连接时会索引失效。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.3、事务&lt;a href=&quot;#13事务&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;1.3.1  说一说你对数据库事务的了解&lt;a href=&quot;#131--说一说你对数据库事务的了解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​		事务可由一条非常简单的SQL语句组成，也可以由一组复杂的SQL语句组成。在事务中的操作，要么都执行修改，要么都不执行，这就是事务的目的，也是事务模型区别于文件系统的重要特征之一。&lt;/p&gt;&lt;p&gt;事务需遵循ACID四个特性：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A（atomicity），原子性&lt;/strong&gt;。原子性指整个数据库事务是不可分割的工作单位。只有使事务中所有的数据库操作都执行成功，整个事务的执行才算成功。事务中任何一个SQL语句执行失败，那么已没事经执行成功的SQL语句也必须撤销，数据库状态应该退回到执行事务前的状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;C（consistency），一致性&lt;/strong&gt;。一致性指事务将数据库从一种状态转变为另一种一致的状态。在事务开始之前和事务结束以后，数据库的完整性约束没有被破坏。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;I（isolation），隔离性&lt;/strong&gt;。事务的隔离性要求每个读写事务的对象与其他事务的操作对象能相互分离，即该事务提交前对其他事务都不可见，这通常使用锁来实现。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;D（durability） ，持久性&lt;/strong&gt;。事务一旦提交，其结果就是永久性的，即使发生宕机等故障，数据库也能将数据恢复。持久性保证的是事务系统的高可靠性，而不是高可用性。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.3.2 事务有哪几种类型，它们之间有什么区别？&lt;a href=&quot;#132-事务有哪几种类型它们之间有什么区别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;事务可以分为以下几种类型：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;扁平事务：是事务类型中最简单的一种，而在实际生产环境中，这可能是使用最为频繁的事务。在扁平事务中，所有操作都处于同一层次，其由BEGIN WORK开始，由COMMIT WORK或ROLLBACK WORK结束。处于之间的操作是原子的，要么都执行，要么都回滚。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;带有保存点的扁平事务：除了支持扁平事务支持的操作外，允许在事务执行过程中回滚到同一事务中较早的一个状态，这是因为可能某些事务在执行过程中出现的错误并不会对所有的操作都无效，放弃整个事务不合乎要求，开销也太大。保存点（savepoint）用来通知系统应该记住事务当前的状态，以便以后发生错误时，事务能回到该状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;链事务：可视为保存点模式的一个变种。链事务的思想是：在提交一个事务时，释放不需要的数据对象，将必要的处理上下文隐式地传给下一个要开始的事务。注意，提交事务操作和开始下一个事务操作将合并为一个原子操作。这意味着下一个事务将看到上一个事务的结果，就好像在一个事务中进行的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;嵌套事务：是一个层次结构框架。有一个顶层事务（top-level transaction）控制着各个层次的事务。顶层事务之下嵌套的事务被称为子事务（subtransaction），其控制每一个局部的变换。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分布式事务：通常是一个在分布式环境下运行的扁平事务，因此需要根据数据所在位置访问网络中的不同节点。对于分布式事务，同样需要满足ACID特性，要么都发生，要么都失效。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;对于MySQL的InnoDB存储引擎来说，它支持扁平事务、带有保存点的扁平事务、链事务、分布式事务。对于嵌套事务，MySQL数据库并不是原生的，因此对于有并行事务需求的用户来说MySQL就无能为力了，但是用户可以通过带有保存点的事务来模拟串行的嵌套事务。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.3.3 MySQL的ACID特性分别是怎么实现的？&lt;a href=&quot;#133-mysql的acid特性分别是怎么实现的&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;原子性实现原理：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		实现原子性的关键，是当事务回滚时能够撤销所有已经成功执行的sql语句。InnoDB实现回滚靠的是&lt;strong&gt;undo log&lt;/strong&gt;，当事务对数据库进行&lt;strong&gt;修改时&lt;/strong&gt;，InnoDB会&lt;strong&gt;生成对应的undo log&lt;/strong&gt;。如果事务执行&lt;strong&gt;失败或调用了rollback&lt;/strong&gt;，导致事务需要回滚，便可以&lt;strong&gt;利用undo log中的信息&lt;/strong&gt;将数据&lt;strong&gt;回滚到修改之前&lt;/strong&gt;的样子。&lt;/p&gt;&lt;p&gt;​		undo log属于逻辑日志，它记录的是sql执行相关的信息。当发生回滚时，InnoDB会根据undo log的内容做与之前相反的工作。对于insert，回滚时会执行delete。对于delete，回滚时会执行insert。对于update，回滚时则会执行相反的update，把数据改回去。&lt;/p&gt;&lt;p&gt;**持久性实现原理：**数据写入磁盘&lt;/p&gt;&lt;p&gt;​		InnoDB作为MySQL的存储引擎，数据是存放在磁盘中的，但如果每次读写数据都需要磁盘IO，效率会很低。为此，InnoDB提供了缓存(Buffer Pool)，Buffer Pool中包含了磁盘中部分数据页的映射，作为访问数据库的缓冲。&lt;strong&gt;当从数据库读取数据时，会首先从Buffer Pool中读取，如果Buffer Pool中没有，则从磁盘读取后放入Buffer Pool。当向数据库写入数据时，会首先写入Buffer Pool，Buffer Pool中修改的数据会定期刷新到磁盘中（这一过程称为刷脏）&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​		Buffer Pool的使用大大提高了读写数据的效率，但是也带了新的问题：&lt;strong&gt;如果MySQL宕机，而此时Buffer Pool中修改的数据还没有刷新到磁盘，就会导致数据的丢失，事务的持久性无法保证&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;​		于是，&lt;strong&gt;redo log被引入来解决这个问题&lt;/strong&gt;。当&lt;strong&gt;数据修改&lt;/strong&gt;时，除了修改Buffer Pool中的数据，还会&lt;strong&gt;在redo log记录&lt;/strong&gt;这次操作。当&lt;strong&gt;事务提交&lt;/strong&gt;时，会调用fsync接口&lt;strong&gt;对redo log进行刷盘&lt;/strong&gt;。如果&lt;strong&gt;MySQL宕机&lt;/strong&gt;，重启时可以&lt;strong&gt;读取redo log中的数据&lt;/strong&gt;，&lt;strong&gt;对数据库进行恢复&lt;/strong&gt;。redo log采用的是WAL（Write-ahead logging，&lt;strong&gt;预写式日志&lt;/strong&gt;），所有&lt;strong&gt;修改先写入日志&lt;/strong&gt;，再&lt;strong&gt;更新到Buffer Pool&lt;/strong&gt;，保证了数据不会因MySQL宕机而丢失，从而满足了持久性要求。&lt;/p&gt;&lt;p&gt;​		既然redo log也需要在事务提交时将日志写入磁盘，为什么它比直接将Buffer Pool中修改的数据写入磁盘(即刷脏)要快呢？主要有以下两方面的原因：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;刷脏是随机IO，因为每次修改的数据位置随机，但写redo log是追加操作，属于顺序IO。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;刷脏是以数据页（Page）为单位的，MySQL默认页大小是16KB，一个Page上一个小修改都要整页写入。而redo log中只包含真正需要写入的部分，无效IO大大减少。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;隔离性实现原理：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		隔离性追求的是并发情形下事务之间互不干扰。简单起见，我们主要考虑最简单的读操作和写操作(加锁读等特殊读操作会特殊说明)，那么隔离性的探讨，主要可以分为两个方面。&lt;/p&gt;&lt;p&gt;​		第一方面，&lt;strong&gt;(一个事务)写操作对(另一个事务)写操作的影响：锁机制保证隔离性。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		隔离性要求同一时刻只能有一个事务对数据进行写操作，InnoDB通过锁机制来保证这一点。锁机制的基本原理可以概括为：事务在修改数据之前，需要先获得相应的锁。获得锁之后，事务便可以修改数据。该事务操作期间，这部分数据是锁定的，其他事务如果需要修改数据，需要等待当前事务提交或回滚后释放锁。&lt;/p&gt;&lt;p&gt;​		按照粒度，锁可以分为表锁、行锁以及其他位于二者之间的锁。表锁在操作数据时会锁定整张表，并发性能较差。行锁则只锁定需要操作的数据，并发性能好。但是由于加锁本身需要消耗资源，因此在锁定数据较多情况下使用表锁可以节省大量资源。MySQL中不同的存储引擎支持的锁是不一样的，例如MyIsam只支持表锁，而InnoDB同时支持表锁和行锁，且出于性能考虑，绝大多数情况下使用的都是行锁。&lt;/p&gt;&lt;p&gt;​		第二方面，&lt;strong&gt;(一个事务)写操作对(另一个事务)读操作的影响：MVVC保证隔离性。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		InnoDB默认的隔离级别是RR（REPEATABLE READ），RR解决脏读、不可重复读、幻读等问题，使用的是MVCC。MVCC全称Multi-Version Concurrency Control，即&lt;strong&gt;多版本的并发控制协议&lt;/strong&gt;。它最大的优点是读不加锁，因此读写不冲突，并发性能好。InnoDB实现MVCC，多个版本的数据可以共存，主要基于以下技术及数据结构：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;隐藏列：InnoDB中每行数据都有隐藏列，隐藏列中包含了本行数据的事务id、指向undo log的指针等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;基于undo log的版本链：每行数据的隐藏列中包含了指向undo log的指针，而每条undo log也会指向更早版本的undo log，从而形成一条版本链。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ReadView：通过隐藏列和版本链，MySQL可以将数据恢复到指定版本。但是具体要恢复到哪个版本，则需要根据ReadView来确定。所谓ReadView，是指事务（记做事务A）在某一时刻给整个事务系统（trx_sys）打快照，之后再进行读操作时，会将读取到的数据中的事务id与trx_sys快照比较，从而判断数据对该ReadView是否可见，即对事务A是否可见。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;一致性实现原理：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​		可以说，一致性是事务追求的最终目标。前面提到的原子性、持久性和隔离性，都是为了保证数据库状态的一致性。此外，除了数据库层面的保障，一致性的实现也需要应用层面进行保障。实现一致性的措施包括：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;保证原子性、持久性和隔离性，如果这些特性无法保证，事务的一致性也无法保证。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;数据库本身提供保障，例如不允许向整形列插入字符串值、字符串长度不能超过列的限制等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;应用层面进行保障，例如如果转账操作只扣除转账者的余额，而没有增加接收者的余额，无论数据库实现的多么完美，也无法保证状态的一致。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.3.4 谈谈MySQL的事务隔离级别&lt;a href=&quot;#134-谈谈mysql的事务隔离级别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;并发情况下，读操作可能存在的三类问题：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;脏读：当前事务(A)中可以读到其他事务(B)未提交的数据（脏数据），这种现象是脏读。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不可重复读：在事务A中先后两次读取同一个数据，两次读取的结果不一样，这种现象称为不可重复读。脏读与不可重复读的区别在于：前者读到的是其他事务未提交的数据，后者读到的是其他事务已提交的数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;幻读：在事务A中按照某个条件先后两次查询数据库，两次查询结果的条数不同，这种现象称为幻读。不可重复读与幻读的区别可以通俗的理解为：前者是数据变了，后者是数据的行数变了。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;SQL 标准定义了四种隔离级别，这四种隔离级别分别是：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;读未提交（READ UNCOMMITTED）&lt;/strong&gt;：它是性能最好、也最野蛮的方式，因为它压根儿就不加锁，所以根本谈不上什么隔离效果，可以理解为&lt;strong&gt;没有隔离&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;读提交 （READ COMMITTED）&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;可重复读 （REPEATABLE READ）&lt;/strong&gt;：为了解决不可重复读，MySQL 采用了 &lt;strong&gt;MVVC (多版本并发控制)&lt;/strong&gt; 的方式。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;串行化 （SERIALIZABLE）&lt;/strong&gt;：&lt;strong&gt;读&lt;/strong&gt;的时候加&lt;strong&gt;共享锁&lt;/strong&gt;，其他事务可以并发读，但是不能写。&lt;strong&gt;写&lt;/strong&gt;的时候加&lt;strong&gt;排它锁&lt;/strong&gt;，其他事务不能并发写也不能并发读。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;mysql隔离级别&quot; loading=&quot;lazy&quot; width=&quot;1584&quot; height=&quot;441&quot; src=&quot;/_astro/mysql%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB.CB_oUOpU_Z1Nz06d.webp&quot; srcset=&quot;/_astro/mysql%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB.CB_oUOpU_1Lmoid.webp 640w, /_astro/mysql%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB.CB_oUOpU_ZzXOxR.webp 750w, /_astro/mysql%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB.CB_oUOpU_UI4PQ.webp 828w, /_astro/mysql%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB.CB_oUOpU_ZgxFI2.webp 1080w, /_astro/mysql%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB.CB_oUOpU_1pddrz.webp 1280w, /_astro/mysql%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB.CB_oUOpU_Z1Nz06d.webp 1584w&quot; /&gt;&lt;figcaption&gt;mysql隔离级别&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.3.5 如何实现可重复读&lt;a href=&quot;#135-如何实现可重复读&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​		为了实现可重复读，MySQL 采用了 &lt;strong&gt;MVVC (多版本并发控制)&lt;/strong&gt; 的方式。&lt;/p&gt;&lt;p&gt;​		我们在数据库表中看到的一行记录可能实际上有多个版本，每个版本的记录除了有数据本身外，还要有一个表示版本的字段，记为 row trx_id，而这个字段就是使其产生的事务的 id，事务 ID 记为transaction id，它在事务开始的时候向事务系统申请，按时间先后顺序递增。&lt;/p&gt;&lt;p&gt;可重复读是&lt;strong&gt;在事务开始&lt;/strong&gt;的时候&lt;strong&gt;生成一个当前事务全局性的快照&lt;/strong&gt;。对于一个快照来说，它能够&lt;strong&gt;读到哪些版本数据&lt;/strong&gt;，要遵循以下规则：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;当前&lt;strong&gt;事务内的更新&lt;/strong&gt;，可以读到；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;版本&lt;strong&gt;未提交&lt;/strong&gt;，不能读到；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;版本&lt;strong&gt;已提交&lt;/strong&gt;，但是却在&lt;strong&gt;快照创建后提交的&lt;/strong&gt;，不能读到；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;版本&lt;strong&gt;已提交&lt;/strong&gt;，且是在快照&lt;strong&gt;创建前提交&lt;/strong&gt;的，可以读到。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.3.6  如何解决幻读问题&lt;a href=&quot;#136--如何解决幻读问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​		MySQL 已经在可重复读隔离级别下解决了幻读的问题，用的是间隙锁。MySQL 把行锁和间隙锁合并在一起，解决了并发写和幻读的问题，这个锁叫做 Next-Key锁。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.4、锁&lt;a href=&quot;#14锁&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;1.4.1 数据库的锁&lt;a href=&quot;#141-数据库的锁&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	锁是数据库系统区别于文件系统的一个关键特性，&lt;strong&gt;锁机制用于管理对共享资源的并发访问&lt;/strong&gt;。下面我们以MySQL数据库的InnoDB引擎为例，来说明锁的一些特点。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;锁的类型：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;InnoDB存储引擎实现了如下两种标准的&lt;strong&gt;行级锁&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;共享锁（S Lock）&lt;/strong&gt;，允许事务读一行数据。&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;排他锁（X Lock）&lt;/strong&gt;，允许事务删除或更新一行数据。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;锁的粒度&lt;/strong&gt;：(锁的级别)&lt;/p&gt;&lt;p&gt;​	InnoDB存储引擎支持多粒度锁定，这种锁定允许事务&lt;strong&gt;在行级上的锁和表级上的锁同时存在&lt;/strong&gt;。为了支持在不同粒度上进行加锁操作，InnoDB存储引擎支持一种额外的锁方式，称之为意向锁。意向锁是将锁定的对象分为多个层次，意向锁意味着事务希望在更细粒度上进行加锁。&lt;/p&gt;&lt;p&gt;​	InnoDB存储引擎支持意向锁设计比较简练，其意向锁即为表级别的锁。设计目的主要是为了在一个事务中揭示下一行将被请求的锁类型。其支持两种意向锁：&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;意向共享锁（IS Lock）&lt;/strong&gt;，事务想要获得一张表中某几行的共享锁。&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;意向排他锁（IX Lock）&lt;/strong&gt;，事务想要获得一张表中某几行的排他锁。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;锁的粒度&quot; loading=&quot;lazy&quot; width=&quot;1580&quot; height=&quot;275&quot; src=&quot;/_astro/%E9%94%81%E7%9A%84%E7%B2%92%E5%BA%A6.CAmTbSPv_Z2fTV2n.webp&quot; srcset=&quot;/_astro/%E9%94%81%E7%9A%84%E7%B2%92%E5%BA%A6.CAmTbSPv_ZNl3uI.webp 640w, /_astro/%E9%94%81%E7%9A%84%E7%B2%92%E5%BA%A6.CAmTbSPv_gG1AI.webp 750w, /_astro/%E9%94%81%E7%9A%84%E7%B2%92%E5%BA%A6.CAmTbSPv_234JAH.webp 828w, /_astro/%E9%94%81%E7%9A%84%E7%B2%92%E5%BA%A6.CAmTbSPv_17hU8B.webp 1080w, /_astro/%E9%94%81%E7%9A%84%E7%B2%92%E5%BA%A6.CAmTbSPv_2ntzq3.webp 1280w, /_astro/%E9%94%81%E7%9A%84%E7%B2%92%E5%BA%A6.CAmTbSPv_Z2fTV2n.webp 1580w&quot; /&gt;&lt;figcaption&gt;锁的粒度&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;锁的算法：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;InnoDB存储引擎有3种行锁的算法，其分别是：&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;Record Lock&lt;/strong&gt;：单个行记录上的锁。给索引上的索引项加锁来实现的。&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;Gap Lock&lt;/strong&gt;：间隙锁，锁定一个范围，但不包含记录本身。是为了阻止多个事务将记录插入到同一范围内，而这会导致幻读问题的产&lt;/p&gt;&lt;p&gt;生。&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;Next-Key Lock&lt;/strong&gt;∶Gap Lock+Record Lock，锁定一个范围，并且锁定记录本身。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;关于死锁：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	死锁是指&lt;strong&gt;两个或两个以上的事务在执行过程中，因争夺锁资源而造成的一种互相等待的现象&lt;/strong&gt;。若无外力作用，事务都将无法推进下去。&lt;/p&gt;&lt;p&gt;​	解决死锁问题最简单的一种方法是&lt;strong&gt;超时&lt;/strong&gt;，即当两个事务互相等待时，当一个等待时间超过设置的某一阈值时，其中一个事务进行回滚，另一个等待的事务就能继续进行。&lt;/p&gt;&lt;p&gt;​	除了超时机制，当前数据库还都普遍采用**wait-for graph（等待图）**的方式来进行死锁检测。较之超时的解决方案，这是一种更为主动的死锁检测方式。InnoDB存储引擎也采用的这种方式。wait-for graph要求数据库保存以下两种信息：&lt;/p&gt;&lt;p&gt;​	锁的信息链表；&lt;/p&gt;&lt;p&gt;​	事务等待链表；&lt;/p&gt;&lt;p&gt;​	通过上述链表可以构造出一张图，而在这个图中若存在回路，就代表存在死锁，因此资源间相互发生等待。这是一种较为主动的死锁检测机制，在每个事务请求锁并发生等待时都会判断是否存在回路，若存在则有死锁，通常来说InnoDB存储引擎选择回滚undo量最小的事务。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;锁的升级：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;​	锁升级（Lock Escalation）是指将当前&lt;strong&gt;锁的粒度降低&lt;/strong&gt;。举例来说，数据库可以把一个表的1000个行锁升级为一个页锁，或者将页锁升级为表锁。&lt;/p&gt;&lt;p&gt;​	&lt;strong&gt;InnoDB存储引擎不存在锁升级的问题&lt;/strong&gt;。因为其不是根据每个记录来产生行锁的，相反，其根据每个事务访问的每个页对锁进行管理的，采用的是位图的方式。因此不管一个事务锁住页中一个记录还是多个记录，其开销通常都是一致的。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.4.2  InnoDB中行级锁是怎么实现的？&lt;a href=&quot;#142--innodb中行级锁是怎么实现的&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;​	InnoDB行级锁是通过给索引上的索引项加锁来实现的。只有通过索引条件检索数据，InnoDB才使用行级锁，否则，InnoDB将使用表锁。&lt;/p&gt;&lt;p&gt;​	当表中锁定其中的某几行时，不同的事务可以使用不同的索引锁定不同的行。另外，不论使用主键索引、唯一索引还是普通索引，InnoDB都会使用行锁来对数据加锁。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.4.3 mysql各引擎支持的锁？&lt;a href=&quot;#143-mysql各引擎支持的锁&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;myisam：表锁（MyISAM中是不会产生死锁的，因为MyISAM总是一次性获得所需的全部锁，要么全部满足，要么全部等待。）&lt;/p&gt;&lt;p&gt;innodb：行锁或表锁（在InnoDB中，锁是逐步获得的，就造成了死锁的可能。 ）（行级锁并不是直接锁记录，而是锁索引。）&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如果一条sql语句操作了主键索引，MySQL就会锁定这条主键索引；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果一条语句操作了非主键索引，MySQL会先锁定该非主键索引，再锁定相关的主键索引。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.4.4 mysql如何排查死锁？&lt;a href=&quot;#144-mysql如何排查死锁&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;查看死锁日志&lt;/li&gt;
&lt;li&gt;分析死锁日志
&lt;ol&gt;
&lt;li&gt;死锁日志分事务1，事务2拆分&lt;/li&gt;
&lt;li&gt;找出发生死锁的SQL&lt;/li&gt;
&lt;li&gt;找出事务持有什么锁，都在等待什么锁&lt;/li&gt;
&lt;li&gt;SQL加锁分析&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.5、优化&lt;a href=&quot;#15优化&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;1.5.1 说一说你对数据库优化的理解&lt;a href=&quot;#151-说一说你对数据库优化的理解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;MySQL数据库优化是多方面的，原则是减少系统的瓶颈，减少资源的占用，增加系统的反应速度。例如，通过优化文件系统，提高磁盘I\O的读写速度；通过优化操作系统调度策略，提高MySQL在高负荷情况下的负载能力；优化表结构、索引、查询语句等使查询响应更快。&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;针对&lt;strong&gt;查询&lt;/strong&gt;，我们可以通过&lt;strong&gt;使用索引&lt;/strong&gt;、&lt;strong&gt;使用连接代替子查询&lt;/strong&gt;的方式来提高查询速度。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;针对&lt;strong&gt;慢查询&lt;/strong&gt;，我们可以通过&lt;strong&gt;分析慢查询日志&lt;/strong&gt;，来发现引起慢查询的原因，从而有针对性的进行优化。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;针对&lt;strong&gt;插入&lt;/strong&gt;，我们可以通过&lt;strong&gt;禁用索引&lt;/strong&gt;、&lt;strong&gt;禁用检查&lt;/strong&gt;等方式来提高插入速度，在&lt;strong&gt;插入之后再启用索引和检查&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;针对&lt;strong&gt;数据库结构&lt;/strong&gt;，我们可以通过将&lt;strong&gt;字段很多的表拆分成多张表&lt;/strong&gt;、&lt;strong&gt;增加中间表&lt;/strong&gt;、&lt;strong&gt;增加冗余字段&lt;/strong&gt;等方式进行优化&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.5.2 该如何优化MySQL的查询？&lt;a href=&quot;#152-该如何优化mysql的查询&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用索引&lt;/strong&gt;：如果查询时没有使用索引，查询语句将扫描表中的所有记录。在数据量大的情况下，这样查询的速度会很慢。如果使用索引进行查询，查询语句可以根据索引快速定位到待查询记录，从而减少查询的记录数，达到提高查询速度的目的。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;有几种特殊情况，在这些情况下有可能使用带有索引的字段查询时索引并没有起作用:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;1. 使用LIKE关键字的查询语句&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;在使用LIKE关键字进行查询的查询语句中，如果匹配字符串的第一个字符为“%”，索引不会起作用。只有“%”不在第一个位置，索引才会起作用。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;2. 使用多列索引的查询语句&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;MySQL可以为多个字段创建索引。一个索引可以包括16个字段。对于多列索引，只有查询条件中使用了这些字段中的第1个字段时索引才会被使用。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;3. 使用OR关键字的查询语句&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;查询语句的查询条件中只有OR关键字，且OR前后的两个条件中的列都是索引时，查询中才使用索引。否则，查询将不使用索引。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;优化子查询&lt;/strong&gt;：使用子查询可以进行SELECT语句的嵌套查询，即一个SELECT查询的结果作为另一个SELECT语句的条件。子查询可以一次性完成很多逻辑上需要多个步骤才能完成的SQL操作。在MySQL中，可以使用连接（JOIN）查询来替代子查询。连接查询不需要建立临时表，其速度比子查询要快，如果查询中使用索引，性能会更好。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.5.3 怎样插入数据才能更高效？&lt;a href=&quot;#153-怎样插入数据才能更高效&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;影响插入速度的主要是索引、唯一性校验、一次插入记录条数等。针对这些情况，可以分别进行优化。&lt;/p&gt;&lt;p&gt;对于InnoDB引擎的表，常见的优化方法如下：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;禁用唯一性检查&lt;/strong&gt;：插入数据之前执行 set unique_checks=0 来禁止对唯一索引的检查，数据导入完成之后再运行set unique_checks=1 。这个和MyISAM引擎的使用方法一样。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;禁用外键检查&lt;/strong&gt;：插入数据之前执行禁止对外键的检查，数据插入完成之后再恢复对外键的检查。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;禁用自动提交&lt;/strong&gt;：插入数据之前禁止事务的自动提交，数据导入完成之后，执行恢复自动提交操作。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.5.4  表中包含几千万条数据该怎么办？**************&lt;a href=&quot;#154--表中包含几千万条数据该怎么办&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;优化SQL和索引；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;增加缓存，如memcached、redis；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;读写分离，可以采用主从复制，也可以采用主主复制；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用MySQL自带的分区表，这对应用是透明的，无需改代码，但SQL语句是要针对分区表做优化的；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;做垂直拆分，即根据模块的耦合度，将一个大的系统分为多个小的系统；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;做水平拆分，要选择一个合理的sharding key，为了有好的查询效率，表结构也要改动，做一定的冗余，应用也要改，sql中尽量带sharding key，将数据定位到限定的表上去查，而不是扫描全部的表。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.5.5  MySQL的慢查询&lt;a href=&quot;#155--mysql的慢查询&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;**慢查询核心字段？**怎么看执行计划（explain），如何理解其中各个字段的含义？&lt;/p&gt;&lt;p&gt;​	在 select 语句之前增加 explain 关键字，会返回执行计划的信息。&lt;/p&gt;&lt;img alt=&quot;explain&quot; /&gt;&lt;p&gt;（1）id 列：是 select 语句的序号，MySQL将 select 查询分为简单查询和复杂查询。&lt;/p&gt;&lt;p&gt;（2）select_type列：表示对应行是是简单还是复杂的查询。&lt;/p&gt;&lt;p&gt;（3）table 列：表示 explain 的一行正在访问哪个表。&lt;/p&gt;&lt;p&gt;（4）type 列：最重要的列之一。表示关联类型或访问类型，即 MySQL 决定如何查找表中的行。从最优到最差分别为：system &amp;gt; const &amp;gt; eq_ref &amp;gt; ref &amp;gt; fulltext &amp;gt; ref_or_null &amp;gt; index_merge &amp;gt; unique_subquery &amp;gt; index_subquery &amp;gt; range &amp;gt; index &amp;gt; ALL&lt;/p&gt;&lt;p&gt;（5）possible_keys 列：显示查询可能使用哪些索引来查找。&lt;/p&gt;&lt;p&gt;（6）key 列：这一列显示 mysql 实际采用哪个索引来优化对该表的访问。&lt;/p&gt;&lt;p&gt;（7）key_len 列：显示了mysql在索引里使用的字节数，通过这个值可以算出具体使用了索引中的哪些列。&lt;/p&gt;&lt;p&gt;（8）ref 列：这一列显示了在key列记录的索引中，表查找值所用到的列或常量，常见的有：const（常量），func，NULL，字段名。&lt;/p&gt;&lt;p&gt;（9）rows 列：这一列是 mysql 估计要读取并检测的行数，注意这个不是结果集里的行数。&lt;/p&gt;&lt;p&gt;（10）Extra 列：显示额外信息。比如有 Using index、Using where、Using temporary等。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;慢查询优化&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;开启慢查询日志MySQL中慢查询日志默认是关闭的，可以通过配置文件my.ini中的添加slow_query_log = ON，也可以在MySQL服务启动的时候使用 —log-slow-queries[=file_name] 启动慢查询日志。启动慢查询日志时，需要在my.ini文件中配置long_query_time=1选项指定记录阈值，如果某条查询语句的查询时间超过了这个值，这个查询过程将被记录到慢查询日志文件中。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分析慢查询日志：直接分析mysql慢查询日志，利用explain关键字可以模拟优化器执行SQL查询语句，来分析sql慢查询语句&lt;/p&gt;
&lt;p&gt;常见慢查询优化：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;索引没起作用的情况&lt;/li&gt;
&lt;li&gt;优化数据库结构&lt;/li&gt;
&lt;li&gt;分解关联查询&lt;/li&gt;
&lt;li&gt;优化LIMIT分页&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.5.6 日常工作中你是怎么优化SQL的？&lt;a href=&quot;#156-日常工作中你是怎么优化sql的&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;优化表结构&lt;/strong&gt;：
&lt;ol&gt;
&lt;li&gt;尽量使用数字型字段（字符型会降低查询和连接性能，增加存储开销）&lt;/li&gt;
&lt;li&gt;尽可能使用varchar代替char（边长的存储空间小，节省存储空间）&lt;/li&gt;
&lt;li&gt;当索引列有大量重复元素时，可以删除索引（如性别列 只有男、女、未知）&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优化查询&lt;/strong&gt;：
&lt;ol&gt;
&lt;li&gt;应尽量避免在 where 子句中使用!=或&amp;lt;&amp;gt;操作符&lt;/li&gt;
&lt;li&gt;应尽量避免在 where 子句中使用 or 来连接条件&lt;/li&gt;
&lt;li&gt;任何查询也不要出现select *
&lt;ol&gt;
&lt;li&gt;多取了不需要的列&lt;/li&gt;
&lt;li&gt;杜绝了覆盖索引的可能：使用select * 就表示肯定用不到覆盖索引（索引是高效找到行的一个方法，当能通过检索索引就可以读取想要的数据，那就不需要再到数据表中读取行了。如果一个索引包含了（或覆盖了）满足查询语句中字段与条件的数据就叫 做覆盖索引。）&lt;/li&gt;
&lt;li&gt;增加传输时间和网络开销&lt;/li&gt;
&lt;li&gt;增加了IO次数：表中存在字段是大字段例如text、varchar等，当字段超过728字节&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;避免在 where 子句中对字段进行 null 值判断&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;索引优化&lt;/strong&gt;：
&lt;ol&gt;
&lt;li&gt;对作为查询条件和 order by的字段建立索引&lt;/li&gt;
&lt;li&gt;避免建立过多的索引，多使用组合索引&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.5.6 mysql分库分表&lt;a href=&quot;#156-mysql分库分表&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;分库分表方案:&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;水平分库：以字段为依据，按照一定策略（hash、range等），将一个库中的数据拆分到多个库中。&lt;/li&gt;
&lt;li&gt;水平分表：以字段为依据，按照一定策略（hash、range等），将一个表中的数据拆分到多个表中。&lt;/li&gt;
&lt;li&gt;垂直分库：以表为依据，按照业务归属不同，将不同的表拆分到不同的库中。&lt;/li&gt;
&lt;li&gt;垂直分表：以字段为依据，按照字段的活跃性，将表中字段拆到不同的表（主表和扩展表）中。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;常用的分库分表中间件：&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;sharding-jdbc&lt;/li&gt;
&lt;li&gt;Mycat&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;分库分表可能遇到的问题&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;事务问题：需要用分布式事务啦&lt;/li&gt;
&lt;li&gt;跨节点Join的问题：解决这一问题可以分两次查询实现&lt;/li&gt;
&lt;li&gt;跨节点的count,order by,group by以及聚合函数问题：分别在各个节点上得到结果后在应用程序端进行合并。&lt;/li&gt;
&lt;li&gt;数据迁移，容量规划，扩容等问题&lt;/li&gt;
&lt;li&gt;ID问题：数据库被切分后，不能再依赖数据库自身的主键生成机制啦，最简单可以考虑UUID&lt;/li&gt;
&lt;li&gt;跨分片的排序分页问题&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.6、其他&lt;a href=&quot;#16其他&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;1.6.1 介绍一下数据库设计的三大范式&lt;a href=&quot;#161-介绍一下数据库设计的三大范式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;第一范式（1NF）：是指在关系模型中，对于添加的一个规范要求，所有的域都应该是原子性的，即数据库表的每一列都是不可分割的原子数据项，而不能是集合，数组，记录等非原子数据项。&lt;/li&gt;
&lt;li&gt;第二范式（2NF）：在1NF的基础上，非码属性必须完全依赖于候选码（在1NF基础上消除非主属性对主码的部分函数依赖）。&lt;/li&gt;
&lt;li&gt;第三范式（3NF）：在2NF基础上，任何非主属性不依赖于其它非主属性（在2NF基础上消除传递依赖）。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.6.2 说一说你对MySQL引擎的了解&lt;a href=&quot;#162-说一说你对mysql引擎的了解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;MySQL提供了多个不同的存储引擎，包括处理事务安全表的引擎和处理非事务安全表的引擎。在MySQL中，不需要在整个服务器中使用同一种存储引擎，针对具体的要求，可以对每一个表使用不同的存储引擎。MySQL 8.0支持的存储引擎有InnoDB、MyISAM、Memory、Merge、Archive、Federated、CSV、BLACKHOLE等。其中，最常用的引擎是InnoDB和MyISAM。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;InnoDB存储引擎：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;InnoDB是事务型数据库的首选引擎，支持事务安全表（ACID），支持行锁定和外键。MySQL 5.5.5之后，InnoDB作为默认存储引擎，主要特性如下：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;InnoDB给MySQL提供了具有提交、回滚和崩溃恢复能力的事务安全（ACID兼容）存储引擎。InnoDB锁定在行级并且也在SELECT语句中提供一个类似Oracle的非锁定读。这些功能增加了多用户部署和性能。在SQL查询中，可以自由地将InnoDB类型的表与其他MySQL表的类型混合起来，甚至在同一个查询中也可以混合。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;InnoDB是为处理巨大数据量的最大性能设计。它的CPU效率可能是任何其他基于磁盘的关系数据库引擎所不能匹敌的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;InnoDB存储引擎完全与MySQL服务器整合，为在主内存中缓存数据和索引而维持它自己的缓冲池。InnoDB将它的表和索引存在一个逻辑表空间中，表空间可以包含数个文件（或原始磁盘分区）。这与MyISAM表不同，比如在MyISAM表中每个表被存在分离的文件中。InnoDB表可以是任何尺寸，即使在文件尺寸被限制为2GB的操作系统上。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;InnoDB支持外键完整性约束（FOREIGN KEY）。存储表中的数据时，每张表的存储都按主键顺序存放，如果没有显示在表定义时指定主键，InnoDB会为每一行生成一个6B的ROWID，并以此作为主键。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;InnoDB被用在众多需要高性能的大型数据库站点上。InnoDB不创建目录，使用InnoDB时，MySQL将在数据目录下创建一个名为ibdata1的10MB大小的自动扩展数据文件，以及两个名为ib_logfile0和ib_logfile1的5MB大小的日志文件。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;MyISAM存储引擎&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;MyISAM基于ISAM存储引擎，并对其进行扩展。它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM拥有较高的插入、查询速度，但不支持事务。MyISAM的主要特性如下：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在支持大文件（达63位文件长度）的文件系统和操作系统上被支持。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;当把删除和更新及插入操作混合使用的时候，动态尺寸的行产生更少碎片。这要通过合并相邻被删除的块以及若下一个块被删除则扩展到下一块来自动完成。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每个MyISAM表最大的索引数是64，这可以通过重新编译来改变。每个索引最大的列数是16个。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大的键长度是1000B，这也可以通过编译来改变。对于键长度超过250B的情况，一个超过1024B的键将被用上。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;BLOB和TEXT列可以被索引。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;NULL值被允许在索引的列中，这个值占每个键的0~1个字节。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;所有数字键值以高字节优先被存储，以允许一个更高的索引压缩。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每个表一个AUTO_INCREMENT列的内部处理。MyISAM为INSERT和UPDATE操作自动更新这一列，这使得AUTO_INCREMENT列更快（至少10%）。在序列顶的值被删除之后就不能再利用。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可以把数据文件和索引文件放在不同目录。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每个字符列可以有不同的字符集。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;有VARCHAR的表可以固定或动态记录长度。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;VARCHAR和CHAR列可以多达64KB。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.6.3 说一说你对redo log、undo log、binlog的了解&lt;a href=&quot;#163-说一说你对redo-logundo-logbinlog的了解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;binlog（Binary Log）&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;二进制日志文件就是常说的binlog。&lt;strong&gt;二进制日志记录了MySQL所有修改数据库的操作，然后以二进制的形式记录在日志文件中&lt;/strong&gt;，其中还包括每条语句所执行的时间和所消耗的资源，以及相关的事务信息。默认情况下，二进制日志功能是开启的，启动时可以重新配置 —log-bin[=file_name] 选项，修改二进制日志存放的目录和文件名称。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;redo log：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;重做日志用来实现事务的持久性&lt;/strong&gt;，即事务ACID中的D。它由两部分组成：一是&lt;strong&gt;内存中的重做日志缓冲&lt;/strong&gt;（redo log buffer），其是易失的；二是&lt;strong&gt;重做日志文件&lt;/strong&gt;（redo log file），它是持久的。&lt;/p&gt;&lt;p&gt;InnoDB是事务的存储引擎，它通过Force Log at Commit机制实现事务的持久性，即当事务提交（COMMIT）时，必须先将该事务的所有日志写入到重做日志文件进行持久化，待事务的COMMIT操作完成才算完成。这里的日志是指重做日志，在InnoDB存储引擎中，由两部分组成，即redo log和undo log。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;redo log用来保证事务的持久性，undo log用来帮助事务回滚及MVCC的功能&lt;/strong&gt;。redo log基本上都是顺序写的，在数据库运行时不需要对redo log的文件进行读取操作。而undo log是需要进行随机读写的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;undo log：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;重做日志记录了事务的行为，可以很好地通过其对页进行“重做”操作。但是事务有时还需要进行回滚操作，这时就需要undo。因此在对数据库进行修改时，InnoDB存储引擎不但会产生redo，还会产生一定量的undo。这样如果用户执行的事务或语句由于某种原因失败了，又或者用户用一条ROLLBACK语句请求回滚，就可以利用这些undo信息将数据回滚到修改之前的样子。&lt;/p&gt;&lt;p&gt;redo存放在重做日志文件中，与redo不同，undo存放在数据库内部的一个特殊段（segment）中，这个段称为undo段（undo segment），undo段位于共享表空间内。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.6.4  MySQL主从同步是如何实现的？&lt;a href=&quot;#164--mysql主从同步是如何实现的&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;复制（replication）是MySQL数据库提供的一种高可用高性能的解决方案，一般用来建立大型的应用。&lt;/p&gt;&lt;p&gt;总体来说，replication的工作原理分为以下3个步骤：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;主服务器（master）把数据更改记录到二进制日志（binlog）中。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从服务器（slave）把主服务器的二进制日志复制到自己的中继日志（relay log）中。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从服务器重做中继日志中的日志，把更改应用到自己的数据库上，以达到数据的最终一致性。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;复制的工作原理并不复杂，其实就是一个完全备份加上二进制日志备份的还原。不同的是这个二进制日志的还原操作基本上实时在进行中。这里特别需要注意的是，复制不是完全实时地进行同步，而是异步实时。这中间存在主从服务器之间的执行延时，如果主服务器的压力很大，则可能导致主从服务器延时较大。复制的工作原理如下图所示，其中从服务器有2个线程，一个是I/O线程，负责读取主服务器的二进制日志，并将其保存为中继日志；另一个是SQL线程，复制执行中继日志。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1.6.5 Mysql的基础架构&lt;a href=&quot;#165-mysql的基础架构&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;img alt=&quot;Mysql架构&quot; /&gt;&lt;p&gt;​	Mysql逻辑架构图主要分三层：&lt;/p&gt;&lt;p&gt;（1）第一层负责连接处理，授权认证，安全等等&lt;/p&gt;&lt;p&gt;（2）第二层负责编译并优化SQL&lt;/p&gt;&lt;p&gt;（3）第三层是存储引擎。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;web开发&lt;a href=&quot;#web开发&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;section&gt;&lt;h2&gt;1、Mybatis&lt;a href=&quot;#1mybatis&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img alt=&quot;Hibernate&quot; loading=&quot;lazy&quot; width=&quot;1606&quot; height=&quot;392&quot; src=&quot;/_astro/Hibernate.BGU-J2QM_Z1dKDpo.webp&quot; srcset=&quot;/_astro/Hibernate.BGU-J2QM_23S4cy.webp 640w, /_astro/Hibernate.BGU-J2QM_JRfJh.webp 750w, /_astro/Hibernate.BGU-J2QM_1v8W4F.webp 828w, /_astro/Hibernate.BGU-J2QM_qpLrv.webp 1080w, /_astro/Hibernate.BGU-J2QM_1k7Bxs.webp 1280w, /_astro/Hibernate.BGU-J2QM_Z1dKDpo.webp 1606w&quot; /&gt;&lt;figcaption&gt;Hibernate&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;​		首先，都知道jpa的实现是著名的ssh中的h——&amp;gt;Hibernate。对Hibernate的理解：一个不用写sql语句就可以操作数据库的框架。JPA是Java Persistence API的简称，中文名Java持久层API，是JDK 5.0注解或XML描述对象－关系表的映射关系，并将运行期的实体对象持久化到数据库中。jpa规范对于单表的简单查询确实简单方便又实用。&lt;/p&gt;&lt;p&gt;​		MyBatis 作为一个半ORM框架，MyBatis 可以使用 XML 或注解来配置和映射原生信息，将 POJO映射成数据库中的记录，避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。称Mybatis是半自动ORM映射工具，是因为在查询关联对象或关联集合对象时，需要手动编写sql来完成。不像Hibernate这种全自动ORM映射工具，Hibernate查询关联对象或者关联集合对象时，可以根据对象关系模型直接获取。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;1.1 谈谈MyBatis和JPA的区别&lt;a href=&quot;#11-谈谈mybatis和jpa的区别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;ORM映射不同：&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;MyBatis是半自动的ORM框架，提供数据库与结果集的映射；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;JPA（默认采用Hibernate实现）是全自动的ORM框架，提供对象与数据库的映射。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;可移植性不同：&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;JPA通过它强大的映射结构和HQL语言，大大降低了对象与数据库的耦合性；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MyBatis由于需要写SQL，因此与数据库的耦合性直接取决于SQL的写法，如果SQL不具备通用性而用了很多数据库的特性SQL的话，移植性就会降低很多，移植时成本很高。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;日志系统的完整性不同&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;JPA日志系统非常健全、涉及广泛，包括：SQL记录、关系异常、优化警告、缓存提示、脏数据警告等；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MyBatis除了基本的记录功能外，日志功能薄弱很多。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;SQL优化上的区别&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;由于Mybatis的SQL都是写在XML里，因此优化SQL比Hibernate方便很多。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;而Hibernate的SQL很多都是自动生成的，无法直接维护SQL。虽有HQL，但功能还是不及SQL强大，见到报表等复杂需求时HQL就无能为力，也就是说HQL是有局限的Hhibernate虽然也支持原生SQL，但开发模式上却与ORM不同，需要转换思维，因此使用上不是非常方便。总之写SQL的灵活度上Hibernate不及Mybatis。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.2 Mybaits的优缺点：&lt;a href=&quot;#12-mybaits的优缺点&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;（1）优点：&lt;/p&gt;&lt;p&gt;​	① 基于SQL语句编程，相当灵活，不会对应用程序或者数据库的现有设计造成任何影响，SQL写在XML里，解除sql与程序代码的耦合，便于统一管理；提供XML标签，支持编写动态SQL语句，并可重用。&lt;/p&gt;&lt;p&gt;​	② 与JDBC相比，减少了50%以上的代码量，消除了JDBC大量冗余的代码，不需要手动开关连接；&lt;/p&gt;&lt;p&gt;​	③ 很好的与各种数据库兼容（因为MyBatis使用JDBC来连接数据库，所以只要JDBC支持的数据库MyBatis都支持）。&lt;/p&gt;&lt;p&gt;​	④ 能够与Spring很好的集成&lt;/p&gt;&lt;p&gt;​	⑤ 提供映射标签，支持对象与数据库的ORM字段关系映射；提供对象关系映射标签，支持对象关系组件维护。&lt;/p&gt;&lt;p&gt;（2）缺点：&lt;/p&gt;&lt;p&gt;​	① SQL语句的编写工作量较大，尤其当字段多、关联表多时，对开发人员编写SQL语句的功底有一定要求。&lt;/p&gt;&lt;p&gt;​	② SQL语句依赖于数据库，导致数据库移植性差，不能随意更换数据库。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.3 MyBatis输入输出支持的类型有哪些？&lt;a href=&quot;#13-mybatis输入输出支持的类型有哪些&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;parameterType&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;MyBatis支持多种输入输出类型，包括：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;简单的类型，如整数、小数、字符串等；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;集合类型，如Map等；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;自定义的JavaBean。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;其中，简单的类型，其数值直接映射到参数上。对于Map或JavaBean则将其属性按照名称映射到参数上。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.4 MyBatis中的$和#有什么区别？&lt;a href=&quot;#14-mybatis中的和有什么区别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;​		使用#设置参数时，MyBatis会创建预编译的SQL语句，然后在执行SQL时MyBatis会为预编译SQL中的占位符（?）赋值。预编译的SQL语句执行效率高，并且可以防止注入攻击。使用$设置参数时，MyBatis只是创建普通的SQL语句，然后在执行SQL语句时MyBatis将参数直接拼入到SQL里。这种方式在效率、安全性上均不如前者，但是可以解决一些特殊情况下的问题。例如，在一些动态表格（根据不同的条件产生不同的动态列）中，我们要传递SQL的列名，根据某些列进行排序，或者传递列名给SQL都是比较常见的场景，这就无法使用预编译的方式了。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;1.5 通常一个mapper.xml文件，都会对应一个Dao接口，这个Dao接口的工作原理是什么？Dao接口里的方法，参数不同时，方法能重载吗？&lt;a href=&quot;#15-通常一个mapperxml文件都会对应一个dao接口这个dao接口的工作原理是什么dao接口里的方法参数不同时方法能重载吗&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;​		Mapper 接口的工作原理是JDK动态代理，Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象proxy，代理对象会拦截接口方法，根据类的全限定名+方法名，唯一定位到一个MapperStatement并调用执行器执行所代表的sql，然后将sql执行结果返回。&lt;/p&gt;&lt;p&gt;​		Mapper接口里的方法，是不能重载的，因为是使用 全限名+方法名 的保存和寻找策略。&lt;/p&gt;&lt;p&gt;​		Dao接口即Mapper接口。接口的全限名，就是映射文件中的namespace的值；接口的方法名，就是映射文件中Mapper的Statement的id值；接口方法内的参数，就是传递给sql的参数。&lt;/p&gt;&lt;p&gt;​		当调用接口方法时，接口全限名+方法名拼接字符串作为key值，可唯一定位一个MapperStatement。在Mybatis中，每一个SQL标签，比如、、、标签，都会被解析为一个MapperStatement对象。​		举例：com.mybatis3.mappers.StudentDao.findStudentById，可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面 id 为 findStudentById 的 MapperStatement。1.6 MyBatis里如何实现一对多关联查询？#一对多映射有两种配置方式，都是使用collection标签实现的。在此之前，为了能够存储一对多的数据，需要在主表对应的实体类中增加集合属性，用于封装子表对应的实体类。嵌套查询：

通过select标签定义查询主表的SQL，返回结果通过reusltMap进行映射。


在resultMap中，除了映射主表属性，还要通过collection标签映射子表属性，该标签需包含如下
内容：
​		通过property属性指定子表属性名；
​		通过javaType属性指定封装子表属性的集合类型；
​		通过ofType属性指定子表的实体类型；
​		通过select属性指定查询子表所依赖的SQL，这个SQL需单独定义，内部包含查询子表的语句。

嵌套结果：

通过select标签定义关联查询主表和子表的SQL，返回结果通过resultMap进行映射。


在resultMap中，除了映射主表属性，还要通过collection标签映射子表属性，该标签需包含如下
内容：
​		通过property属性指定子表属性名；
​		通过ofType属性指定子表的实体类型；
​		通过result子标签定义子表字段和属性的映射关系。

既然不安全，为什么还需要不安全，为什么还需要不安全，为什么还需要，什么时候会用到它？​		它可以解决一些特殊情况下的问题。例如，在一些动态表格（根据不同的条件产生不同的动态列）中，我们要传递SQL的列名，根据某些列进行排序，或者传递列名给SQL都是比较常见的场景，这就无法使用预编译的方式了。1.7 MyBatis的xml文件和Mapper接口是怎么绑定的？#​		是通过xml文件中，  根标签的namespace属性进行绑定的，即namespace属性的值需要配置成接口的全限定名称，MyBatis内部就会通过这个值将这个接口与这个xml关联起来。1.8 Mybatis的SqlSession#​		SqlSession是Mybatis最重要的构建之一，可以简单的认为Mybatis一系列的配置目的是生成类似 JDBC生成的Connection对象的SqlSession对象，这样才能与数据库开启“沟通”，通过SqlSession可以实现增删改查（当然现在更加推荐是使用Mapper接口形式）（1）SqlSession简单原理介绍​		SqlSession提供select/insert/update/delete方法，在旧版本中使用使用SqlSession接口的这些方法，但是新版的Mybatis中就会建议使用Mapper接口的方法。　　映射器其实就是一个动态代理对象，进入到MapperMethod的execute方法就能简单找到SqlSession的删除、更新、查询、选择方法，从底层实现来说：通过动态代理技术，让接口跑起来，之后采用命令模式，最后还是采用了SqlSession的接口方法（getMapper()方法等到Mapper）执行SQL查询（也就是说Mapper接口方法的实现底层还是采用SqlSession接口方法实现的）。（2）SqlSession重要的四个对象​	1）Execute：调度执行StatementHandler、ParmmeterHandler、ResultHandler执行相应的SQL语句；​	2）StatementHandler：使用数据库中Statement（PrepareStatement）执行操作，即底层是封装好了的prepareStatement；​	3）ParammeterHandler：处理SQL参数；​	4）ResultHandler：结果集ResultSet封装处理返回。1.9 SqlSessionFactory#​		首先SqlSessionFactory与SqlSession都是同一个接口。SqlSessionFactory是通过SqlSessionFactoryBuilder的build方法创建的，而build方法创建的是一个SqlSessionFactory的实现类,叫DefaultSqlSessionFactory，然后这个实现类主要用的设计模式是建造者(build)模式,而里面最终要达到的一个目的是为了创建出DefaultSqlSession,这个是SqlSession的实现类.​		前面说了SqlSession也是同一个接口,那么SqlSession=SqlSessionFactory.openSession()就相当于SqlSession=DefaultSqlSession这个实现类.​		那么这个实现类是干什么的呢?     这个实现类可以进行增删查改以及事务操作等.​		那么DefaultSqlSession是怎么进行这些操作的呢?   答:通过调用Executor执行器.1.10 了解MyBatis缓存机制吗？#MyBatis的缓存分为一级缓存和二级缓存。一级缓存：​		一级缓存也叫本地缓存，它默认会启用，并且不能关闭。MyBatis 的一级缓存是在会话（SqlSession）层面进行缓存的。一级缓存存在于SqlSession的生命周期中，即它是SqlSession级别的缓存。在同一个 SqlSession 中查询时，MyBatis 会把执行的方法和参数通过算法生成缓存的键值，将键值和查询结果存入一个Map对象中。如果同一个SqlSession 中执行的方法和参数完全一致，那么通过算法会生成相同的键值，当Map 缓存对象中己经存在该键值时，则会返回缓存中的对象。二级缓存：​		二级缓存存在于SqlSessionFactory 的生命周期中，即它是SqlSessionFactory级别的缓存。若想使用二级缓存，需要在如下两处进行配置。在MyBatis 的全局配置settings 中有一个参数cacheEnabled，这个参数是二级缓存的全局开关，默认值是true ，初始状态为启用状态。MyBatis 的二级缓存是和命名空间绑定的，即二级缓存需要配置在Mapper.xml 映射文件中。在保证二级缓存的全局配置开启的情况下，给Mapper.xml 开启二级缓存只需要在Mapper. xml 中添加如下代码：1&amp;lt;cache /&amp;gt;二级缓存具有如下效果：​		映射语句文件中的所有SELECT 语句将会被缓存。​		映射语句文件中的所有时INSERT 、UPDATE 、DELETE 语句会刷新缓存。​		缓存会使用Least Rece ntly U sed ( LRU ，最近最少使用的）算法来收回。​		根据时间表（如no Flush Int erv al ，没有刷新间隔），缓存不会以任何时间顺序来刷新。​		缓存会存储集合或对象（无论查询方法返回什么类型的值）的1024 个引用。​		缓存会被视为read/write（可读／可写）的，意味着对象检索不是共享的，而且可以安全地被调用者修改，而不干扰其他调用者或线程所做的潜在修改。1.11 Mybatis动态sql有什么用？执行原理？有哪些动态sql？#​		Mybatis动态sql可以在Xml映射文件内，以标签的形式编写动态sql，执行原理是根据表达式的值 完成逻辑判断 并动态拼接sql的功能。​		Mybatis提供了9种动态sql标签：trim | where | set | foreach | if | choose | when | otherwise | bind。1.12 使用MyBatis的mapper接口调用时有哪些要求？#​	Mapper接口方法名和mapper.xml中定义的每个sql的id相同；​	Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同；​	Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同；​	Mapper.xml文件中的namespace即是mapper接口的类路径。2、Spring#2.1 请你说说Spring的核心是什么#​		Spring是一种轻量级框架，旨在提高开发人员的开发效率以及系统的可维护性。​		我们一般说的Spring框架就是Spring Framework，它是很多模块的集合，使用这些模块可以很方便地协助我们进行开发。这些模块是核心容器、数据访问/集成、Web、AOP（面向切面编程）、工具、消息和测试模块。比如Core Container中的Core组件是Spring所有组件的核心，Beans组件和Context组件是实现IOC和DI的基础，AOP组件用来实现面向切面编程。​		IoC（Inversion of Control）是控制反转的意思，这是一种面向对象编程的设计思想。在不采用这种思想的情况下，我们需要自己维护对象与对象之间的依赖关系，很容易造成对象之间的耦合度过高，在一个大型的项目中这十分的不利于代码的维护。IoC则可以解决这种问题，它可以帮我们维护对象与对象之间的依赖关系，降低对象之间的耦合度。​		说到IoC就不得不说DI（Dependency Injection），DI是依赖注入的意思，它是IoC实现的实现方式，就是说IoC是通过DI来实现的。由于IoC这个词汇比较抽象而DI却更直观，所以很多时候我们就用DI来代替它，在很多时候我们简单地将IoC和DI划等号，这是一种习惯。而实现依赖注入的关键是IoC容器，它的本质就是一个工厂。​		AOP（Aspect Oriented Programing）是面向切面编程思想，这种思想是对OOP的补充，它可以在OOP的基础上进一步提高编程的效率。简单来说，它可以统一解决一批组件的共性需求（如权限检查、记录日志、事务管理等）。在AOP思想下，我们可以将解决共性需求的代码独立出来，然后通过配置的方式，声明这些代码在什么地方、什么时机调用。当满足调用条件时，AOP会将该业务代码织入到我们指定的位置，从而统一解决了问题，又不需要修改这一批组件的代码。2.2 说一说你对Spring IOC的理解#​		Ioc—Inversion of Control，即“控制反转”，不是什么技术，而是一种设计思想。在传统的Java程序设计中，我们直接在对象内部通过new进行创建对象，是程序主动去创建依赖对象，我们直接获取依赖对象；而IoC是有专门一个容器来创建这些对象，即由Ioc容器来控制对象的创建及注入依赖对象；​		IOC容器是Spring用来实现IOC的载体，IOC容器实际上就是一个Map(key, value)，Map中存放的是各种对象。将对象之间的相互依赖关系交给IOC容器来管理，并由IOC容器完成对象的注入。这样可以很大程度上简化应用的开发，把应用从复杂的依赖关系中解放出来。IOC容器就像是一个工厂一样，当我们需要创建一个对象的时候，只需要配置好配置文件/注解即可，完全不用考虑对象是如何被创建出来的。​		DI—Dependency Injection，即“依赖注入”：组件之间依赖关系由容器在运行期决定，形象的说，即由容器动态的将某个依赖关系注入到组件之中。**依赖注入的目的并非为软件系统带来更多功能，而是为了提升组件重用的频率，并为系统搭建一个灵活、可扩展的平台。**通过依赖注入机制，我们只需要通过简单的配置，而无需任何代码就可指定目标需要的资源，完成自身的业务逻辑，而不需要关心具体的资源来自何处，由谁实现。2.3 说一说你对Spring AOP的理解#​		AOP （Aspect Orient Programming）,直译过来就是 面向切面编程。AOP 是一种编程思想，是面向对象编程（OOP）的一种补充。面向对象编程将程序抽象成各个层次的对象，而面向切面编程是将程序抽象成各个切面。​		Spring AOP是基于动态代理的，如果要代理的对象实现了某个接口，那么Spring AOP就会使用JDK动态代理去创建代理对象；而对于没有实现接口的对象，就无法使用JDK动态代理，转而使用CGlib动态代理生成一个被代理对象的子类来作为代理。​		AOP 要达到的效果是，保证开发者不修改源代码的前提下，去为系统中的业务组件添加某种通用功能。AOP的术语：

连接点（join point）：对应的是具体被拦截的对象，因为Spring只能支持方法，所以被拦截的对象往往就是指特定的方法，AOP将通过动态代理技术把它织入对应的流程中。（类里面哪些方法可以被增强，这些方法叫做连接点）


切点（point cut）：有时候，我们的切面不单单应用于单个方法，也可能是多个类的不同方法，这时，可以通过正则式和指示器的规则去定义，从而适配连接点。切点就是提供这样一个功能的概念。（实际增强的方法叫做切点）


通知（advice）：就是按照约定的流程下的方法，分为前置通知(在方法之前执行)、后置通知(在方法之后执行)、环绕通知(在方法之前或之后执行)、最终通知(在后置之后执行)和异常通知(在方法执行出现异常)，它会根据约定织入流程中。


目标对象（target）：即被代理对象。


引入（introduction）：是指引入新的类和其方法，增强现有Bean的功能。


织入（weaving）：它是一个通过动态代理技术，为原有服务对象生成代理对象，然后将与切点定义匹配的连接点拦截，并按约定将各类通知织入约定流程的过程。


切面（aspect）：是一个可以定义切点、各类通知和引入的内容，SpringAOP将通过它的信息来增强Bean的功能或者将对应的方法织入流程。（把增强应用到具体方法上面，过程叫做切面）

Spring AOP：AOP可以有多种实现方式，而Spring AOP支持如下两种实现方式。​		JDK动态代理：这是Java提供的动态代理技术，可以在运行时创建接口的实现类的代理实例。Spring AOP默认采用这种方式，在接口的代理实例中织入代码。​		CGLib动态代理：采用底层的字节码技术，在运行时创建子类代理的实例。当目标对象不存在接口实现类时，Spring AOP就会采用这种方式，在子类实例中织入代码。JDK动态代理：
定义一个接口及其实现类
自定义InvocationHandler的实现类并重写invoke方法，在invoke方法中调用原生方法(被代理类的方法)并自定义一些处理逻辑。
通过Proxy.newProxyInstance(ClassLoader     loader, Class&amp;lt;?&amp;gt;[] interfaces, InvocationHandler h);方法创建代理对象。
JDK动态代理和CGLIB动态代理对比
JDK动态代理只能代理实现了接口的实现类或者直接代理接口，而CGLIB可以代理为实现任何接口类。另外，CGLIB动态代理是通过生成一个被代理的子类来拦截被代理类的方法调用，因此不能声明成final类型的类和方法。
就效率来说大部分情况都是JDK动态代理更加优秀。
既然有没有接口都可以用CGLIB，为什么Spring还要使用JDK动态代理？​		在性能方面，CGLib创建的代理对象比JDK动态代理创建的代理对象高很多。但是，CGLib在创建代理对象时所花费的时间比JDK动态代理多很多。所以，对于单例的对象因为无需频繁创建代理对象，采用CGLib动态代理比较合适。反之，对于多例的对象因为需要频繁的创建代理对象，则JDK动态代理更合适。2.4 说一说你对Spring容器的了解#Spring主要提供了两种类型的容器：BeanFactory和ApplicationContext。

BeanFactory：是基础类型的IoC容器，提供完整的IoC服务支持。如果没有特殊指定，默认采用延迟初始化策略。只有当客户端对象需要访问容器中的某个受管对象的时候，才对该受管对象进行初始化以及依赖注入操作。所以，相对来说，容器启动初期速度较快，所需要的资源有限。对于资源有限，并且功能要求不是很严格的场景，BeanFactory是比较合适的IoC容器选择。


ApplicationContext：它是在BeanFactory的基础上构建的，是相对比较高级的容器实现，除了拥有BeanFactory的所有支持，ApplicationContext还提供了其他高级特性，比如事件发布、国际化信息支持等。ApplicationContext所管理的对象，在该类型容器启动之后，默认全部初始化并绑定完成。所以，相对于BeanFactory来说，ApplicationContext要求更多的系统资源，同时，因为在启动时就完成所有初始化，容器启动时间较之BeanFactory也会长一些。在那些系统资源充足，并且要求更多功能的场景中，ApplicationContext类型的容器是比较合适的选择。

2.5 说一说你对BeanFactory的了解#​		BeanFactory是一个类工厂，与传统类工厂不同的是，BeanFactory是类的通用工厂，它可以创建并管理各种类的对象。这些可被创建和管理的对象本身没有什么特别之处，仅是一个POJO，Spring称这些被创建和管理的Java对象为Bean。并且，Spring中所说的Bean比JavaBean更为宽泛一些，所有可以被Spring容器实例化并管理的Java类都可以成为Bean。​		BeanFactory是Spring容器的顶层接口，Spring为BeanFactory提供了多种实现，最常用的是XmlBeanFactory。但它在Spring 3.2中已被废弃，建议使用XmlBeanDefinitionReader、DefaultListableBeanFactory替代。BeanFactory最主要的方法就是 getBean(String beanName) ，该方法从容器中返回特定名称的Bean。2.6 Spring是如何管理Bean的？#​		Spring通过IoC容器来管理Bean，我们可以通过XML配置或者注解配置，来指导IoC容器对Bean的管理。因为注解配置比XML配置方便很多，所以现在大多时候会使用注解配置的方式。以下是管理Bean时常用的一些注解：

@ComponentScan用于声明扫描策略，通过它的声明，容器就知道要扫描哪些包下带有声明的类，也可以知道哪些特定的类是被排除在外的。


@Component、@Repository、@Service、@Controller用于声明Bean，它们的作用一样，但是语义不同。@Component用于声明通用的Bean，@Repository用于声明DAO层的Bean，@Service用于声明业务层的Bean，@Controller用于声明视图层的控制器Bean，被这些注解声明的类就可以被容器扫描并创建。


@Autowired、@Qualifier用于注入Bean，即告诉容器应该为当前属性注入哪个Bean。其中，@Autowired是按照Bean的类型进行匹配的，如果这个属性的类型具有多个Bean，就可以通过**@Qualifier指定Bean的名称**，以消除歧义。


@Scope用于声明Bean的作用域，默认情况下Bean是单例的，即在整个容器中这个类型只有一个实例。可以通过@Scope注解指定prototype值将其声明为多例的，也可以将Bean声明为session级作用域、request级作用域等等，但最常用的还是默认的单例模式。


@PostConstruct、@PreDestroy用于声明Bean的生命周期。其中，被**@PostConstruct修饰的方法将在Bean实例化后被调用**，@PreDestroy修饰的方法将在容器销毁前被调用。

2.7 说一说Bean的生命周期#Spring Bean的生命周期指的是从一个普通的Java类变成Bean的过程Bean生命周期11.  实例化Bean​		对于BeanFactory容器，当客户向容器请求一个尚未初始化的bean时，或初始化bean的时候需要注入另一个尚未初始化的依赖时，容器就会调用createBean进行实例化。
​		对于ApplicationContext容器，当容器启动结束后，便实例化所有的bean。
​		容器通过获取BeanDefinition对象中的信息进行实例化。并且这一步仅仅是简单的实例化，并未进行依赖注入。
​		实例化对象被包装在BeanWrapper对象中，BeanWrapper提供了设置对象属性的接口。2. 设置对象属性（依赖注入）​		实例化后的对象被封装在BeanWrapper对象中，并且此时对象仍然是一个原生的状态，并没有进行依赖注入。
​		紧接着，通过BeanWrapper提供的设置属性的接口根据BeanDefinition中的信息进行依赖注入。3. 处理Aware接口​	Spring会检测改对象是否实现了xxxAware接口，并将xxxAware实例注入给Bean：
如果这个Bean实现了BeanNameAware接口，会调用它实现的SetBeanName(String beanId)方法，此时传递的就是Spring配置文件中Bean的id值。
如果这个Bean实现了BeanFactoryAware接口，会调用它实现的SetBeanFactory()方法，此时传递的就是Spring工厂自身。
如果这个Bean实现了ApplicationContextAware接口，会调用它实现的SetApplicationContext(ApplicationContextAware)方法，传入Spring上下文。
**4. 执行 BeanPostProcessor **​	如果想对Bean进行一些自定义的处理，可以让Bean实现BeanPostProcessor 接口，那将会调用postProcessBeforeInitialization(Object obj,String s)方法5. 执行标注 @PostConstruct 注解的方法​	一般标注在 init 方法上，表示初始化方法6. 执行 InitializingBean与init-method​	如果Bean在Spring配置文件中配置了init-method属性。则会自动调用其配置文件的初始化方法。7. 如果这个Bean实现了BeanPostProcessor接口​	将会调用postProcessAfterInitialization(Object obj,String s)方法。由于这个方法是在Bean初始化结束时调用，所以可以被应用于内存或则缓存技术。8. 执行标注 @PreDestory注解的方法​	执行自定义销毁方法。9. DisposableBean​	当Bean不再需要时，会经过清理阶段，如果Bean实现了DisposableBean这个接口，会调用其实现的destory()方法。10. 调用Spring配置的destory-methodBean的生命周期11AOP可以发生在Spring生命周期的那个阶段？2.8 @Autowired和@Resource注解有什么区别？#

@Autowired是Spring提供的注解，@Resource是JDK提供的注解。


@Autowired是只能按类型注入，@Resource默认按名称注入，也支持按类型注入。


@Autowired按类型装配依赖对象，默认情况下它要求依赖对象必须存在，如果允许null值，可以设置它required属性为false，如果我们想使用按名称装配，可以结合@Qualifier注解一起使用。
@Resource有两个中重要的属性：name和type。name属性指定byName，如果没有指定name属性，当注解标注在字段上，即默认取字段的名称作为bean名称寻找依赖对象，当注解标注在属性的setter方法上，即默认取属性名作为bean名称寻找依赖对象。需要注意的是，@Resource如果没有指定name属性，并且按照默认的名称仍然找不到依赖对象时， @Resource注解会回退到按类型装配。但一旦指定了name属性，就只能按名称装配了。

2.9 Spring为什么建议使用构造器来注入？#2.9.1 spring的依赖注入方式#
基于 field 注入（属性注入）：所谓基于 field 注入，就是在bean的变量上使用注解进行依赖注入。本质上是通过反射的方式直接注入到field。这是平常开发中看的最多也是最熟悉的一种方式，同时，也正是 Spring 团队所不推荐的方式。比如：
1@Autowired2private Svc svc;1好处：这种方式非常的简洁，代码看起来很简单，通俗易懂。你的类可以专注于业务而不被依赖注入所污染。你只需要把@Autowired扔到变量之上就好了，不需要特殊的构造器或者set方法，依赖注入容器会提供你所需的依赖。2坏处：3（1）容易违背了单一职责原则：使用这种基于 field 注入的方式，添加依赖是很简单的，就算你的类中有十几个依赖你可能都觉得没有什么问题，普通的开发者很可能会无意识地给一个类添加很多的依赖。但是当使用构造器方式注入，到了某个特定的点，构造器中的参数变得太多以至于很明显地发现something is wrong。拥有太多的依赖通常意味着你的类要承担更多的责任，明显违背了单一职责原则（SRP：Single responsibility principle）。4（2）依赖注入与容器本身耦合：依赖注入框架的核心思想之一就是受容器管理的类不应该去依赖容器所使用的依赖。换句话说，这个类应该是一个简单的POJO(Plain Ordinary Java Object)能够被单独实例化并且你也能为它提供它所需的依赖。5  产生问题：1.类和依赖容器强耦合，不能在容器外使用6      2.类不能绕过反射（例如单元测试的时候）进行实例化，必须通过依赖容器才能实例化，这更像是集成测试7（3）不能使用属性注入的方式构建不可变对象(final 修饰的变量)1Spring开发团队的意见：不建议使用基于filed注入，（在使用IDEA 进行Spring 开发的时候，当你在字段上面使用@Autowired注解的时候，你会发现IDEA 会有警告提示：Field injection is not recommended）2（1）强制依赖就用构造器方式3（2）可选、可变的依赖就用setter 注入4当然你可以在同一个类中使用这两种方法。构造器注入更适合强制性的注入旨在不变性，Setter注入更适合可变性的注入。
基于 setter 注入：通过对应变量的setXXX()方法以及在方法上面使用注解，来完成依赖注入。比如：
1private Helper helper;2
3@Autowired4public void setHelper(Helper helper) {5    this.helper = helper;6}​	注：在 Spring 4.3 及以后的版本中，setter 上面的 @Autowired 注解是可以不写的。1基于 setter 的注入，则只应该被用于注入非必需的依赖，同时在类中应该对这个依赖提供一个合理的默认值。如果使用 setter 注入必需的依赖，那么将会有过多的 null 检查充斥在代码中。使用 setter 注入的一个优点是，这个依赖可以很方便的被改变或者重新注入。
基于 constructor 注入（构造器注入）：将各个必需的依赖全部放在带有注解构造方法的参数中，并在构造方法中完成对应变量的初始化，这种方式，就是基于构造方法的注入。比如：
1private final Svc svc;2
3@Autowired4public HelpService(@Qualifier(&quot;svcB&quot;) Svc svc) {5    this.svc = svc;6}​	注：在 Spring 4.3 及以后的版本中，如果这个类只有一个构造方法，那么这个构造方法上面也可以不写 @Autowired 注解。1Spring 团队提倡使用基于构造方法的注入，因为这样一方面可以将依赖注入到一个不可变的变量中 (注：final 修饰的变量)，另一方面也可以保证这些变量的值不会是 null。此外，经过构造方法完成依赖注入的组件 (注：比如各个 service)，在被调用时可以保证它们都完全准备好了。与此同时，从代码质量的角度来看，一个巨大的构造方法通常代表着出现了代码异味，这个类可能承担了过多的责任。2.9.2 @Autowired, @Resource,  @Inject 三个注解的区别#
@Autowired：@Autowired为Spring 框架提供的注解，需要导入包org.springframework.beans.factory.annotation.Autowired。

按照type在上下文中查找匹配的bean
如果有多个bean，则按照name进行匹配

如果有@Qualifier注解，则按照@Qualifier指定的name进行匹配
如果没有，则按照变量名进行匹配


匹配不到，则报错。（@Autowired(required=false)，如果设置required为false(默认为true)，则注入失败时不会抛出异常）


@Resource：@Resource是JSR-250定义的注解。Spring 在 CommonAnnotationBeanPostProcessor实现了对JSR-250的注解的处理，其中就包括@Resource。

@Resource有两个重要的属性：name和type，而Spring 将@Resource注解的name属性解析为bean的名字，而type属性则解析为bean的类型。

如果同时指定了name和type，则从Spring上下文中找到唯一匹配的bean进行装配，找不到则抛出异常。
如果指定了name，则从上下文中查找名称（id）匹配的bean进行装配，找不到则抛出异常。
如果指定了type，则从上下文中找到类型匹配的唯一bean进行装配，找不到或是找到多个，都会抛出异常。
如果既没有指定name，又没有指定type，则默认按照byName方式进行装配；如果没有匹配，按照byType进行装配。




@Inject：在Spring 的环境下，@Inject和@Autowired 是相同的，因为它们的依赖注入都是使用AutowiredAnnotationBeanPostProcessor来处理的。

@Inject是 JSR-330 定义的规范，如果使用这种方式，切换到Guice也是可以的。
如果硬要说两个的区别，首先@Inject是Java EE包里的，在SE环境需要单独引入。另一个区别在于@Autowired可以设置required=false而@Inject并没有这个属性。


2.10 Spring中默认提供的单例是线程安全的吗？#​		不是。​		Spring容器本身并没有提供Bean的线程安全策略。如果单例的Bean是一个无状态的Bean，即线程中的操作不会对Bean的成员执行查询以外的操作，那么这个单例的Bean是线程安全的。比如，Controller、Service、DAO这样的组件，通常都是单例且线程安全的。如果单例的Bean是一个有状态的Bean，则可以采用ThreadLocal对状态数据做线程隔离，来保证线程安全。​		有状态会话bean ：每个用户有自己特有的一个实例，在用户的生存期内，bean保持了用户的信息，即“有状态”；一旦用户灭亡（调用结束或实例结束），bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。简单来说，有状态就是有数据存储功能。有状态对象(Stateful Bean)，就是有实例变量的对象 ，可以保存数据，是非线程安全的。​		无状态会话bean ：bean一旦实例化就被加进会话池中，各个用户都可以共用。即使用户已经消亡，bean 的生命期也不一定结束，它可能依然存在于会话池中，供其他用户调用。由于没有特定的用户，那么也就不能保持某一用户的状态，所以叫无状态bean。但无状态会话bean 并非没有状态，如果它有自己的属性（变量），那么这些变量就会受到所有调用它的用户的影响，这是在实际应用中必须注意的。简单来说，无状态就是一次操作，不能保存数据。无状态对象(Stateless Bean)，就是没有实例变量的对象 .不能保存数据，是不变类，是线程安全的。2.11 Spring如何管理事务？#​		Spring为事务管理提供了一致的编程模板，在高层次上建立了统一的事务抽象。也就是说，不管是选择MyBatis、Hibernate、JPA还是Spring JDBC，Spring都可以让用户以统一的编程模型进行事务管理。Spring支持两种事务编程模型：

编程式事务
Spring提供了TransactionTemplate模板，利用该模板我们可以通过编程的方式实现事务管理，而无需关注资源获取、复用、释放、事务同步及异常处理等操作。相对于声明式事务来说，这种方式相对麻烦一些，但是好在更为灵活，我们可以将事务管理的范围控制的更为精确。


声明式事务
Spring事务管理的亮点在于声明式事务管理，它允许我们通过声明的方式，在IoC配置中指定事务的边界和事务属性，Spring会自动在指定的事务边界上应用事务属性。相对于编程式事务来说，这种方式十分的方便，只需要在需要做事务管理的方法上，增加**@Transactional**注解，以声明事务特征即可。

2.12 Spring的事务传播方式有哪些？#​		当我们调用一个业务方法时，它的内部可能会调用其他的业务方法，以完成一个完整的业务操作。这种业务方法嵌套调用的时候，如果这两个方法都是要保证事务的，那么就要通过Spring的事务传播机制控制当前事务如何传播到被嵌套调用的业务方法中。Spring在TransactionDefinition接口中规定了7种类型的事务传播行为，它们规定了事务方法和事务方法发生嵌套调用时如何进行传播，如下表：事务传播

隔离级别：



ISOLATION_DEFAULT：默认的事务隔离级别。mysql是ISOLATION_REPEATABLE_READ
ISOLATION_READ_UNCOMMITTED：读未提交。未解决任何并发问题。
ISOLATION_READ_COMMITTED：读已提交，解决脏读，存在不可重复读与幻读。
ISOLATION_REPEATABLE_READ：可重复读。解决脏读、不可重复读，存在幻读
ISOLATION_SERIALIZABLE：串行化，不存在并发问题。



事务的传播行为：控制业务方法是不是有事务的，是什么样的事务。
7个传播行为，表示你的业务方法调用时，事务在方法之间是如何使用的。

PROPAGATION_REQUIRED：指定的方法必须在事务内执行。若当前存在事务，就加到当前事务中；若当前没有事务，则创建一个新的事务。这种传播行为是最常见的选择，也是Spring默认的事务传播行为。
PROPAGATION_REQUIRES_NEW：指定的方法支持当前事务，但若当前没有事务，也可以以非事务方式执行。
PROPAGATION_SUPPORTS：总是新建一个事务，若当前存在事务，就将当前事务挂起，直到新事物执行完毕。
PROPAGATION_MANDATORY：
PROPAGATION_NESTED：
PROPAGATION_NEVER：
PROPAGATION_NOT_SUPPORT：


2.13 Spring的事务如何配置，常用注解有哪些？#​		事务的打开、回滚和提交是由事务管理器来完成的，我们使用不同的数据库访问框架，就要使用与之对应的事务管理器。在Spring Boot中，当你添加了数据库访问框架的起步依赖时，它就会进行自动配s置，即自动实例化正确的事务管理器。对于声明式事务，是使用**@Transactional进行标注**的。这个注解可以标注在类或者方法上。​		当它标注在类上时，代表这个类所有公共（public）非静态的方法都将启用事务功能。​		当它标注在方法上时，代表这个方法将启用事务功能。另外，在@Transactional注解上，我们可以使用isolation属性声明事务的隔离级别，使用propagation属性声明事务的传播机制。2.14 说一说你对声明式事务的理解#​		Spring事务管理的亮点在于声明式事务管理，它允许我们通过声明的方式，在IoC配置中指定事务的边界和事务属性，Spring会自动在指定的事务边界上应用事务属性。相对于编程式事务来说，这种方式十分的方便，只需要在需要做事务管理的方法上，增加@Transactional注解，以声明事务特征即可。3、SpringMVC#3.1 什么是MVC？#​		Spring MVC是一个基于MVC架构的用来简化web应用程序开发的应用开发框架，它是Spring的一部分，它和Struts2一样都属于表现层的框架。 MVC（Model模型 View 视图 Controller 控制器）：这是一种软件架构思想，是一种开发模式，将软件划分为三种不同类型的模块，分别是模型，视图，和控制器。 模型：用于封装业务逻辑处理（java类）； 视图：用于数据展现和操作界面（Servlet）； 控制器：用于协调视图和模型（jsp）； 处理流程：视图将请求发送给控制器，由控制器选择对应的模型来处理；模型将处理结果交给控制器，控制器选择合适的视图来展现处理结果；3.2 Spring MVC的五大组键 ？#img文字解析：
客户端请求提交到DispatcherServlet
由DispatcherServlet控制器查询一个或多个HandlerMapping，找到处理请求的Controller
DispatcherServlet将请求提交到Controller
Controller调用业务逻辑处理后，返回ModelAndView
DispatcherServlet查询一个或多个ViewResoler视图解析器，找到ModelAndView指定的视图 视图负责将结果显示到客户
3.2 DAO层是做什么的？#​		DAO是Data Access Obejct的缩写，即数据访问对象，在项目中它通常作为独立的一层，专门用于访问数据库。这一层的具体实现技术有很多，常用的有Spring JDBC、Hibernate、JPA、MyBatis等，在Spring框架下无论采用哪一种技术访问数据库，它的编程模式都是统一的。3.3 介绍一下Spring MVC的执行流程#

整个过程开始于客户端发出的一个HTTP请求，Web应用服务器接收到这个请求。如果匹配DispatcherServlet的请求映射路径，则Web容器将该请求转交给DispatcherServlet处理。


DispatcherServlet接收到这个请求后，将根据请求的信息（包括URL、HTTP方法、请求报文头、请求参数、Cookie等）及HandlerMapping的配置找到处理请求的处理器（Handler）。可将HandlerMapping看做路由控制器，将Handler看做目标主机。值得注意的是，在Spring MVC中并没有定义一个Handler接口，实际上任何一个Object都可以成为请求处理器。


当DispatcherServlet根据HandlerMapping得到对应当前请求的Handler后，通过HandlerAdapter对Handler进行封装，再以统一的适配器接口调用Handler。HandlerAdapter是Spring MVC框架级接口，顾名思义，HandlerAdapter是一个适配器，它用统一的接口对各种Handler方法进行调用。


处理器完成业务逻 辑的处理后，将返回一个ModelAndView给DispatcherServlet，ModelAndView包含了视图逻辑名和模型数据信息。


ModelAndView中包含的是“逻辑视图名”而非真正的视图对象，DispatcherServlet借由ViewResolver完成逻辑视图名到真实视图对象的解析工作。


当得到真实的视图对象View后，DispatcherServlet就使用这个View对象对ModelAndView中的模型数据进行视图渲染。


最终客户端得到的响应消息可能是一个普通的HTML页面，也可能是一个XML或JSON串，甚至是一张图片或一个PDF文档等不同的媒体形式。

3.4 说一说你知道的Spring MVC注解#@RequestMapping：​	作用：该注解的作用就是用来处理请求地址映射的，也就是说将其中的处理器方法映射到url路径上。​	属性：​		method：是让你指定请求的method的类型，比如常用的有get和post。​		value：是指请求的实际地址，如果是多个地址就用{}来指定就可以啦。​		produces：指定返回的内容类型，当request请求头中的Accept类型中包含指定的类型才可以返回的。​		consumes：指定处理请求的提交内容类型，比如一些json、html、text等的类型。​		headers：指定request中必须包含那些的headed值时，它才会用该方法处理请求的。​		params：指定request中一定要有的参数值，它才会使用该方法处理请求。@RequestParam：​	作用：是将请求参数绑定到你的控制器的方法参数上，是Spring MVC中的接收普通参数的注解。​	属性：​		value是请求参数中的名称。​		required是请求参数是否必须提供参数，它的默认是true，意思是表示必须提供。@RequestBody：​	作用：如果作用在方法上，就表示该方法的返回结果是直接按写入的Http responsebody中（一般在异步获取数据时使用的注解）。​	属性：required，是否必须有请求体。它的默认值是true，在使用该注解时，值得注意的当为true时get的请求方式是报错的，如果你取值为false的话，get的请求是null。@PathVaribale：​	作用：该注解是用于绑定url中的占位符，但是注意，spring3.0以后，url才开始支持占位符的，它是Spring MVC支持的rest风格url的一个重要的标志。3.5 介绍一下Spring MVC的拦截器#​		拦截器会对处理器进行拦截，这样通过拦截器就可以增强处理器的功能。Spring MVC中，所有的拦截器都需要实现HandlerInterceptor接口，该接口包含如下三个方法：preHandle()、postHandle()、afterCompletion()。SpringMVC拦截器通过上图可以看出，Spring MVC拦截器的执行流程如下：​		执行preHandle方法，它会返回一个布尔值。如果为false，则结束所有流程，如果为true，则执行下一步。​		执行处理器逻辑，它包含控制器的功能。​		执行postHandle方法。​		执行视图解析和视图渲染。​		执行afterCompletion方法。Spring MVC拦截器的开发步骤如下：

开发拦截器：
实现handlerInterceptor接口，从三个方法中选择合适的方法，实现拦截时要执行的具体业务逻辑。


注册拦截器：
定义配置类，并让它实现WebMvcConfigurer接口，在接口的addInterceptors方法中，注册拦截器，并定义该拦截器匹配哪些请求路径。

3.6 怎么去做请求拦截？#​	如果是对Controller进行拦截，则可以使用Spring MVC的拦截器。​	如果是对所有的请求（如访问静态资源的请求）进行拦截，则可以使用Filter。​	如果是对除了Controller之外的其他Bean的请求进行拦截，则可以使用Spring AOP。3.7 post和get的区别#
get是请求从指定的资源请求数据，用于获取数据，一般用于搜索排序和筛选 之类的操作。post是请求向指定的资源提交要被处理的数据，用于将数据发送给服务器， 一般用于修改和写入数据。
get的参数是通过url传递的且url中的参数长度是有限制的，而post的参数是在请求体中。
get使用url传递参数，因此是不安全的，不能用来传递敏感信息
get的请求会被浏览器主动缓存，因此参数会被完整的保留在浏览器记录中，而post请求不会被主动缓存，除非手动设置
对请求的参数类型，get只接受ACSII字符，而post没有限制
get在浏览器回退时是无害的，而post会再次提交请求。
3.8 cookie和session的区别是什么？#​		在程序中，会话跟踪是很重要的事情。理论上，一个用户的所有请求操作都应该属于同一个会话，而另一个用户的所有请求操作则应该属于另一个会话，二者不能混淆。例如，用户A在超市购买的任何商品都应该放在A的购物车内，不论是用户A什么时间购买的，这都是属于同一个会话的，不能放入用户B或用户C的购物车内，这不属于同一个会话。​		而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕，客户端与服务器端的连接就会关闭，再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。即用户A购买了一件商品放入购物车内，当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话，必须引入一种机制。cookie：由于HTTP是一种无状态的协议，服务器单从网络连接上无从知道客户身份。怎么办呢？就给客户端们颁发一个通行证吧，每人一个，无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。session：

存储位置不同：cookie存放于客户端；session存放于服务端。


存储容量不同：单个cookie保存的数据&amp;lt;=4KB，一个站点最多保存20个cookie；而session并没有上限。


存储方式不同：cookie只能保存ASCII字符串，并需要通过编码当时存储为Unicode字符或者二进制数据；session中能够存储任何类型的数据，例如字符串、整数、集合等。


隐私策略不同：cookie对客户端是可见的，别有用心的人可以分析存放在本地的cookie并进行cookie欺骗，所以它是不安全的；session存储在服务器上，对客户端是透明的，不存在敏感信息泄露的风险。


生命周期不同：可以通过设置cookie的属性，达到cookie长期有效的效果；session依赖于名为JSESSIONID的cookie，而该cookie的默认过期时间为-1，只需关闭窗口该session就会失效，因此session不能长期有效。Cookies生存期限就到你关闭浏览器为止 Session默认是30分钟。


服务器压力不同：cookie保存在客户端，不占用服务器资源；session保管在服务器上，每个用户都会产生一个session，如果并发量大的话，则会消耗大量的服务器内存。


浏览器支持不同：cookie是需要浏览器支持的，如果客户端禁用了cookie，则会话跟踪就会失效；运用session就需要使用URL重写的方式，所有用到session的URL都要进行重写，否则session会话跟踪也会失效。


跨域支持不同：cookie支持跨域访问，session不支持跨域访问。

4、SpringBoot#4.1 说说你对Spring Boot的理解#​		从本质上来说，Spring Boot就是Spring，它做了那些没有它你自己也会去做的Spring Bean配置。Spring Boot使用“习惯优于配置”的理念让你的项目快速地运行起来，使用Spring Boot很容易创建一个能独立运行、准生产级别、基于Spring框架的项目，使用Spring Boot你可以不用或者只需要很少的Spring配置。​		简而言之，Spring Boot本身并不提供Spring的核心功能，而是作为Spring的脚手架框架，以达到快速构建项目、预置三方配置、开箱即用的目的。Spring Boot有如下的优点：

可以快速构建项目；


可以对主流开发框架的无配置集成；


项目可独立运行，无需外部依赖Servlet容器；


提供运行时的应用监控；


可以极大地提高开发、部署效率；


可以与云计算天然集成。

4.2 Spring Boot启动流程#​		首先，Spring Boot项目创建完成会默认生成一个名为 *Application 的入口类，我们是通过该类的main方法启动Spring Boot项目的。在main方法中，通过SpringApplication的静态方法，即run方法进行SpringApplication类的实例化操作，然后再针对实例化对象调用另外一个run方法来完成整个项目的初始化和启动。SpringApplication调用的run方法的大致流程，如下图：其中，SpringApplication在run方法中重点做了以下操作：

获取监听器和参数配置；


打印Banner信息；


创建并初始化容器；


监听器发送通知。

​	当然，除了上述核心操作，run方法运行过程中还涉及启动时长统计、异常报告、启动日志、异常处理等辅助操作。比较完整的流程4.3 请描述Spring Boot自动装配的过程#SpringBoot自动装载​		整个自动装配的过程是：Spring Boot通过@SpringBootApplication里的@EnableAutoConfiguration注解开启自动配置，加载spring.factories中注册的各种AutoConfiguration类，当某个AutoConfiguration类满足其注解@Conditional指定的生效条件（Starters提供的依赖、配置或Spring容器中是否存在某个Bean等）时，实例化该AutoConfiguration类中定义的Bean（组件等），并注入Spring容器，就可以完成依赖框架的自动配置。
中间件#1、Redis#https://mp.weixin.qq.com/s/lyhMbWVhYKu2BmdyxZu5Og1.1 什么是Redis，Redis有哪些特点？#​		Redis是一种支持key-value等多种数据结构的存储系统，可用于缓存、事件发布或订阅，高速队列等场景。支持网络，提供字符串，哈希，列表，队列，集合结构直接存取，基于内存，可持久化。特点1：丰富的数据类型
传统的SQL是用来处理二维关系数据的
MemCached数据库，键和值都是字符串
文档型数据库(MongoDB)是由JSON/BSON组成的文档
​		Redis虽然也是键值对数据库，但和MEMCached不同：Redis不仅可以是字符串，还可以是五种数据结构中的任意一种。通过选用不同的存储结构，用户可以使用Redis解决不同的问题。特点2：内存存储数据库有两种，一种是硬盘数据库，一种是内存数据库​		硬盘数据库是把值存储在硬盘上，在内存中就存储一下索引，当硬盘数据库想访问硬盘的值时，它先在内存里找到索引，然后再找值。问题在于，在读取和写入硬盘的时候，如果读写比较多的时候，它会把硬盘的IO功能堵死。​		内存存储是讲所有的数据都存储在内存里面，数据读取和写入速度非常快。特点3：持久化功能​		将数据存储在内存里面的数据保存到硬盘中，保证数据安全，方便进行数据备份和恢复。1.2 Redis有哪些数据结构#Redis是key-value数据库，key的类型只能是String，但是value的数据类型就比较丰富了，主要包括五种：
string：字符串 INT、EMBSTR、RAW
hash：哈希  ZIPLIST、Hashtable

list：列表   ZIPLIST、LINKEDLIST


set：无序集合  INTSET、Hashtable
sorted set：有序集合  ZIPLIST、SKIPLIST
bitmap（位图）、hyperLogLogs（用于计算唯一事物的概率数据结构）、geospatial indexes（空间索引）
redis五大数据类型的底层实现：redis-数据类型底层实现1.2.1 string字符串#1set KEY_NAME VALUE2get KEY_NAME3incr KEY_NAME //根据key将value自增14decr KEY_NAME //根据key将value自减1​		string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象。string类型是Redis最基本的数据类型，一个键最大能存储512MB。1.2.2 hash哈希#1hset KEY_NAME FIELD VALUE2hget KEY_NAME FIELD​		Redis的hash 是一个键值(key—&amp;gt;value)对集合。Redis的hash是一个string类型的field和value的映射表，hash特别适合用于存储对象。1.2.3 list列表#1lpush KEY_NAME VALUE1.. VALUEN  //在 key 对应 list 的头部添加字符串元素(左边)2rpush KEY_NAME VALUE1..VALUEN  //在 key 对应 list 的尾部添加字符串元素(右边)3lrem KEY_NAME COUNT VALUE  //对应 list 中删除 count 个和 value 相同的元素4llen KEY_NAME  //返回 key 对应 list 的长度5lindex/rindex KEY_NAME // 从左边/右边 开始列表某个索引下标的值6lrange/rrange KEY_NAME // 从左边/右边 开始列表某索引范围的值7lpop/rpop KEY_NAME // 从左边/右边弹出一个值8blpop/brpop KEY_NAME：从列表的左侧/右侧弹出一个数据，若列表为空则进入阻塞状态。​		Redis 列表是简单的字符串列表，按照插入顺序排序。可以添加一个元素到列表的头部（左边）或者尾部（右边）1.2.4 set无序集合#1sadd KEY_NAME VALUE1...VALUEn //向集合添加元素 sadd key member1 member2……2scard KEY_NAME //统计集合中有多少元素3spop KEY_NAME //随机取出一个元素4smembers KEY_NAME //查看集合中剩余元素​		Redis的Set是string类型的无序集合。集合是通过哈希表实现的，所以添加，删除，查找的复杂度都是O(1)。1.2.5 sorted set有序集合#1zadd KEY_NAME SCORE1 VALUE1.. SCOREN VALUEN //向有序集合中添加元素已经对应的分数2zcard  KEY_NAME //统计集合中有多少元素3zscore KEY_NAME SCORE //查询某个成员的分数4zrank KEY_NAME  SCORE//查询某个成员的排名5zrange KEY_NAME range//查询排名在某个范围的成员​		Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。​		redis正是通过分数来为集合中的成员进行从小到大的排序。​		zset的成员是唯一的,但分数(score)却可以重复。1zset的底层结构是跳跃表，而与之类似的Java中的有序Set是TreeSet，使用红黑树实现的。2concurrent包里面，还有一个类叫做ConcurrentSkipListMap，从它的名字就可以看出来，也是用跳跃表实现的，这个和zset最像。3
4跳表是什么？5  跳表是一个随机化的数据结构，实质就是一种可以进行二分查找的有序链表。6  跳表在原有的有序链表上面增加了多级索引，通过索引来实现快速查找。7  跳表不仅能提高搜索性能，同时也可以提高插入和删除操作的性能。1.3 一个字符串类型的值能存储最大容量是多少？#​		查询官方文档（https://redis.io/topics/data-types）可以看到String类型的value值最多支持的长度为512M，所以正确的答案是512M。1.4 能说一下Redis每种数据结构的使用场景#（1）string的应用场景：字符串类型的使用场景：信息缓存、计数器、分布式锁等等。常用命令：get/set/del/incr/decr/incrby/decrbykey的定义一般使用:连接例如：pageview:userid实战场景1：记录每一个用户的访问次数，或者记录每一个商品的浏览次数​		使用理由：每一个用户访问次数或者商品浏览次数的修改是很频繁的，如果使用mysql这种文件系统频繁修改会造成mysql压力，效率也低。而使用redis的好处有二：使用内存，很快；单线程，所以无竞争，数据不会被改乱。实战场景2：缓存频繁读取，但是不常修改的信息，如用户信息，视频信息：​		业务逻辑上：先从redis读取，有值就从redis读取，没有则从mysql读取，并写一份到redis中作为缓存，注意要设置过期时间。实战场景3：限定某个ip特定时间内的访问次数​		用key记录IP，value记录访问次数，同时key的过期时间设置为60秒，如果key过期了则重新设置，否则进行判断，当一分钟内访问超过100次，则禁止访问。实战场景4:分布式session​		我们知道session是以文件的形式保存在服务器中的；如果你的应用做了负载均衡，将网站的项目放在多个服务器上，当用户在服务器A上进行登陆，session文件会写在A服务器；当用户跳转页面时，请求被分配到B服务器上的时候，就找不到这个session文件，用户就要重新登陆。​		如果想要多个服务器共享一个session，可以将session存放在redis中，redis可以独立于所有负载均衡服务器，也可以放在其中一台负载均衡服务器上；但是所有应用所在的服务器连接的都是同一个redis服务器。（2）hash的使用场景​		当对象的某个属性需要频繁修改时，不适合用string+json，因为它不够灵活，每次修改都需要重新将整个对象序列化并赋值；如果使用hash类型，则可以针对某个属性单独修改，没有序列化，也不需要修改整个对象。比如，商品的价格、销量、关注数、评价数等可能经常发生变化的属性，就适合存储在hash类型里。（3）list的使用场景​		列表本质是一个有序的，元素可重复的队列。​		实战场景：定时排行榜（4）set的使用场景​		集合的特点是无序性和确定性（不重复）。​		实战场景：收藏夹（5）sorted set的使用场景​		有序集合的特点是有序，无重复值。与set不同的是sorted set每个元素都会关联一个score属性，redis正是通过score来为集合中的成员进行从小到大的排序。​		实战场景：实时排行榜1.5 Redis如何做持久化的？能说一下RDB和AOF的实现原理吗？#什么是持久化？​		持久化（Persistence），即把数据（如内存中的对象）保存到可永久保存的存储设备中（如磁盘）。持久化的主要应用是将内存中的对象存储在数据库中，或者存储在磁盘文件中、XML数据文件中等等。Redis为什么要持久化？​		Redis是内存数据库，为了保证效率所有的操作都是在内存中完成。数据都是缓存在内存中，当你重启系统或者关闭系统，之前缓存在内存中的数据都会丢失再也不能找回。因此为了避免这种情况，Redis需要实现持久化将内存中的数据存储起来。Redis如何实现持久化？Redis官方提供了不同级别的持久化方式：
RDB持久化：能够在指定的时间间隔能对你的数据进行快照存储。
AOF持久化：记录每次对服务器写的操作，当服务器重启的时候会重新执行这些命令来恢复原始的数据，AOF命令以redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写，使得AOF文件的体积不至于过大。
不使用持久化：如果你只希望你的数据在服务器运行的时候存在，你也可以选择不使用任何持久化方式。
同时开启RDB和AOF：你也可以同时开启两种持久化方式，在这种情况下当redis重启的时候会优先载入AOF文件来恢复原始的数据，因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
1.5.1 RDB持久化#​		RDB(Redis Database)持久化是把当前内存数据生成快照保存到硬盘的过程，触发RDB持久化过程分为手动触发和自动触发。（1）手动触发​		手动触发对应save命令，会阻塞当前Redis服务器，直到RDB过程完成为止，对于内存比较大的实例会造成长时间阻塞，线上环境不建议使用。（2）自动触发​		自动触发对应bgsave命令，Redis进程执行fork操作创建子进程，RDB持久化过程由子进程负责，完成后自动结束。阻塞只发生在fork阶段，一般时间很短。​		在redis.conf配置文件中可以配置：1save &amp;lt;seconds&amp;gt; &amp;lt;changes&amp;gt;​		表示xx秒内数据修改xx次时自动触发bgsave。如果想关闭自动触发，可以在save命令后面加一个空串，即：1save &quot;&quot;还有其他常见可以触发bgsave，如：
如果从节点执行全量复制操作，主节点自动执行bgsave生成RDB文件并发送给从节点。
默认情况下执行shutdown命令时，如果没有开启AOF持久化功能则自动执行bgsave。
bgsave工作机制：bgsave（1）执行bgsave命令，Redis父进程判断当前是否存在正在执行的子进 程，如RDB/AOF子进程，如果存在，bgsave命令直接返回。（2）父进程执行fork操作创建子进程，fork操作过程中父进程会阻塞，通过info stats命令查看latest_fork_usec选项，可以获取最近一个fork操作的耗时，单位为微秒（3）父进程fork完成后，bgsave命令返回“Background saving started”信息并不再阻塞父进程，可以继续响应其他命令。（4）子进程创建RDB文件，根据父进程内存生成临时快照文件，完成后对原有文件进行原子替换。执行lastsave命令可以获取最后一次生成RDB的 时间，对应info统计的rdb_last_save_time选项。（5）进程发送信号给父进程表示完成，父进程更新统计信息，具体见 info Persistence下的rdb_*相关选项。1.5.2 AOF持久化#​		AOF（append only file）持久化：以独立日志的方式记录每次写命令， 重启时再重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用是解决了数据持久化的实时性，目前已经是Redis持久化的主流方式。AOF持久化工作机制​		开启AOF功能需要配置：appendonly yes，默认不开启。​		AOF文件名 通过appendfilename配置设置，默认文件名是appendonly.aof。保存路径同 RDB持久化方式一致，通过dir配置指定。​		AOF的工作流程操作：命令写入 （append）、文件同步（sync）、文件重写（rewrite）、重启加载 （load）。（1） 所有的写入命令会追加到aof_buf（缓冲区）中。（2）AOF缓冲区根据对应的策略向硬盘做同步操作。​		AOF为什么把命令追加到aof_buf中？Redis使用单线程响应命令，如果每次写AOF文件命令都直接追加到硬盘，那么性能完全取决于当前硬盘负载。先写入缓冲区aof_buf中，还有另一个好处，Redis可以提供多种缓冲区同步硬盘的策略，在性能和安全性方面做出平衡。（3）随着AOF文件越来越大，需要定期对AOF文件进行重写，达到压缩的目的。（4）当Redis服务器重启时，可以加载AOF文件进行数据恢复。AOF重写（rewrite）机制重写的目的：
减小AOF文件占用空间；
更小的AOF 文件可以更快地被Redis加载恢复。
AOF重写可以分为手动触发和自动触发：

手动触发：直接调用bgrewriteaof命令。


自动触发：根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机。
​	auto-aof-rewrite-min-size：表示运行AOF重写时文件最小体积，默认 为64MB。
​	auto-aof-rewrite-percentage：代表当前AOF文件空间 （aof_current_size）和上一次重写后AOF文件空间（aof_base_size）的比值。

自动触发时机​		当aof_current_size&amp;gt;auto-aof-rewrite-minsize 并且（aof_current_size-aof_base_size）/aof_base_size&amp;gt;=auto-aof-rewritepercentage。​		其中aof_current_size和aof_base_size可以在info Persistence统计信息中查看。AOF文件重写后为什么会变小？（1）旧的AOF文件含有无效的命令，如：del key1， hdel key2等。重写只保留最终数据的写入命令。（2）多条命令可以合并，如lpush list a，lpush list b，lpush list c可以直接转化为lpush list a b c。AOF文件数据恢复数据恢复流程说明：（1）AOF持久化开启且存在AOF文件时，优先加载AOF文件。（2）AOF关闭或者AOF文件不存在时，加载RDB文件。（3）加载AOF/RDB文件成功后，Redis启动成功。（4）AOF/RDB文件存在错误时，Redis启动失败并打印错误信息。1.5.3 RDB和AOF的优缺点#RDB优点：
RDB 是一个非常紧凑的文件,它保存了某个时间点的数据集,非常适用于数据集的备份,比如你可以在每个小时保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集。
RDB 是一个紧凑的单一文件,很方便传送到另一个远端数据中心，非常适用于灾难恢复。
RDB 在保存 RDB 文件时父进程唯一需要做的就是 fork 出一个子进程,接下来的工作全部由子进程来做，父进程不需要再做其他 IO 操作，所以 RDB 持久化方式可以最大化 Redis 的性能。
与AOF相比,在恢复大的数据集的时候，RDB 方式会更快一些。
RDB缺点：
Redis 要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在 Redis 意外宕机,你可能会丢失几分钟的数据。
RDB 需要经常 fork 子进程来保存数据集到硬盘上,当数据集比较大的时候, fork 的过程是非常耗时的,可能会导致 Redis 在一些毫秒级内不能响应客户端的请求。
AOF优点：
你可以使用不同的 fsync 策略：无 fsync、每秒 fsync 、每次写的时候 fsync .使用默认的每秒 fsync 策略, Redis 的性能依然很好( fsync 是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障，你最多丢失1秒的数据
AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满，写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题。
Redis 可以在 AOF 文件体积变得过大时，自动地在后台对 AOF 进行重写：重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。整个重写操作是绝对安全的，因为 Redis 在创建新 AOF 文件的过程中，会继续将命令追加到现有的 AOF 文件里面，即使重写过程中发生停机，现有的 AOF 文件也不会丢失。而一旦新 AOF 文件创建完毕，Redis 就会从旧 AOF 文件切换到新 AOF 文件，并开始对新 AOF 文件进行追加操作。
AOF 文件有序地保存了对数据库执行的所有写入操作， 这些写入操作以 Redis 协议的格式保存， 因此 AOF 文件的内容非常容易被人读懂， 对文件进行分析（parse）也很轻松。导出（export） AOF 文件也非常简单：举个例子， 如果你不小心执行了 FLUSHALL 命令， 但只要 AOF 文件未被重写， 那么只要停止服务器， 移除 AOF 文件末尾的 FLUSHALL 命令， 并重启 Redis ， 就可以将数据集恢复到 FLUSHALL 执行之前的状态。
AOF缺点：
对于相同的数据集来说，AOF 文件的体积通常要大于 RDB 文件的体积。
数据恢复（load）时AOF比RDB慢，通常RDB 可以提供更有保证的最大延迟时间。
RDB和AOF简单对比总结
RDB 是紧凑的二进制文件，比较适合备份，全量复制等场景；RDB 恢复数据远快于 AOF；但RDB 无法实现实时或者秒级持久化；新老版本无法兼容 RDB 格式。
AOF可以更好地保护数据不丢失；append-only 模式写入性能比较高；适合做灾难性的误删除紧急恢复。但对于同一份文件，AOF 文件要比 RDB 快照大；AOF 开启后，会对写的 QPS 有所影响，相对于 RDB 来说 写 QPS 要下降；数据库恢复比较慢， 不合适做冷备。
1.6 讲解一下Redis的线程模型？#​		Redis基于Reactor模式开发了网络事件处理器，这个处理器被称为文件事件处理器。它的组成结构为4部分：多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的，所以Redis才叫单线程模型。文件事件处理器消息处理流程：

文件事件处理器使用I/O多路复用(multiplexing)程序来同时监听多个套接字，并根据套接字目前执行的任务来为套接字关联不同的事件处理器。


当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时，与操作相对应的文件事件就会产生，这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。

​	尽管多个文件事件可能会并发地出现，但I/O多路复用程序总是会将所有产生事件的套接字都推到一个队列里面，然后通过这个队列，以有序（sequentially）、同步（synchronously）、每次一个套接字的方式向文件事件分派器传送套接字：当上一个套接字产生的事件被处理完毕之后（该套接字为事件所关联的事件处理器执行完毕）， I/O多路复用程序才会继续向文件事件分派器传送下一个套接字。
I/O 多路复用程序的实现：​		Redis的I/O多路复用程序的所有功能是通过包装select、epoll、evport和kqueue这些I/O多路复用函数库来实现的，每个I/O多路复用函数库在Redis源码中都对应一个单独的文件，比如ae_select.c、ae_epoll.c、ae_kqueue.c等。​		因为Redis为每个I/O多路复用函数库都实现了相同的API，所以I/O多路复用程序的底层实现是可以互换的文件事件的类型：​	I/O 多路复用程序可以监听多个套接字的ae.h/AE_READABLE事件和ae.h/AE_WRITABLE事件，这两类事件和套接字操作之间的对应关系如下：

当套接字变得可读时（客户端对套接字执行write操作，或者执行close操作），或者有新的可应答（acceptable）套接字出现时（客户端对服务器的监听套接字执行connect操作），套接字产生AE_READABLE 事件。


当套接字变得可写时（客户端对套接字执行read操作），套接字产生AE_WRITABLE事件。I/O多路复用程序允许服务器同时监听套接字的AE_READABLE事件和AE_WRITABLE事件，如果一个套接字同时产生了这两种事件，那么文件事件分派器会优先处理AE_READABLE事件，等到AE_READABLE事件处理完之后，才处理AE_WRITABLE 事件。这也就是说，如果一个套接字又可读又可写的话，那么服务器将先读套接字，后写套接字。

文件事件的处理器：​	Redis为文件事件编写了多个处理器，这些事件处理器分别用于实现不同的网络通讯需求，常用的处理器如下：

为了对连接服务器的各个客户端进行应答， 服务器要为监听套接字关联连接应答处理器。


为了接收客户端传来的命令请求， 服务器要为客户端套接字关联命令请求处理器。


为了向客户端返回命令的执行结果， 服务器要为客户端套接字关联命令回复处理器。

如果面试官继续追问为啥 redis 单线程模型也能效率这么高？
纯内存操作
核心是基于非阻塞的 IO 多路复用机制
单线程反而避免了多线程的频繁上下文切换问题
1.7  缓存雪崩、缓存穿透、缓存预热、缓存击穿、缓存降级的区别是什么？#​		在实际生产环境中有时会遇到缓存穿透、缓存击穿、缓存雪崩等异常场景，为了避免异常带来巨大损失，我们需要了解每种异常发生的原因以及解决方案，帮助提升系统可靠性和高可用。1.7.1 缓存穿透（大量访问数据库不存在数据）#什么是缓存穿透？​		缓存穿透是指用户请求的数据在缓存中不存在即没有命中，同时在数据库中也不存在，导致用户每次请求该数据都要去数据库中查询一遍，然后返回空。​		如果有恶意攻击者不断请求系统中不存在的数据，会导致短时间大量请求落在数据库上，造成数据库压力过大，甚至击垮数据库系统。缓存穿透常用的解决方案（1）布隆过滤器（推荐）​	布隆过滤器（Bloom Filter，简称BF）由Burton Howard Bloom在1970年提出，是一种空间效率高的概率型数据结构。​	布隆过滤器专门用来检测集合中是否存在特定的元素。​	如果在平时我们要判断一个元素是否在一个集合中，通常会采用查找比较的方法，下面分析不同的数据结构查找效率：
采用线性表存储，查找时间复杂度为O(N)
采用平衡二叉排序树（AVL、红黑树）存储，查找时间复杂度为O(logN)
采用哈希表存储，考虑到哈希碰撞，整体时间复杂度也要O[log(n/m)]
​		当需要判断一个元素是否存在于海量数据集合中，不仅查找时间慢，还会占用大量存储空间。接下来看一下布隆过滤器如何解决这个问题。布隆过滤器设计思想​		布隆过滤器由一个长度为m比特的位数组（bit array）与k个哈希函数（hash function）组成的数据结构。位数组初始化均为0，所有的哈希函数都可以分别把输入数据尽量均匀地散列。​		当要向布隆过滤器中插入一个元素时，该元素经过k个哈希函数计算产生k个哈希值，以哈希值作为位数组中的下标，将所有k个对应的比特值由0置为1。​		当要查询一个元素时，同样将其经过哈希函数计算产生哈希值，然后检查对应的k个比特值：如果有任意一个比特为0，表明该元素一定不在集合中；如果所有比特均为1，表明该集合有可能性在集合中。为什么不是一定在集合中呢？因为不同的元素计算的哈希值有可能一样，会出现哈希碰撞，导致一个不存在的元素有可能对应的比特位为1，这就是所谓“假阳性”（false positive）。相对地，“假阴性”（false negative）在BF中是绝不会出现的。​		总结一下：布隆过滤器认为不在的，一定不会在集合中；布隆过滤器认为在的，可能在也可能不在集合中。布隆过滤器优缺点优点：
节省空间：不需要存储数据本身，只需要存储数据对应hash比特位
时间复杂度低：插入和查找的时间复杂度都为O(k)，k为哈希函数的个数
缺点：
存在假阳性：布隆过滤器判断存在，可能出现元素不在集合中；判断准确率取决于哈希函数的个数
不能删除元素：如果一个元素被删除，但是却不能从布隆过滤器中删除，这也是造成假阳性的原因了
布隆过滤器适用场景
爬虫系统url去重
垃圾邮件过滤
黑名单
（2）返回空对象​	当缓存未命中，查询持久层也为空，可以将返回的空对象写到缓存中，这样下次请求该key时直接从缓存中查询返回空对象，请求不会落到持久层数据库。为了避免存储过多空对象，通常会给空对象设置一个过期时间。​	这种方法会存在两个问题：
如果有大量的key穿透，缓存空对象会占用宝贵的内存空间。
空对象的key设置了过期时间，在这段时间可能会存在缓存和持久层数据不一致的场景。
1.7.2 缓存击穿（大量并发访问同一个数据点）#什么是缓存击穿？​	缓存击穿，是指一个key非常热点，在不停的扛着大并发，大并发集中对这一个点进行访问，当这个key在失效的瞬间，持续的大并发就穿破缓存，直接请求数据库，就像在一个屏障上凿开了一个洞。缓存击穿危害​	数据库瞬时压力骤增，造成大量请求阻塞。缓存击穿的解决方案：方案一：使用互斥锁（mutex key）​	这种思路比较简单，就是让一个线程回写缓存，其他线程等待回写缓存线程执行完，重新读缓存即可。​	同一时间只有一个线程读数据库然后回写缓存，其他线程都处于阻塞状态。如果是高并发场景，大量线程阻塞势必会降低吞吐量。这种情况如何解决？​	如果是分布式应用就需要使用分布式锁。方案二：热点数据永不过期永不过期实际包含两层意思：
物理不过期，针对热点key不设置过期时间
逻辑过期，把过期时间存在key对应的value里，如果发现要过期了，通过一个后台的异步线程进行缓存的构建
​	从实战看这种方法对于性能非常友好，唯一不足的就是构建缓存时候，其余线程(非构建缓存的线程)可能访问的是老数据，对于不追求严格强一致性的系统是可以接受的。1.7.3 缓存雪崩（大批量数据同时到期）#什么是缓存雪崩？​	缓存雪崩是指缓存中数据大批量到过期时间，而查询数据量巨大，请求直接落到数据库上，引起数据库压力过大甚至宕机。和缓存击穿不同的是，缓存击穿指并发查同一条数据，缓存雪崩是不同数据都过期了，很多数据都查不到从而查数据库。产生的问题：​	如果大量的key过期时间设置的过于集中，到过期的那个时间点，Redis可能会出现短暂的卡顿现象(因为redis是单线程的)。严重的话可能会导致服务器雪崩，所以我们一般在过期时间上加一个随机值，让过期时间尽量分散。缓存雪崩解决方案​	常用的解决方案有：
均匀过期
加互斥锁
缓存永不过期
双层缓存策略
（1）均匀过期​		设置不同的过期时间，让缓存失效的时间点尽量均匀。通常可以为有效期增加随机值或者统一规划有效期。（2）加互斥锁​		跟缓存击穿解决思路一致，同一时间只让一个线程构建缓存，其他线程阻塞排队。（3）缓存永不过期​		跟缓存击穿解决思路一致，缓存在物理上永远不过期，用一个异步的线程更新缓存。（4）双层缓存策略​		使用主备两层缓存：​			主缓存：有效期按照经验值设置，设置为主读取的缓存，主缓存失效后从数据库加载最新值。​			备份缓存：有效期长，获取锁失败时读取的缓存，主缓存更新时需要同步更新备份缓存。1.7.4 缓存预热（系统刚上线就将相关数据加载到缓存中）#什么是缓存预热？​	缓存预热就是系统上线后，将相关的缓存数据直接加载到缓存系统，这样就可以避免在用户请求的时候，先查询数据库，然后再将数据回写到缓存。​	如果不进行预热， 那么 Redis 初始状态数据为空，系统上线初期，对于高并发的流量，都会访问到数据库中， 对数据库造成流量的压力。缓存预热的操作方法
数据量不大的时候，工程启动的时候进行加载缓存动作；
数据量大的时候，设置一个定时任务脚本，进行缓存的刷新；
数据量太大的时候，优先保证热点数据进行提前加载到缓存。
1.7.5 缓存降级（缓存失效或缓存服务器挂掉，不去访问服务器直接返回默认数据）#​	缓存降级是指缓存失效或缓存服务器挂掉的情况下，不去访问数据库，直接返回默认数据或访问服务的内存数据。​	在项目实战中通常会将部分热点数据缓存到服务的内存中，这样一旦缓存出现异常，可以直接使用服务的内存数据，从而避免数据库遭受巨大压力。​	降级一般是有损的操作，所以尽量减少降级对于业务的影响程度。1.8 Redis的内存淘汰机制#​	Redis内存淘汰策略是指当缓存内存不足时，通过淘汰旧数据处理新加入数据选择的策略。如何配置最大内存？（1）通过配置文件配置​		修改redis.conf配置文件1maxmemory 1024mb //设置Redis最大占用内存大小为1024M​		注意：maxmemory默认配置为0，在64位操作系统下redis最大内存为操作系统剩余内存，在32位操作系统下redis最大内存为3GB。（2）通过动态命令配置​		Redis支持运行时通过命令动态修改内存大小：1127.0.0.1:6379&amp;gt; config set maxmemory 200mb //设置Redis最大占用内存大小为200M2127.0.0.1:6379&amp;gt; config get maxmemory //获取设置的Redis能使用的最大内存大小31) &quot;maxmemory&quot;42) &quot;209715200&quot;淘汰策略的分类​	Redis最大占用内存用完之后，如果继续添加数据，如何处理这种情况呢？实际上Redis官方已经定义了八种策略来处理这种情况：
noeviction：默认策略，对于写请求直接返回错误，不进行淘汰。
allkeys-lru：lru(less recently used), 最近最少使用。从所有的key中使用近似LRU算法进行淘汰。
volatile-lru：从设置了过期时间的key中使用近似LRU算法进行淘汰。
allkeys-random：从所有的key中随机淘汰。
volatile-random：从设置了过期时间的key中随机淘汰。
volatile-ttl：ttl(time to live)，在设置了过期时间的key中根据key的过期时间进行淘汰，越早过期的越优先被淘汰。
allkeys-lfu：lfu(Least Frequently Used)，最少使用频率。从所有的key中使用近似LFU算法进行淘汰。从Redis4.0开始支持。
volatile-lfu：从设置了过期时间的key中使用近似LFU算法进行淘汰。从Redis4.0开始支持。
LRU算法​		LRU(Least Recently Used)，即最近最少使用，是一种缓存置换算法。在使用内存作为缓存的时候，缓存的大小一般是固定的。当缓存被占满，这个时候继续往缓存里面添加数据，就需要淘汰一部分老的数据，释放内存空间用来存储新的数据。这个时候就可以使用LRU算法了。其核心思想是：如果一个数据在最近一段时间没有被用到，那么将来被使用到的可能性也很小，所以就可以被淘汰掉。​		Redis使用的是近似LRU算法，它跟常规的LRU算法还不太一样。近似LRU算法通过随机采样法淘汰数据，每次随机出5个（默认）key，从里面淘汰掉最近最少使用的key。​		Redis为了实现近似LRU算法，给每个key增加了一个额外增加了一个24bit的字段，用来存储该key最后一次被访问的时间。LFU算法​		LFU(Least Frequently Used)，是Redis4.0新加的一种淘汰策略，它的核心思想是根据key的最近被访问的频率进行淘汰，很少被访问的优先被淘汰，被访问的多的则被留下来。​		LFU算法能更好的表示一个key被访问的热度。假如你使用的是LRU算法，一个key很久没有被访问到，只刚刚是偶尔被访问了一次，那么它就被认为是热点数据，不会被淘汰，而有些key将来是很有可能被访问到的则被淘汰了。如果使用LFU算法则不会出现这种情况，因为使用一次并不会使一个key成为热点数据。MySQL里有2000w数据，redis中只存20w的数据，如何保证redis中的数据都是热点数据？​	redis内存数据集大小上升到一定大小的时候，就会施行数据淘汰策略。1.9 Redis有事务机制吗？#​	有事务机制。Redis事务生命周期：
开启事务：使用MULTI开启一个事务
命令入队列：每次操作的命令都会加入到一个队列中，但命令此时不会真正被执行
提交事务：使用EXEC命令提交事务，开始顺序执行队列中的命令
1.10 Redis事务到底是不是原子性的？#​		Redis事务不支持事务回滚机制：如果事务执行中出现了命令执行错误(例如对 String 类型的数据库键执行 LPUSH 操作)，只会返回当前命令执行的错误给客户端，并不会影响下面的命令的执行。​		在事务开始后，用户可以输入事务要执行的命令;在命令入事务队列前，会对命令进行检查，如果命令不存在或者是命令参数不对，则会返回错误可客户端，并且修改客户端状态。当后面客户端执行 EXEC 命令时，服务器就会直接拒绝执行此事务了。所以说，官方认为Redis 事务其实是支持原子性的!即使 Redis 不支持事务回滚机制，但是它会检查每一个事务中的命令是否错误。​		但是我们要注意一个点就是：Redis 事务不支持检查那些程序员自己逻辑错误。例如对 String 类型的数据库键执行对 HashMap 类型的操作! 所以严格来说，Redis的事务不是原子性的，因为程序员自己的逻辑错误导致的错误 ，后面的代码正常执行，没有回滚，因此我们认为事务不是原子性的。​	总结：官方认为Redis是一个原子操作，是因为事务有一个事务队列，在命令入事务队列前，会对命令进行检查，如果命令不存在或者命令参数不对，则返回错误给客户端，并修改客户端状态，在后面执行exec的时候，服务端就会直接拒绝执行此任务了 。但是这个检查不检查程序员自己写的逻辑错误，例如对 String 类型的数据库键执行 LPUSH 操作，只会返回当前命令执行的错误给客户端，并不会影响下面的命令的执行，所以在事务发生错误的时候，没有回滚到事务发生之前的状态，我们认为Redis是不支持原子性的。Redis官方认为这种语法错误很大可能出现在程序员开发阶段，不会出现在生产阶段，如果支持事务回滚能力会导致设计复杂，这与Redis的初衷相违背，Redis的设计目标是功能简化及确保更快的运行速度。1.11 Redis为什么不支持回滚？#事务的内部错误：在一个事务的运行期间，可能会遇到两种类型的命令错误：
一个命令可能会在被放入队列时失败。因此，事务有可能在调用EXEC命令之前就发生错误。例如，这个命令可能会有语法错误（参数的数量错误、命令名称错误，等等），或者可能会有某些临界条件（例如：如果使用maxmemory指令，为Redis服务器配置内存限制，那么就可能会有内存溢出条件）。
在调用EXEC命令之后，事务中的某个命令可能会执行失败。例如，我们对某个键执行了错误类型的操作（例如，对一个字符串（String）类型的键执行列表（List）类型的操作）。
​		在事务运行期间虽然Redis命令可能会执行失败，但是Redis依然会执行事务内剩余的命令而不会执行回滚操作。如果你熟悉mysql关系型数据库事务，你会对此非常疑惑，Redis官方的理由如下：1只有当被调用的Redis命令有语法错误时，这条命令才会执行失败（在将这个命令放入事务队列期间，Redis能够发现此类问题），或者对某个键执行不符合其数据类型的操作：实际上，这就意味着只有程序错误才会导致Redis命令执行失败，这种错误很有可能在程序开发期间发现，一般很少在生产环境发现。支持事务回滚能力会导致设计复杂，这与Redis的初衷相违背，Redis的设计目标是功能简化及确保更快的运行速度。​		对于官方的这种理由有一个普遍的反对观点：程序有bug怎么办？但其实回归不能解决程序的bug，比如某位粗心的程序员计划更新键A，实际上最后更新了键B，回滚机制是没法解决这种人为错误的。正因为这种人为的错误不太可能进入生产系统，所以官方在设计Redis时选用更加简单和快速的方法，没有实现回滚的机制。1.12 Redis事务相关的命令有哪几个？#（1）WATCH​		可以为Redis事务提供 check-and-set （CAS）行为。被WATCH的键会被监视，并会发觉这些键是否被改动过了。如果有至少一个被监视的键在 EXEC 执行之前被修改了， 那么整个事务都会被取消， EXEC 返回nil-reply来表示事务已经失败。（2）MULTI​		用于开启一个事务，它总是返回OK。MULTI执行之后,客户端可以继续向服务器发送任意多条命令， 这些命令不会立即被执行，而是被放到一个队列中，当 EXEC命令被调用时， 所有队列中的命令才会被执行。（3）UNWATCH​		取消 WATCH 命令对所有 key 的监视，一般用于DISCARD和EXEC命令之前。如果在执行 WATCH 命令之后， EXEC 命令或 DISCARD 命令先被执行了的话，那么就不需要再执行 UNWATCH 了。因为 EXEC 命令会执行事务，因此 WATCH 命令的效果已经产生了；而 DISCARD 命令在取消事务的同时也会取消所有对 key 的监视，因此这两个命令执行之后，就没有必要执行 UNWATCH 了。（4）DISCARD​		当执行 DISCARD 命令时， 事务会被放弃， 事务队列会被清空，并且客户端会从事务状态中退出。（5）EXEC​		负责触发并执行事务中的所有命令：​		如果客户端成功开启事务后执行EXEC，那么事务中的所有命令都会被执行。​		如果客户端在使用MULTI开启了事务后，却因为断线而没有成功执行EXEC,那么事务中的所有命令都不会被执行。需要特别注意的是：即使事务中有某条/某些命令执行失败了，事务队列中的其他命令仍然会继续执行，Redis不会停止执行事务中的命令，而不会像我们通常使用的关系型数据库一样进行回滚。​		Redis事务使用：使用MULTI命令便可以进入一个Redis事务。这个命令的返回值总是OK。此时，用户可以发出多个Redis命令。Redis会将这些命令放入队列，而不是执行这些命令。一旦调用EXEC命令，那么Redis就会执行事务中的所有命令。相反，调用DISCARD命令将会清除事务队列，然后退出事务。![redis事务实例](./Acti1/redis事务实例 (2).png)1.13 什么是Redis主从复制？#​		主从复制，是指将一台Redis服务器的数据，复制到其他的Redis服务器。前者称为主节点(master)，后者称为从节点(slave)；数据的复制是单向的，只能由主节点到从节点。主从复制的作用
数据冗余：主从复制实现了数据的热备份，是持久化之外的一种数据冗余方式。
故障恢复：当主节点出现问题时，可以由从节点提供服务，实现快速的故障恢复；实际上是一种服务的冗余。
负载均衡：在主从复制的基础上，配合读写分离，可以由主节点提供写服务，由从节点提供读服务，分担服务器负载；尤其是在写少读多的场景下，通过多个从节点分担读负载，可以大大提高Redis服务器的并发量。
高可用基石：主从复制还是哨兵和集群能够实施的基础，因此说主从复制是Redis高可用的基础。
主从复制实现原理​	主从复制过程主要可以分为3个阶段：连接建立阶段、数据同步阶段、命令传播阶段。

连接建立阶段
​	该阶段的主要作用是在主从节点之间建立连接，为数据同步做好准备。
步骤1：保存主节点信息
​	slaveof命令是异步的，在从节点上执行slaveof命令，从节点立即向客户端返回ok，从节点服务器内部维护了两个字段，即masterhost和masterport字段，用于存储主节点的ip和port信息。
步骤2：建立socket连接
​	从节点每秒1次调用复制定时函数replicationCron()，如果发现了有主节点可以连接，便会根据主节点的ip和port，创建socket连接。
​	从节点为该socket建立一个专门处理复制工作的文件事件处理器，负责后续的复制工作，如接收RDB文件、接收命令传播等。
​	主节点接收到从节点的socket连接后（即accept之后），为该socket创建相应的客户端状态，并将从节点看做是连接到主节点的一个客户端，后面的步骤会以从节点向主节点发送命令请求的形式来进行。
步骤3：发送ping命令
​	从节点成为主节点的客户端之后，发送ping命令进行首次请求，目的是：检查socket连接是否可用，以及主节点当前是否能够处理请求。
​	从节点发送ping命令后，可能出现3种情况：
（1）返回pong：说明socket连接正常，且主节点当前可以处理请求，复制过程继续。
（2）超时：一定时间后从节点仍未收到主节点的回复，说明socket连接不可用，则从节点断开socket连接，并重连。
（3）返回pong以外的结果：如果主节点返回其他结果，如正在处理超时运行的脚本，说明主节点当前无法处理命令，则从节点断开socket连接，并重连。
步骤4：身份验证
​	如果从节点中设置了masterauth选项，则从节点需要向主节点进行身份验证；没有设置该选项，则不需要验证。从节点进行身份验证是通过向主节点发送auth命令进行的，auth命令的参数即为配置文件中的masterauth的值。
​	如果主节点设置密码的状态，与从节点masterauth的状态一致（一致是指都存在，且密码相同，或者都不存在），则身份验证通过，复制过程继续；如果不一致，则从节点断开socket连接，并重连。
步骤5：发送从节点端口信息
​	身份验证之后，从节点会向主节点发送其监听的端口号（前述例子中为6380），主节点将该信息保存到该从节点对应的客户端的slave_listening_port字段中；该端口信息除了在主节点中执行info Replication时显示以外，没有其他作用。


数据同步阶段
​	主从节点之间的连接建立以后，便可以开始进行数据同步，该阶段可以理解为从节点数据的初始化。具体执行的方式是：从节点向主节点发送psync命令（Redis2.8以前是sync命令），开始同步。
​	数据同步阶段是主从复制最核心的阶段，根据主从节点当前状态的不同，可以分为全量复制和部分复制，后面再讲解这两种复制方式以及psync命令的执行过程，这里不再详述。


命令传播阶段
​	数据同步阶段完成后，主从节点进入命令传播阶段；在这个阶段主节点将自己执行的写命令发送给从节点，从节点接收命令并执行，从而保证主从节点数据的一致性。
​	需要注意的是，命令传播是异步的过程，即主节点发送写命令后并不会等待从节点的回复；因此实际上主从节点之间很难保持实时的一致性，延迟在所难免。数据不一致的程度，与主从节点之间的网络状况、主节点写命令的执行频率、以及主节点中的repl-disable-tcp-nodelay配置等有关。

1.14 如何实现Redis的高可用？#​	实现Redis的高可用，主要有哨兵和集群两种方式。1.15 Sentinel（哨兵模式）的原理你能讲一下吗？(高可用的实现方案)#​		Redis 的主从复制模式下，一旦主节点由于故障不能提供服务，需要手动将从节点晋升为主节点，同时还要通知客户端更新主节点地址，这种故障处理方式从一定程度上是无法接受的。​		Redis 2.8 以后提供了 Redis Sentinel 哨兵机制来解决这个问题。​		Redis Sentinel 是 Redis 高可用的实现方案。Sentinel 是一个管理多个 Redis 实例的工具，它可以实现对 Redis 的监控、通知、自动故障转移。​		Redis Sentinel架构图如下：哨兵机制哨兵的作用：
集群监控，即时刻监控着redis的主节点和从节点进程是否是在正常工作。
消息通知，就是说当它发现有redis实例有故障的话，就会发送消息给管理员
故障自动转移，如果redis 主节点宕机了的话，它就会将请求转到从节点上，从节点升为主节点。
充当配置中心，如果发生了故障转移，它会通知将主节点的新地址写在配置中心告诉客户端。
哨兵模式的原理：​	哨兵模式的主要作用在于它能够自动完成故障发现和故障转移，并通知客户端，从而实现高可用。哨兵模式通常由一组 Sentinel 节点和一组（或多组）主从复制节点组成。心跳机制：（1）Sentinel与Redis Node​		Redis Sentinel 是一个特殊的 Redis 节点。在哨兵模式创建时，需要通过配置指定 Sentinel 与 Redis Master Node 之间的关系，然后 Sentinel 会从主节点上获取所有从节点的信息，之后 Sentinel 会定时向主节点和从节点发送 info 命令获取其拓扑结构和状态信息。（2）Sentinel与Sentinel​		基于 Redis 的订阅发布功能， 每个 Sentinel 节点会向主节点的 sentinel：hello 频道上发送该 Sentinel 节点对于主节点的判断以及当前 Sentinel 节点的信息 ，同时每个 Sentinel 节点也会订阅该频道， 来获取其他 Sentinel 节点的信息以及它们对主节点的判断。​		通过以上两步所有的 Sentinel 节点以及它们与所有的 Redis 节点之间都已经彼此感知到，之后每个 Sentinel 节点会向主节点、从节点、以及其余 Sentinel 节点定时发送 ping 命令作为心跳检测， 来确认这些节点是否可达。故障转移​		每个 Sentinel 都会定时进行心跳检查，当发现主节点出现心跳检测超时的情况时，此时认为该主节点已经不可用，这种判定称为主观下线。​		之后该 Sentinel 节点会通过 sentinel ismaster-down-by-addr 命令向其他 Sentinel 节点询问对主节点的判断， 当 quorum（法定人数） 个 Sentinel 节点都认为该节点故障时，则执行客观下线，即认为该节点已经不可用。这也同时解释了为什么必须需要一组 Sentinel 节点，因为单个 Sentinel 节点很容易对故障状态做出误判1这里 quorum 的值是我们在哨兵模式搭建时指定的，后文会有说明，通常为 Sentinel节点总数/2+1，即半数以上节点做出主观下线判断就可以执行客观下线。​	因为故障转移的工作只需要一个 Sentinel 节点来完成，所以 Sentinel 节点之间会再做一次选举工作， 基于 Raft 算法选出一个 Sentinel 领导者来进行故障转移的工作。​	被选举出的 Sentinel 领导者进行故障转移的具体步骤如下：（1）在从节点列表中选出一个节点作为新的主节点
过滤不健康或者不满足要求的节点；
选择 slave-priority（优先级）最高的从节点， 如果存在则返回， 不存在则继续；
选择复制偏移量最大的从节点 ， 如果存在则返回， 不存在则继续；
选择 runid 最小的从节点。
（2）Sentinel 领导者节点会对选出来的从节点执行 slaveof no one 命令让其成为主节点。（3）Sentinel 领导者节点会向剩余的从节点发送命令，让他们从新的主节点上复制数据。（4）Sentinel 领导者会将原来的主节点更新为从节点， 并对其进行监控， 当其恢复后命令它去复制新的主节点。为什么至少三个哨兵?​		如果咱们的哨兵集群只部署了两个节点，那么 quorum=1,如果master宕机，sentinel 1 和 sentinel 2 只要有一个认为master宕机就会进行切换，同时sentinel 1 和sentinel 2 就会选出一个sentinel来进行故障转移，这个时候就需要用到majority，即大多数哨兵都是运行的，2个哨兵的majority是2(3个的majority=2，4个的majority=2，5个的majority=3)，也就是说现在这两个哨兵节点都是正常运行的就可以进行故障转移。​		但是，现在master和sentinel1运行的整个机器如果宕机了的话，那么哨兵就只有一个了，此时就无法来通过majority来进行故障转移了，所以，我们至少需要三台哨兵实例。​		3 节点sentinel架构，假设master所在的机器不可用的话，那么哨兵还剩2个，sentinel 2 和sentinel3 就会认为master宕机，然后选举一个来处理故障转移；三个哨兵节点的majority为2，现在还有2个哨兵在工作着，就可以允许执行故障转移。1.16 Cluster（集群）的原理你能讲一下吗？#​	引入Cluster模式的原因：​		不管是主从模式还是哨兵模式都只能由一个master在写数据，在海量数据高并发场景，一个节点写数据容易出现瓶颈，引入Cluster模式可以实现多个节点同时写数据。​		Redis-Cluster采用无中心结构，每个节点都保存数据，节点之间互相连接从而知道整个集群状态。​	如图所示Cluster模式其实就是多个主从复制的结构组合起来的，每一个主从复制结构可以看成一个节点，那么上面的Cluster集群中就有三个节点。1、所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
2、节点的fail是通过集群中超过半数的节点检测失效时才生效。
3、客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
4、redis-cluster把所有的物理节点映射到[0-16383]slot上（不一定是平均分配）,cluster 负责维护node&amp;lt;-&amp;gt;slot&amp;lt;-&amp;gt;value。
5、Redis集群预分好16384个桶，当需要在 Redis 集群中放置一个 key-value 时，根据 CRC16(key) mod 16384的值，决定将一个key放到哪个桶中。Redis哈希槽​		Redis集群没有使用一致性hash,而是引入了哈希槽的概念，当需要在 Redis 集群中放置一个 key-value 时，根据 CRC16(key) mod 16384的值，决定将一个key放到哪个桶中Redis集群最大节点个数是多少？​		Redis集群预分好16384个桶(哈希槽)1.17 Memcache与Redis的区别都有哪些？#存储方式

Memecache把数据全部存在内存之中，断电后会挂掉，数据不能超过内存大小。


Redis有部份存在硬盘上，这样能保证数据的持久性。可以持久化到硬盘

数据支持类型

Memcache对数据类型支持相对简单。


Redis有丰富的数据类型。

使用底层模型不同

它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。


Redis直接自己构建了VM 机制 ，因为一般的系统调用系统函数的话，会浪费一定的时间去移动和请求。

1.18 使用keys查找的相关问题#假如Redis里面有1亿个key，其中有10w个key是以某个固定的已知的前缀开头的，如果将它们全部找出来？答：使用keys指令可以扫出指定模式的key列表：keys pre*如果这个redis正在给线上的业务提供服务，那使用keys指令会有什么问题？答：redis 的单线程的。keys 指令会导致线 程阻塞一段时间，线上服务会停顿，直到指令执行完毕，服务才能恢复。这个时 候可以使用 scan 指令，scan 指令可以无阻塞的提取出指定模式的 key 列表，但是会有一定的重复概率，在客户端做一次去重就可以了，但是整体所花费的时间 会比直接用 keys 指令长。1scan指令的特点：21、复杂度虽然也是 O(n)，但是它是通过游标分步进行的，不会阻塞线程;32、提供 limit 参数，可以控制每次返回结果的最大条数，limit 只是一个 hint，返回的结果可多可少;43、同 keys 一样，它也提供模式匹配功能;54、服务器不需要为游标保存状态，游标的唯一状态就是 scan 返回给客户端的游标整数;65、返回的结果可能会有重复，需要客户端去重复，这点非常重要;76、遍历的过程中如果有数据修改，改动后的数据能不能遍历到是不确定的;87、单次返回的结果是空的并不意味着遍历结束，而要看返回的游标值是否为零9
10scan指令的用法：11  scan提供3个参数：第一个是 cursor 整数值，第二个是 key 的正则模式，第三个是遍历的 limit hint，例如：scan 0 match key99* count 1000 解释：从0开始遍历，匹配key99*，总数是1000 ，1000不是结果数量，是redis单次遍历字典槽位数量(约等于)1.19 Redis常用的客户端有哪些？#Jedis：是老牌的Redis的Java实现客户端，提供了比较全面的Redis命令的支持。Redisson：实现了分布式和可扩展的Java数据结构。Lettuce：高级Redis客户端，用于线程安全同步，异步和响应使用，支持集群，Sentinel，管道和编码器。优点：

Jedis：比较全面的提供了Redis的操作特性。


Redisson：促使使用者对Redis的关注分离，提供很多分布式相关操作服务，例如，分布式锁，分布式集合，可通过Redis支持延迟队列。


Lettuce：基于Netty框架的事件驱动的通信层，其方法调用是异步的。Lettuce的API是线程安全的，所以可以操  作单个Lettuce连接来完成各种操作。

1.20 Redis的过期策略#Redis支持如下两种过期策略：惰性删除：客户端访问一个key的时候，Redis会先检查它的过期时间，如果发现过期就立刻删除这个key。定期删除：Redis会将设置了过期时间的key放到一个独立的字典中，并对该字典进行每秒10次的过期扫描，过期扫描不会遍历字典中所有的key，而是采用了一种简单的贪心策略。该策略的删除逻辑如下：

从过期字典中随机选择20个key；


删除这20个key中已过期的key；


如果已过期key的比例超过25%，则重复步骤1。

1.21  如何保证缓存与数据库的双写一致性？（Redis做缓存）#​	四种同步策略：​	想要保证缓存与数据库的双写一致，一共有4种方式，即4种同步策略：

先更新缓存，再更新数据库；


先更新数据库，再更新缓存；


先删除缓存，再更新数据库；


先更新数据库，再删除缓存。

1.21.1 Cache-Aside 先写数据库再删缓存#Cache-Aside：Cache-Aside可能是项目中最常见的一种模式。它是一种控制逻辑实现在应用程序中的模式。缓存不和数据库直接进行交互，而是由应用程序来同时和缓存以及数据库打交道。​	读数据时
程序需要判断缓存中是否已经存在数据。
当缓存中已经存在数据(也就是缓存命中，cache hit)，则直接从缓存中返回数据
当缓存中不存在数据(也就是缓存未命中，cache miss)，则先从数据库里读取数据，并且存入缓存，然后返回数据
写数据时，有两种策略：​	第一种策略：先更新数据库，再更新缓存但这种策略有线程安全的问题，可能出现缓存和数据库不一致。试想有两个写的线程，线程A和线程B
A写数据库
B后于A写数据库
B写缓存
A写缓存
缓存和数据库中的数据不一致，缓存中的是脏数据
要解决线程安全的问题，我们可以加锁，不过实现起来比较麻烦，因此我们不考虑这种写策略，而使用第二种策略。第二种策略：先更新数据库，在删除缓存那么这种写策略会有线程安全的问题吗？有，试想一下有两个线程，线程A读，线程B写
A读数据，由于未命中那么从数据库中取数据
B写数据库
B删除缓存
A由于网络延迟比较慢，将脏数据写入缓存
​	但是这种情况可能性非常的小，需要同时满足很多条件，近乎不太可能发生，所以我们一般都采用这种写策略。另外可以对缓存中的数据设置合适的过期时间，即使发生的脏数据的情况，也不会发生很长时间。应用场景​	应用于缓存不支持Read-Through/Write-Through的系统。优点
缓存仅仅保存被请求的数据，属于懒加载模式(Lazy Loading)，和下文的Write-Through模式相比，避免了任何数据都被写入缓存造成缓存频繁的更新。
缺点
当发生缓存未命中的情况时，则会比较慢，因为要经过三个步骤：查询缓存，从数据库读取，写入缓存。
复杂的逻辑都在应用程序中，如果实现微服务，多个微服务中会有重复的逻辑代码
1.21.2 Read-Through/Write-Through 先写缓存再写数据库#​	这种模式中，应用程序将缓存作为主要的数据源，而数据库对于应用程序是透明的，更新数据库和从数据库的读取的任务都交给缓存来代理了，所以对于应用程序来说，简单很多。Read-Through由缓存配置一个读模块，它知道如何将数据库中的数据写入缓存。在数据被请求的时候，如果未命中，则将数据从数据库载入缓存。Write-Through缓存配置一个写模块，它知道如何将数据写入数据库。当应用要写入数据时，缓存会先存储数据，并调用写模块将数据写入数据库。​	应用场景​	Read Through/Write Through适用于写入之后经常被读取的应用。​	优点
缓存不存在脏数据
相比较Cache-Aside懒加载模式，读取速度更高，因为较少因为缓存未命中而从数据库中查找
应用程序的逻辑相对简单
​	缺点

对于总是写入却很少被读取的应用，那么Write-Through会非常浪费性能，因为数据可能更改了很多次，却没有被读取，白白的每次都写入缓存造成写入延迟。
除了Write-Through以外，我们还有另外的两种写模式可以和Read-Through一起来配合使用，分别是Write-Back和Write-Around。

Write-Back​	又叫做Write-Behind。和Write-Through写入的时机不同，Write-Back将缓存作为可靠的数据源，每次都只写入缓存，而写入数据库则采用异步的方式，比如当数据要被移除出缓存的时候再存储到数据库或者一段时间之后批量更新数据库。​	应用场景​	读写效率都非常好，写的时候因为异步存储到数据库，提升了写的效率，适用于读写密集的应用。​	优点
写入和读取数据都非常的快，因为都是从缓存中直接读取和写入。
对于数据库不可用的情况有一定的容忍度，即使数据库暂时不可用，系统也整体可用，当数据库之后恢复的时候，再将数据写入数据库。
​	缺点
有数据丢失的风险，如果缓存挂掉而数据没有及时写到数据库中，那么缓存中的有些数据将永久的丢失了
Write-Around​	和Write-Through不同，更新的时候只写入数据库，不写入缓存，结合Read-Through或者Cache-Aside使用，只在缓存未命中的情况下写缓存。​	应用场景​		适合于只写入一次而很少被读取的应用。​	优点
相比较Write-Through写入的时候的效率较高，如果数据写入后很少被读取，缓存也不会被没用到的数据占满。
​	缺点
如果数据会写入多次，那么可能存在缓存和数据库不一致
1.21.3 延迟双删#上面我们提到，如果是先删缓存、再更新数据库，在没有出现失败时可能会导致数据的不一致。如果在实际的应用中，出于某些考虑我们需要选择这种方式，那有办法解决这个问题吗？答案是有的，那就是采用延时双删的策略，延时双删的基本思路如下：

删除缓存；


更新数据库；


sleep N毫秒；


再次删除缓存。

​	阻塞一段时间之后，再次删除缓存，就可以把这个过程中缓存中不一致的数据删除掉。而具体的时间，要评估你这项业务的大致时间，按照这个时间来设定即可。2、Kafka#2.0 介绍消息队列#2.0.1 消息队列适合什么场景？#​	消息队列：它主要用来暂存生产者生产的消息，供后续其他消费者来消费。它的功能主要有两个：a.暂存(存储)、b.队列(有序：先进先出)。其他大部分场景对数据的消费没有顺序要求，主要用它的暂存能力 。从目前互联网应用中使用消息队列的场景来看，主要有以下三个：
1. 异步处理数据：以快递员送快递为例，在该例子中我们把暂存快递的快递柜比作暂存数据的消息队列。我们来看一下在现实生活中，没有快递柜时，快递员把快递送到目的地后，一般需要把联系收货人来签收快递，如果收货人此时有空，那一切都很顺利。但如果收货人此时不方便(开会、正在吃午饭、外出出差)。那对于快递员而言，就很尴尬，需要一直等待(开会 or 吃午饭)或者将快递拿回去(外出出差)，导致白跑一趟。这对于快递员而言简直太不友好。​		从这儿可以看出，当快递员送货时，是一个同步状态，即需要等待收货人签收后才能去送下一趟单子，对快递员而言效率太低。上述例子虽然有点牵强，大家凑合理解，意思能大概理解到位就 ok。​		接着我们再来看一下，当有了快递柜后，对于快递员而言，每次需要送快递时，只需要将快递投掷到快递柜，然后再通过短信或者电话通知收货人具体的快递信息即可。他就可以继续去派送下一单。而对于收获人而言，也可以根据具体方便的时间来取件。这样一来，二者完全异步了，不用相互等待了​		在这个例子中，如果把快递员比作生产者，收货人比作是消费者，则快递柜就类似于消息队列。我们可以通过采用消息队列来实现异步数据的处理。2. 系统应用解耦：我们以目前最主流的推荐系统中内容的流转来举例。在推荐系统中当创作者发布了一条内容后，该内容会首先经过安全部分的相关审核，通过审核后的内容，通常需要进行内容入库存储、送入模型进行特征的计算和生成。​		假如后期我们想提升推荐的效果，需要单独构建一份冷启动的推荐池，此时也需要用到这部分内容，那问题来了，在没有使用消息队列时，对于上游服务而言，需要通过扩展新的逻辑来实现该功能。同时在该场景里，会存在依赖三个下游服务，如果其中一个下游服务失败后，该如何处理，是重试还是返回失败等这些细节的处理。如果后期这部分数据还想在其他渠道分发，那又该如何对接。明显这种场景下面临着系统紧耦合的问题。​		我们再来看一下，如果我们一开始就引入了消息队列，那问题又会变成怎样的呢？当内容审核通过后，就直接将数据生产出来丢到消息队列中，下游的多个服务再从消息队列消费数据。当后续这一份数据需要扩展供其他系统使用时，也只要通过新的消费者来接入到消息队列消费就 ok。上游生产消息的模块不要做任何的改动。这样我们就通过消息队列进行了系统应用之间的解耦。这是消息队列的第二个用途。3. 业务流量削峰：在现如今的互联网世界中，电商场景中每年的 618 秒杀活动、双 11 抢购便是最典型的案例。这种场景中系统的峰值流量往往集中于一小段时间内，平常的流量比较可控，所以为了防止系统在短时间内的峰值流量冲垮，往往采用消息队列来削弱峰值流量，高峰值期间产生的订单消息等数据首先送入到消息队列中暂存，然后供下游系统根据自己的消费能力来逐步处理。同时这类消息往往对时延的要求不是很高，比较适合采用消息队列暂存。2.0.2 消息队列有哪些主流产品、各自的优缺点？#​	ActiveMQ： （2003）ActiveMQ 由 Apache 软件基金会基于 Java 语言开发的一个开源的消息代理。能够支持多个客户机或服务器。计算机集群等属性支持 ActiveMQ 来管理通信系统。​	RabbitMQ：（2006） RabbitMQ 是实现了高级消息队列协议（AMQP）的开源消息代理软件（亦称面向消息的中间件）。RabbitMQ 服务器是用 Erlang 语言编写的，而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。RabbitMQ 支持多种消息传递协议、传递确认等特性。​	Kafka：（2010）Apache Kafka 是由 Apache 软件基金会开发的一个开源消息系统项目，由 Scala 写成。Kafka 最初是由 LinkedIn 开发，并于 2011 年初开源。2012 年 10 月从 Apache Incubator 毕业。该项目的目标是为处理实时数据提供一个统一、高通量、低等待的平台。Kafka 是一个分布式的、分区的、多复本的日志提交服务。它通过一种独一无二的设计提供了一个消息系统的功能。​	RocketMQ： （2011）Apache RocketMQ 是一个分布式消息和流媒体平台，具有低延迟、强一致、高性能和可靠性、万亿级容量和灵活的可扩展性。它有借鉴 Kafka 的设计思想，但不是 kafka 的拷贝。​	Pulsar： （2012）Apache Pulsar 是 Apache 软件基金会顶级项目，是下一代云原生分布式消息流平台，集消息、存储、轻量化函数式计算为一体，采用计算与存储分离架构设计，支持多租户、持久化存储、多机房跨区域数据复制，具有强一致性、高吞吐、低延时及高可扩展性等流数据存储特性，被看作是云原生时代实时消息流传输、存储和计算最佳解决方案。各种消息队列对比​		上图详细的展示了几种消息队列的各自功能及优缺点，首先，ActiveMQ 和 RabbitMQ 二者属于同一量级，在吞吐量上比后面三者差一个量级；其次，支持强一致性的有 RocketMQ 和 Pulsar，在对消息一致性要求比较高的场景可以采用这些产品。同时 kafka 虽然会有数据丢失的风险，但其吞吐量比较高同时社区非常活跃，在大数据的绝大部分场景里，都可以采用 kafka；最后 kafka、RocketMQ、Pulsar 这几款是基于磁盘存储数据的，内存加速访问。而 ActiveMQ、RabbitMQ 采用内存存储数据，也支持数据持久化到磁盘。2.0.3 消息队列背后的设计思想(整体核心模型、数据存储考量、数据获取方案对比、消费者消费模型)#消息队列核心模型：​		上图是几乎所有消息队列设计的一个核心模型。对于一个消息队列而言，从数据流向的维度，可以拆解为三大部分：生产者、消息队列集群、消费者，数据是从生产者流向消息队列集群，最终再从消息队列集群流向消费者，下面对这几个概念进行一一阐述。​		生产者： 生产数据的服务，通常也称为数据的输入提供方，这里的数据通常指我们的业务数据，例如推荐场景中用户对内容的点击数据、内容曝光数据、电商中的订单数据等等。生产者通常是作为客户端的方式存在，但在支持事务消息的消息队列中，生产者也被设计为服务端，实现事务消息这一特性。其次生产者通常会有多个，消息队列集群内部也会有多个分区队列，所以在生产者发送数据时，通常会存在负载均衡的一些策略，常见的有按 key hash、轮询、随机等方式。其本质是一条数据，被消息队列封装后也被称为一条消息，该条消息只能发送到其消息队列集群内部的一个分区队列中。因此只需按照一定的策略从多个队列中选择一个队列即可。​		消息队列集群： 消息队列集群是消息队列这种组件实现中的核心中的核心，它的主要功能是存储消息、过滤消息、分发消息。其中存储消息主要指生产者生产的数据需要存储到消息队列内部；存储消息可以说是消息队列的核心，一个消息队列吞吐量的高低、性能优劣都和它的存储模型脱不开关系。这部分内容会在 3.2 节进行介绍。​		过滤消息只指消息队列可以通过一定的规则或者策略进行消息的过滤，该项能力通常也被称为消息路由；过滤消息属于高阶的特性功能，AMQP 协议对这些能力抽象的比较完备，部分消息队列可以选择性的实现该协议来达到该功能，关于 AMQP 协议内容读者可以自行搜索资料阅读，此处不再展开。​		分发消息是指消息队列通常需要将消息分发给处理同一逻辑的多个消费者处理或者处理不同逻辑的不同消费者处理。分发消息可以说和消费者模型想挂钩，这块会涉及到不同的数据获取方式，也会涉及到消费者消费消息的模型。这两部分内容会在 3.3 节和 3.4 节进行重点介绍。​		此外绝大部分的消息队列也都支持对消息进行分类，分类的标签称为topic(主题)，一个 topic 中存放的是同一类消息。​		消费者： 最终消息队列存储的消息会被消费者消费使用，消费者也可以看做消息队列中数据的输出方。消费者通常有两种方式从消息队列中获取数据：推送(push)数据、拉取(pull)数据，3.3 节中会对这两种方案进行详细对比说明。其次消费者也经常是作为客户端的角色出现在在消息队列这种组件中。消息队列数据组织方式：​		消息队列存储消息这个环节的一些权衡考量，通常数据的存储无外乎就是两种，一种是存储在非易失性存储中，例如磁盘这种介质；另一种是选择存储在易失性存储中，典型的就是内存。关于二者的对比大家可以参考下表，此处就不再赘述：​		通常在大部分组件设计时，往往会选择一种主要介质来存储、另一种介质作为辅助使用。就拿 redis 来说，它主要采用内存存储数据，磁盘用来做辅助的持久化。拿 RabbitMQ 举例，它也是主要采用内存存储消息，但也支持将消息持久化到磁盘。而 RocketMQ、Kafka、Pulsar 这种，则是数据主要存储在磁盘，通过内存来主力提升系统的性能。关系型数据库例如 mysql 这种组件也是主要采用磁盘组织数据，合理利用内存提升性能。​		针对采用内存存储数据的方案而言，难点一方面在于如何在不降低访问效率的情况下，充分利用有限的内存空间来存储尽可能多的数据，这个过程中少不了对数据结构的选型、优化；另一方面在于如何保证数据尽可能少的丢失，我们可以看到针对此问题的解决方案通常是快照+广泛意义的**预写式日志（Write-ahead logging，缩写 WAL）**来解决。此类典型的代表就是 redis 啦。​		针对采用磁盘存储数据的方案而言，难点一方面在于如何根据系统所要解决的特点场景进行合理的对磁盘布局。读多写少情况下采用 b+树方式存储数据；写多读少情况下采用 lsm tree 这类方案处理。另一方面在于如何尽可能减少对磁盘的频繁访问，一些做法是采用 mmap 进行内存映射，提升读性能；还有一些则是采用缓存机制缓存频繁访问的数据。还有一些则是采用巧妙的数据结构布局，充分利用磁盘预读特性保证系统性能。​		总的来说，针对写磁盘的优化，要不采用顺序写提升性能、要不采用异步写磁盘提升性能(异步写磁盘时需要结合 wal 保证数据的持久化，事实上 wal 也主要采用顺序写的特性)；针对读磁盘的优化，一方面是缓存、另一方面是 mmap 内存映射加速读。​		上述这些存储方案上权衡的选择在 kafka、RocketMQ、Pulsar 中都可以看到。其实抛开消息队列而言，这些存储方案的选择上无论是关系型数据库还是 kv 型组件都是通用的。获取数据的推、拉两种方案对比：​	消费者在从消息队列中获取数据时，主要有两种方案：
​		1. 等待推送数据
​		2. 主动拉取数据​	目前的消息队列实现时，都会选择支持两种的至少一种，关于这两种方案的对比，大家可以参考下表。​		在此处，个人想抛开消息队列谈一点关于这两种方案的理解，其实推拉模型不仅仅只用于消息队列这种组件中，更一般意义上，它解决的其实是数据传送双方的一个问题。本质是数据需要从一方流向另一方。顺着这个思路来看，下面这三个例子都是遵循这个原则。​		网络中传输的数据： 在 IO 多路复用中，以 epoll 为例，当内核检测到监听的描述符有数据到来时，epoll_wait()阻塞会返回，上层用户态程序就知道有数据就绪了，然后可以通过 read()系统调用函数读取数据。这个过程就绪通知，类似于推送，但推送的不是数据，而是数据就绪的信号。具体的数据获取方式还是需要通过拉取的方式来主动读。​		feeds 流系统用户时间线后台实现方案(读扩散、写扩散)： 读扩散和写扩散更是这样一个 case。对于读扩散而言，主要采用拉取的方式获取数据。而对于写扩散而言，它是典型的数据推送的方式。当然在系统实现中，更复杂的场景往往会选择读写结合的思路来实现。​		生活中的点外卖例子： 当下单点外卖时，通常也会有两种方式可以选择，外卖派送、到店自取。不过通常外卖派送比较实时，我们通常就选择这种方式了而已。可以看出外卖派送其实就是一种推的方式，而到店自取，则是拉的方式。消息队列消费模型：​		消费者消费者模型其实是一个 1:N 的关系，一份数据被 N 个消费者组独立使用，每个消费者组中有 M 个消费者进行分摊消费​		其实这种模型也称为发布订阅模型，对于一条消息而言，组间广播、组内单播。一条消息只能被一个消费者组中的一个消费者使用。在消费者组内部也存在一些负载均衡的策略。常用的有：轮询、随机、hash、一致性 hash等方案。kafka的使用：kafka处理事件kafka使用kafka1kafka22.1 请说明什么是Kafka?#Kafka 是一种高吞吐量的分布式发布订阅消息系统，有如下特性：

通过O(1)的磁盘数据结构提供消息的持久化，这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能。


高吞吐量：即使是非常普通的硬件Kafka也可以支持每秒数百万的消息。


支持通过Kafka服务器和消费机集群来分区消息。


支持Hadoop并行数据加载。

2.2 什么是消息传递?#​		一个消息系统负责将数据从一个应用传递到另外一个应用，应用只需关注于数据，无需关注数据在两个或多个应用间是如何传递的。分布式消息传递基于可靠的消息队列，在客户端应用和消息系统之间异步传递消息。有两种主要的消息传递模式：点对点传递模式、发布-订阅模式。大部分的消息系统选用发布-订阅模式。Kafka就是一种发布-订阅模式。点对点消息传递：​		在点对点消息系统中，消息持久化到一个队列中。此时，将有一个或多个消费者消费队列中的数据。但是一条消息只能被消费一次。当一个消费者消费了队列中的某条数据之后，该条数据则从消息队列中删除。该模式即使有多个消费者同时消费数据，也能保证数据处理的顺序。​		即：生产者发送一条消息到queue，只有一个消费者能收到。发布-订阅消息传递：​		在发布-订阅消息系统中，消息被持久化到一个topic中。与点对点消息系统不同的是，消费者可以订阅一个或多个topic，消费者可以消费该topic中所有的数据，同一条数据可以被多个消费者消费，数据被消费后不会立马删除。在发布-订阅消息系统中，消息的生产者称为发布者，消费者称为订阅者。​	即：发布者发送到topic的消息，只有订阅了topic的订阅者才会收到消息。2.3 使用Kafka有什么优点和缺点？#优点：

解耦：在项目启动之初来预测将来项目会碰到什么需求，是极其困难的。消息系统在处理过程中间插入了一个隐含的、基于数据的接口层，两边的处理过程都要实现这一接口。这允许你独立的扩展或修改两边的处理过程，只要确保它们遵守同样的接口约束。
解耦解得是共享数据的生产者和消费者，在生产者和消费者之间增加一层，避免模块之间的直接调用，可以将共享的数放在消息队列中，对于新增业务模块，如果要使用该数据，订阅该消息即可，降低了系统各模块的耦合度，提高了系统的扩展性。


冗余（副本）：有些情况下，处理数据的过程会失败。除非数据被持久化，否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全处理，通过这一方式规避了数据丢失风险。许多消息队列所采用的”插入-获取-删除”范式中，在把一个消息从队列中删除之前，需要你的处理系统明确的指出该消息已经被处理完毕，从而确保你的数据被安全的保存直到你使用完毕。


扩展性：因为消息队列解耦了你的处理过程，所以增大消息入队和处理的频率是很容易的，只要另外增加处理过程即可。不需要改变代码、不需要调节参数。扩展就像调大电力按钮一样简单。


灵活性&amp;amp;峰值处理能力：在访问量剧增的情况下，应用仍然需要继续发挥作用，但是这样的突发流量并不常见；如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力，而不会因为突发的超负荷的请求而完全崩溃。


可恢复性：系统的一部分组件失效时，不会影响到整个系统。消息队列降低了进程间的耦合度，所以即使一个处理消息的进程挂掉，加入队列中的消息仍然可以在系统恢复后被处理。


顺序保证：在大多使用场景下，数据处理的顺序都很重要。大部分消息队列本来就是排序的，并且能保证数据会按照特定的顺序来处理。Kafka保证一个Partition内的消息的有序性。


缓冲：在任何重要的系统中，都会有需要不同的处理时间的元素。例如，加载一张图片比应用过滤器花费更少的时间。消息队列通过一个缓冲层来帮助任务最高效率的执行———写入队列的处理会尽可能的快速。该缓冲有助于控制和优化数据流经过系统的速度。


异步通信：很多时候，用户不想也不需要立即处理消息。消息队列提供了异步处理机制，允许用户把一个消息放入队列，但并不立即处理它。想向队列中放入多少消息就放多少，然后在需要的时候再去处理它们。

缺点：
由于是批量发送，数据并非真正的实时； 仅支持统一分区内消息有序，无法实现全局消息有序；
有可能消息重复消费；
依赖zookeeper进行元数据管理，等等。
2.4 与常见MQ的对比#（1）RabbitMQ​		RabbitMQ是使用Erlang编写的一个开源的消息队列，本身支持很多的协议：AMQP，XMPP, SMTP, STOMP，也正因如此，它非常重量级，更适合于企业级的开发。同时实现了Broker构架，这意味着消息在发送给客户端时先在中心队列排队。对路由，负载均衡或者数据持久化都有很好的支持。（2）Redis​		Redis是一个基于Key-Value对的NoSQL数据库，开发维护很活跃。虽然它是一个Key-Value数据库存储系统，但它本身支持MQ功能，所以完全可以当做一个轻量级的队列服务来使用。对于RabbitMQ和Redis的入队和出队操作，各执行100万次，每10万次记录一次执行时间。测试数据分为128Bytes、512Bytes、1K和10K四个不同大小的数据。实验表明：入队时，当数据比较小时Redis的性能要高于RabbitMQ，而如果数据大小超过了10K，Redis则慢的无法忍受；出队时，无论数据大小，Redis都表现出非常好的性能，而RabbitMQ的出队性能则远低于Redis。（3）Kafka/JafkaKafka是Apache下的一个子项目，是一个高性能跨语言分布式发布/订阅消息队列系统，而Jafka是在Kafka之上孵化而来的，即Kafka的一个升级版。具有以下特性：快速持久化，可以在O(1)的系统开销下进行消息持久化；高吞吐，在一台普通的服务器上既可以达到10W/s的吞吐速率；完全的分布式系统，Broker、Producer、Consumer都原生自动支持分布式，自动实现负载均衡；支持Hadoop数据并行加载，对于像Hadoop的一样的日志数据和离线分析系统，但又要求实时处理的限制，这是一个可行的解决方案。Kafka通过Hadoop的并行加载机制统一了在线和离线的消息处理。Apache Kafka相对于ActiveMQ是一个非常轻量级的消息系统，除了性能非常好之外，还是一个工作良好的分布式系统。2.5 Kafka的名词解释#2.5.1 broker#​		Kafka 集群包含一个或多个服务器，服务器节点称为broker。正常情况下一个broker存一个partition​		broker存储topic的数据。如果某topic有N个partition，集群有N个broker，那么每个broker存储该topic的一个partition。​		如果某topic有N个partition，集群有(N+M)个broker，那么其中有N个broker存储该topic的一个partition，剩下的M个broker不存储该topic的partition数据。​		如果某topic有N个partition，集群中broker数目少于N个，那么一个broker存储该topic的一个或多个partition。在实际生产环境中，尽量避免这种情况的发生，这种情况容易导致Kafka集群数据不均衡。2.5.2 Topic#​		每条发布到Kafka集群的消息都有一个类别，这个类别被称为Topic。（物理上不同Topic的消息分开存储，逻辑上一个Topic的消息虽然保存于一个或多个broker上但用户只需指定消息的Topic即可生产或消费数据而不必关心数据存于何处）​		类似于数据库的表名2.5.3 Partition#​		topic中的数据分割为一个或多个partition。每个topic至少有一个partition。每个partition中的数据使用多个segment文件存储。partition中的数据是有序的，不同partition间的数据丢失了数据的顺序。如果topic有多个partition，消费数据时就不能保证数据的顺序。在需要严格保证消息的消费顺序的场景下，需要将partition数目设为1。2.5.4 Producer#​		生产者即数据的发布者，该角色将消息发布到Kafka的topic中。broker接收到生产者发送的消息后，broker将该消息追加到当前用于追加数据的segment文件中。生产者发送的消息，存储到一个partition中，生产者也可以指定数据存储的partition。2.5.5 Consumer#​		消费者可以从broker中读取数据。消费者可以消费多个topic中的数据。2.5.6 Consumer Group#​		每个Consumer属于一个特定的Consumer Group（可为每个Consumer指定group name，若不指定group name则属于默认的group）。这是kafka用来实现一个topic消息的广播（发给所有的consumer）和单播（发给任意一个consumer）的手段。一个topic可以有多个CG。topic的消息会复制-给consumer。如果需要实现广播，只要每个consumer有一个独立的CG就可以了。要实现单播只要所有的consumer在同一个CG。用CG还可以将consumer进行自由的分组而不需要多次发送消息到不同的topic。2.5.7 Leader#​		每个partition有多个副本，其中有且仅有一个作为Leader，Leader是当前负责数据的读写的partition。2.5.8 Follower#​		Follower跟随Leader，所有写请求都通过Leader路由，数据变更会广播给所有Follower，Follower与Leader保持数据同步。如果Leader失效，则从Follower中选举出一个新的Leader。当Follower与Leader挂掉、卡住或者同步太慢，leader会把这个follower从“in sync replicas”（ISR）列表中删除，重新创建一个Follower。2.5.9 Offset#​		kafka的存储文件都是按照offset.kafka来命名，用offset做名字的好处是方便查找。例如你想找位于2049的位置，只要找到2048.kafka的文件即可。当然the first offset就是00000000000.kafka2.6 为什么说Kafka性能很好，体现在哪里？#
顺序读写 ：收到数据之后直接顺序写入到Topic的每个partition对应的文件尾部，避免了文件的随即写，性能是随机写的1000倍；
零拷贝 ：
分区
批量发送
数据压缩
总结：Kafka速度的秘诀在于，它把所有的消息都变成一个批量的文件，并且进行合理的批量压缩，减少网络IO损耗，通过mmap提高I/O速度，写入数据的时候由于单个Partion是末尾添加所以速度最优。发送数据的时候使用sendfile。2.6.1 零拷贝 mmap（Memory Mapped Files）#对于RocketMQ来说这两个步骤使用的是mmap+write，而Kafka则是使用mmap+write持久化数据，发送数据使用sendfile。传统的文件拷贝：传统的文件拷贝通常通过read()把数据从硬盘读取到内核缓冲区，再复制到用户缓冲区；然后再通过write()写入到socket缓冲区，最后写入网卡设备。整个过程发生了4次用户态和内核态的上下文切换和4次拷贝，具体流程如下：
用户进程通过read()方法向操作系统发起调用，此时上下文从用户态转向内核态
DMA控制器把数据从硬盘中拷贝到读缓冲区
CPU把读缓冲区数据拷贝到应用缓冲区，上下文从内核态转为用户态，read()返回
用户进程通过write()方法发起调用，上下文从用户态转为内核态
CPU将应用缓冲区中数据拷贝到socket缓冲区
DMA控制器把数据从socket缓冲区拷贝到网卡，上下文从内核态切换回用户态，write()返回
那么，这里指的用户态、内核态指的是什么？上下文切换又是什么？​		简单来说，用户空间指的就是用户进程的运行空间，内核空间就是内核的运行空间。​		如果进程运行在内核空间就是内核态，运行在用户空间就是用户态。​		为了安全起见，是互相隔离的，而在用户态和内核态之间的上下文切换也是比较耗时的。​		从上面我们可以看到，一次简单的IO过程产生了4次上下文切换，这个无疑在高并发场景下会对性能产生较大的影响。那么什么又是DMA拷贝呢？​		因为对于一个IO操作而言，都是通过CPU发出对应的指令来完成，但是相比CPU来说，IO的速度太慢了，CPU有大量的时间处于等待IO的状态。​		因此就产生了DMA（Direct Memory Access）直接内存访问技术，本质上来说他就是一块主板上独立的芯片，通过它来进行内存和IO设备的数据传输，从而减少CPU的等待时间。​		但是无论谁来拷贝，频繁的拷贝耗时也是对性能的影响。零拷贝零拷贝技术是指计算机执行操作时，CPU不需要先将数据从某处内存复制到另一个特定区域，这种技术通常用于通过网络传输文件时节省CPU周期和内存带宽。1那么对于零拷贝而言，并非真的是完全没有数据拷贝的过程，只不过是减少用户态和内核态的切换次数以及CPU拷贝的次数。这里，仅仅有针对性的来谈谈几种常见的零拷贝技术。mmap+write​	mmap+write简单来说就是使用mmap替换了read+write中的read操作，减少了一次CPU的拷贝。​	mmap主要实现方式是将读缓冲区的地址和用户缓冲区的地址进行映射，内核缓冲区和应用缓冲区共享，从而减少了从读缓冲区到用户缓冲区的一次CPU拷贝。整个过程发生了4次用户态和内核态的上下文切换和3次拷贝，具体流程如下：
用户进程通过mmap()方法向操作系统发起调用，上下文从用户态转向内核态
DMA控制器把数据从硬盘中拷贝到读缓冲区
上下文从内核态转为用户态，mmap调用返回
用户进程通过write()方法发起调用，上下文从用户态转为内核态
CPU将读缓冲区中数据拷贝到socket缓冲区
DMA控制器把数据从socket缓冲区拷贝到网卡，上下文从内核态切换回用户态，write()返回
​	mmap的方式节省了一次CPU拷贝，同时由于用户进程中的内存是虚拟的，只是映射到内核的读缓冲区，所以可以节省一半的内存空间，比较适合大文件的传输。sendfile​	相比mmap来说，sendfile同样减少了一次CPU拷贝，而且还减少了2次上下文切换。​	sendfile是Linux2.1内核版本后引入的一个系统调用函数，通过使用sendfile数据可以直接在内核空间进行传输，因此避免了用户空间和内核空间的拷贝，同时由于使用sendfile替代了read+write从而节省了一次系统调用，也就是2次上下文切换。​	整个过程发生了2次用户态和内核态的上下文切换和3次拷贝，具体流程如下：
用户进程通过sendfile()方法向操作系统发起调用，上下文从用户态转向内核态
DMA控制器把数据从硬盘中拷贝到读缓冲区
CPU将读缓冲区中数据拷贝到socket缓冲区
DMA控制器把数据从socket缓冲区拷贝到网卡，上下文从内核态切换回用户态，sendfile调用返回
​	sendfile方法IO数据对用户空间完全不可见，所以只能适用于完全不需要用户空间处理的情况，比如静态文件服务器。sendfile+DMA Scatter/Gather​	Linux2.4内核版本之后对sendfile做了进一步优化，通过引入新的硬件支持，这个方式叫做DMA Scatter/Gather 分散/收集功能。​	它将读缓冲区中的数据描述信息—内存地址和偏移量记录到socket缓冲区，由 DMA 根据这些将数据从读缓冲区拷贝到网卡，相比之前版本减少了一次CPU拷贝的过程​	整个过程发生了2次用户态和内核态的上下文切换和2次拷贝，其中更重要的是完全没有CPU拷贝，具体流程如下：
用户进程通过sendfile()方法向操作系统发起调用，上下文从用户态转向内核态
DMA控制器利用scatter把数据从硬盘中拷贝到读缓冲区离散存储
CPU把读缓冲区中的文件描述符和数据长度发送到socket缓冲区
DMA控制器根据文件描述符和数据长度，使用scatter/gather把数据从内核缓冲区拷贝到网卡
sendfile()调用返回，上下文从内核态切换回用户态
DMA gather和sendfile一样数据对用户空间不可见，而且需要硬件支持，同时输入文件描述符只能是文件，但是过程中完全没有CPU拷贝过程，极大提升了性能。总结​	由于CPU和IO速度的差异问题，产生了DMA技术，通过DMA搬运来减少CPU的等待时间。​	传统的IOread+write方式会产生2次DMA拷贝+2次CPU拷贝，同时有4次上下文切换。​	而通过mmap+write方式则产生2次DMA拷贝+1次CPU拷贝，4次上下文切换，通过内存映射减少了一次CPU拷贝，可以减少内存使用，适合大文件的传输。​	sendfile方式是新增的一个系统调用函数，产生2次DMA拷贝+1次CPU拷贝，但是只有2次上下文切换。因为只有一次调用，减少了上下文的切换，但是用户空间对IO数据不可见，适用于静态文件服务器。​	sendfile+DMA gather方式产生2次DMA拷贝，没有CPU拷贝，而且也只有2次上下文切换。虽然极大地提升了性能，但是需要依赖新的硬件设备支持。2.6.2 分区#在一组机器上对数据进行分区2.6.3 批量压缩#在很多情况下，系统的瓶颈不是CPU或磁盘，而是网络IO，对于需要在广域网上的数据中心之间发送消息的数据流水线尤其如此。进行数据压缩会消耗少量的CPU资源,不过对于kafka而言,网络IO更应该需要考虑。

如果每个消息都压缩，但是压缩率相对很低，所以Kafka使用了批量压缩，即将多个消息一起压缩而不是单个消息压缩；


Kafka允许使用递归的消息集合，批量的消息可以通过压缩的形式传输并且在日志中也可以保持压缩格式，直到被消费者解压缩；


Kafka支持多种压缩协议，包括Gzip和Snappy压缩协议。

2.7 请说明Kafka相对传统技术有什么优势?#
快速: 单一的Kafka代理可以处理成千上万的客户端，每秒处理数兆字节的读写操作。
可伸缩: 在一组机器上对数据进行分区简化，以支持更大的数据
持久: 消息是持久性的，并在集群中进行复制，以防止数据丢失。
设计: 它提供了容错保证和持久性
2.8 解释Kafka的Zookeeper是什么?我们可以在没有Zookeeper的情况下使用Kafka吗?#​	Zookeeper是一个开放源码的、高性能的协调服务，它用于Kafka的分布式应用。​	不，不可能越过Zookeeper，直接联系Kafka broker。一旦Zookeeper停止工作，它就不能服务客户端请求。 Zookeeper主要用于在集群中不同节点之间进行通信 。在Kafka中，它被用于提交偏移量，因此如果节点在任何情况下都失败了，它都可以从之前提交的偏移量中获取。除此之外，它还执行其他活动，如: leader检测、分布式同步、配置管理、识别新节点何时离开或连接、集群、节点实时状态等等。2.9 解释Kafka的用户如何消费信息?#​		在Kafka中传递消息是通过使用sendfile API完成的。它支持将字节从套接口转移到磁盘，通过内核空间保存副本，并在内核用户之间调用内核。2.10 Kafka判断一个节点是否还活着有那两个条件？#（1）节点必须可以维护和ZooKeeper的连接，Zookeeper通过心跳机制检查每个节点的连接
（2）如果节点是个follower,他必须能及时的同步leader的写操作，延时不能太久2.11 producer是否直接将数据发送到broker的leader(主节点)？#​		producer直接将数据发送到broker的leader(主节点)，不需要在多个节点进行分发，为了帮助producer做到这点，所有的Kafka节点都可以及时的告知:哪些节点是活动的，目标topic目标分区的leader在哪。这样producer就可以直接将消息发送到目的地了。2.12 Kafa consumer是否可以消费指定分区消息？#​		Kafa consumer消费消息时，向broker发出”fetch”请求去消费特定分区的消息，consumer指定消息在日志中的偏移量（offset），就可以消费从这个位置开始的消息，customer拥有了offset的控制权，可以向后回滚去重新消费之前的消息，这是很有意义的消费者与分区Partition的对应关系​		同一个分区中的数据，只能被一个消费者组中的一个消费者所消费。例如 P0分区中的数据不能被Consumer Group A中C1与C2同时消费。
如果有3个Partition, p0/p1/p2，同一个消费组有3个消费者，c0/c1/c2，则为一一对应关系；
如果有3个Partition, p0/p1/p2，同一个消费组有2个消费者，c0/c1，则其中一个消费者消费2个分区的数据，另一个消费者消费一个分区的数据；
如果有2个Partition, p0/p1，同一个消费组有3个消费者，c0/c1/c3，则其中有一个消费者空闲，另外2个消费者消费分别各自消费一个分区的数据；
2.13 Kafka消息是采用Pull模式，还是Push模式？#​		Kafka最初考虑的问题是，customer应该从brokers拉取消息还是brokers将消息推送到consumer，也就是pull还push。在这方面，Kafka遵循了一种大部分消息系统共同的传统的设计：producer将消息推送到broker，consumer从broker拉取消息。一些消息系统比如Scribe和Apache Flume采用了push模式，将消息推送到下游的consumer。这样做有好处也有坏处：由broker决定消息推送的速率，对于不同消费速率的consumer就不太好处理了。消息系统都致力于让consumer以最大的速率最快速的消费消息，但不幸的是，push模式下，当broker推送的速率远大于consumer消费的速率时，consumer恐怕就要崩溃了。最终Kafka还是选取了传统的pull模式​		**Pull模式的另外一个好处是consumer可以自主决定是否批量的从broker拉取数据。Push模式必须在不知道下游consumer消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。**如果为了避免consumer崩溃而采用较低的推送速率，将可能导致一次只推送较少的消息而造成浪费。Pull模式下，consumer就可以根据自己的消费能力去决定这些策略​		Pull有个缺点是，如果broker没有可供消费的消息，将导致consumer不断在循环中轮询，直到新消息到t达。为了避免这点，Kafka有个参数可以让consumer阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发kafka的Pull模式：​		每次消费者客户端拉取数据时，通过poll方法，先调用fetcher中的fetchedRecords函数，如果获取不到数据，就会发起一个新的sendFetches请求。而在消费数据的时候，每个批次从Kafka Broker Server中拉取数据是有最大数据量限制，默认是500条，由属性（max.poll.records）控制，可以在客户端中设置该属性值来调整我们消费时每次拉取数据的量。max.poll.records返回的是一个poll请求的数据总和，与多少个分区无关。因此，每次消费从所有分区中拉取Topic的数据的总条数不会超过max.poll.records所设置的值。​		而在Fetcher的类中，在sendFetches方法中有限制拉取数据容量的限制，由属性（max.partition.fetch.bytes），默认1MB。可能会有这样一个场景，当满足max.partition.fetch.bytes限制条件，如果需要Fetch出5000条记录，每次默认500条，那么我们需要执行10次才能将这一次通过网络发起的请求全部Fetch完毕。​		这里，可能有疑问，我们不能将默认的max.poll.records属性值调到10000吗？可以调，但是还有个属性需要一起配合才可以，这个就是每次poll的超时时间（Duration.ofMillis(100)），这里需要根据你的实际每条数据的容量大小来确定设置超时时间，如果你将最大值调到10000，当你每条记录的容量很大时，超时时间还是100ms，那么可能拉取的数据少于10000条。​		最后还需要注意的事情，就是会话超时的问题。session.timeout.ms默认是10s，group.min.session.timeout.ms默认是6s，group.max.session.timeout.ms默认是30min。当你在处理消费的业务逻辑的时候，如果在10s内没有处理完，那么消费者客户端就会与Kafka Broker Server断开，消费掉数据所产生的offset就无法提交给Kafka，因为Kafka Broker Server此时认为该消费者程序已经断开，而即使你设置了自动提交属性，或者设置auto.offset.reset属性，你消费的时候还是会出现重复消费的情况，这就是因为session.timeout.ms超时的原因导致的。2.14 Kafka高效文件存储设计特点：#(1) Kafka把topic中一个parition大文件分成多个小文件段，通过多个小文件段，就容易定期清除或删除已经消费完文件，减少磁盘占用。
(2) 通过索引信息可以快速定位message和确定response的最大大小。
(3) 通过index元数据全部映射到memory，可以避免segment file的IO磁盘操作。
(4) 通过索引文件稀疏存储，可以大幅降低index文件元数据占用空间大小。2.15 Kafka 与传统消息系统之间有三个关键区别#​	(1) Kafka 持久化日志，这些日志可以被重复读取和无限期保留
​	(2) Kafka 是一个分布式系统：它以集群的方式运行，可以灵活伸缩，在内部通过复制数据提升容错能力和高可用性
​	(3) Kafka 支持实时的流式处理2.16 消费者负载均衡策略#​	结合consumer的加入和退出进行再平衡策略。2.17 kafka consumer 什么情况会触发再平衡reblance?#​	消费者再平衡过程是指 消费者重新加入消费组，并且重新分配分区Partition给消费者的过程。
新的消费者加入消费组
某个消费者从消费组中退出（异常或正常）
增加订阅主题的分区（kafka的分区数，可以动态的增加，但不能减少）
某台broker宕机，新的协调器ZK当选
某个消费者在心跳会话时间内没有发送心跳请求（配置参数：session.timeout.ms）, 组ZK认为该消费者已经退出。
2.18 描述下kafka consumer 再平衡步骤?#​	kafka的再平衡策略有三种Round Robin、Range和Sticky，默认使用Range
Round Robin：会采用轮询的方式将当前所有的分区依次分配给所有的consumer；
Range：首先会计算每个consumer可以消费的分区个数，然后按照顺序将指定个数范围的分区分配给各个consumer；
Sticky：这种分区策略是最新版本中新增的一种策略，其主要实现了两个目的：

将现有的分区尽可能均衡的分配给各个consumer，存在此目的的原因在于Round Robin和Range分配策略实际上都会导致某几个consumer承载过多的分区，从而导致消费压力不均衡；
如果发生再平衡，那么在重新分配前的基础上会尽力保证当前未宕机的consumer所消费的分区不会被分配给其他的consumer上；


​	步骤：
关闭数据拉取线程，清空队列和消息流，提交偏移量；
释放分区所有权，删除Zookeeper中分区和消费者的所有者关系；
将所有分区重新分配给每个消费者，每个消费者都会分到不同分区；
将分区对应的消费者所有关系写入Zookeeper，记录分区的所有权信息；
重启消费者拉取线程管理器，管理每个分区的拉取线程。
2.19 MQ有什么用？#消息队列有很多使用场景，比较常见的有3个：解耦、异步、削峰。

解耦：传统的软件开发模式，各个模块之间相互调用，数据共享，每个模块都要时刻关注其他模块的是否更改或者是否挂掉等等，使用消息队列，可以避免模块之间直接调用，将所需共享的数据放在消息队列中，对于新增业务模块，只要对该类消息感兴趣，即可订阅该类消息，对原有系统和业务没有任何影响，降低了系统各个模块的耦合度，提高了系统的可扩展性。


异步：消息队列提供了异步处理机制，在很多时候应用不想也不需要立即处理消息，允许应用把一些消息放入消息中间件中，并不立即处理它，在之后需要的时候再慢慢处理。


削峰：在访问量骤增的场景下，需要保证应用系统的平稳性，但是这样突发流量并不常见，如果以这类峰值的标准而投放资源的话，那无疑是巨大的浪费。使用消息队列能够使关键组件支撑突发访问压力，不会因为突发的超负荷请求而完全崩溃。消息队列的容量可以配置的很大，如果采用磁盘存储消息，则几乎等于“无限”容量，这样一来，高峰期的消息可以被积压起来，在随后的时间内进行平滑的处理完成，而不至于让系统短时间内无法承载而导致崩溃。在电商网站的秒杀抢购这种突发性流量很强的业务场景中，消息队列的强大缓冲能力可以很好的起到削峰作用。

2.20 消息队列如何保证消息不丢？#Kafka丢失消息分为如下几种情况：

生产者丢消息：
​	生产者没有设置相应的策略，发送过程中丢失数据。


Kafka自己丢消息：
​	比较常见的一个场景，就是Kafka的某个broker宕机了，然后重新选举partition的leader时。如果此时follower还没来得及同步数据，leader就挂了，然后某个follower成为了leader，它就少了一部分数据。


消费端丢消息：
​	消费者消费到了这个数据，然后消费之自动提交了offset，让Kafka知道你已经消费了这个消息，当你准备处理这个消息时，自己挂掉了，那么这条消息就丢了。

针对上述三种情况，Kafka可以采用如下方式避免消息丢失：

生产者丢消息：
关闭自动提交offset，在自己处理完毕之后手动提交offset，这样就不会丢失数据。


Kafka自己丢消息：
一般要求设置4个参数来保证消息不丢失：

给topic设置 replication.factor 参数，这个值必须大于1，表示要求每个partition必须至少有2个副本。
在kafka服务端设置 min.isync.replicas 参数，这个值必须大于1，表示 要求一个leader至少感知到有不少一个follower在跟自己保持联系正常同步数据，这样才能保证leader挂之后还有一个follower。
在生产者端设置 acks=all ，表示 要求每条每条数据，必须是写入所有replica副本之后，才能认为是写入成功了。
在生产者端设置 retries=MAX (很大的一个值)，表示这个是要求一旦写入事变，就无限重试。



消费端丢消息：
如果按照上面设置了ack=all，则一定不会丢失数据，要求是，你的leader接收到消息，所有的follower都同步到了消息之后，才认为本次写成功了。如果没满足这个条件，生产者会自动不断的重试，重试无限次。


计算机网络#1、OSI七层模型#

为什么分七层
支持异构网络的互联互通。


七层分别负责的内容（功能）
OSI 模型把网络通信的工作分为 7 层，从下到上分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。

(1) 物理层任务：透明地传输比特流。功能：为数据段设备提供传送数据通路传输单位：比特所实现的硬件：集线器，中继器(2) 数据链路层任务：将网络层传输下来的IP数据报组装成帧功能：a. 链路连接的建立、拆除和分离​			b. 帧定界和帧同步​			c. 差错检测传输单位：帧所实现的硬件：交换机、网桥协议：PPP,HDLC、SDLC、STP、ARQ(3) 网络层任务：a. 将传输层传下来的报文段封装成分组​			b.选择合适的路由，使得传输层传下来的分组能够交付到目的主机功能：a. 为传输层提供服务​			b. 组包和拆包​			c. 路由选择​			d.拥塞控制传输单位：数据段所实现的硬件：路由器协议：ICMP、ARP、RARP、IP、IGMP、OSPF(4)传输层任务：负责主机中两个进程之间的通信功能：​			a. 为端到端连接提供可靠的服务​			b. 为端到端连接提供流量控制、差错控制、服务质量等管理服务传输单位：报文段（TCP）或用户数据报（UDP）协议：TCP、UDP(5)会话层任务：不同主机上各进程间的对话功能：管理主机间的会话进程，包括建立、管理以及终止进程间的会话。是一种端到端的服务(6)表示层​		负责处理在两个内部数据表示结构不同的通信系统之间交换信息的表示格式，为数据加密和解密以及为提高传输效率提供必需的数据压缩以及解压等功能。(7)应用层任务：提供系统与用户的接口功能：​			a.文件传输​			b. 访问和管理​			c. 电子邮件服务协议：FTP、SMTP、POP3、HTTP、DNS、TELnet2、TCP/IP模型#

TCP/IP协议定义
TCP/IP（Transmission Control Protocol/Internet Protocol，传输控制协议/网际协议）是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议不仅仅指的是TCP和IP两个协议，而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇， 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性，所以被称为TCP/IP协议。


TCP/IP协议组成
TCP/IP结构模型分为**应用层、传输层、网络层、链路层（网络接口层）**四层，以下是各层的详细

介绍：（1）应用层应用层是TCP/IP协议的第一层，是直接为应用进程提供服务的。​		a. 对不同种类的应用程序它们会根据自己的需要来使用应用层的不同协议，邮件传输应用使用了SMTP协议、万维网应用使用了HTTP协议、远程登录服务应用使用了有TELNET协议。​		b. 应用层还能加密、解密、格式化数据。​		c. 应用层可以建立或解除与其他节点的联系，这样可以充分节省网络资源。（2）传输层​		作为TCP/IP协议的第二层，运输层在整个TCP/IP协议中起到了中流砥柱的作用。且在运输层中，TCP和UDP也同样起到了中流砥柱的作用。（3）网络层​		网络层在TCP/IP协议中的位于第三层。在TCP/IP协议中网络层可以进行网络连接的建立和终止以及IP地址的寻找等功能。（4）链路层（网络接口层）​		在TCP/IP协议中，网络接口层位于第四层。由于网络接口层兼并了物理层和数据链路层。所以，网络接口层既是传输数据的物理媒介，也可以为网络层提供一条准确无误的线路。
TCP/IP协议特点
TCP/IP协议能够迅速发展起来并成为事实上的标准，是它恰好适应了世界范围内数据通信的需要。它有以下特点：（1）协议标准是完全开放的，可以供用户免费使用，并且独立于特定的计算机硬件与操作系统；（2）独立于网络硬件系统，可以运行在广域网，更适合于互联网；（3）网络地址统一分配，网络中每一设备和终端都具有一个唯一地址；（4）高层协议标准化，可以提供多种多样可靠网络服务。3、TCP与UDP的区别#TCP和UDP有如下区别：
连接：TCP面向连接的传输层协议，即传输数据之前必须先建立好连接；UDP无连接。
服务对象：TCP点对点的两点间服务，即一条TCP连接只能有两个端点；UDP支持一对一，一对多，多对一，多对多的交互通信。
可靠性：TCP可靠交付：无差错，不丢失，不重复，按序到达；UDP尽最大努力交付，不保证可靠交付。
拥塞控制/流量控制：TCP有拥塞控制和流量控制保证数据传输的安全性；UDP没有拥塞控制，网络拥塞不会影响源主机的发送效率。
报文长度：TCP动态报文长度，即TCP报文长度是根据接收方的窗口大小和当前网络拥塞情况决定的；UDP面向报文，不合并，不拆分，保留上面传下来报文的边界。
首部开销：TCP首部开销大，首部20个字节；UDP首部开销小，8字节（源端口，目的端口，数据长度，校验和）。
适用场景（由特性决定）：数据完整性需让位于通信实时性，则应该选用TCP 协议（如文件传输、重要状态的更新等）；反之，则使用 UDP 协议（如视频传输、实时通信等）。
4、TCP的三次握手与四次挥手#4.1 三次握手#三次握手

第一次握手：建立连接时，客户端发送syn包（syn=x）到服务器，并进入SYN_SENT状态，等待服务器确认；SYN：同步序列编号（Synchronize Sequence Numbers）。


第二次握手：服务器收到syn包，必须确认客户的SYN（ack=x+1），同时自己也发送一个SYN包 （syn=y），即SYN+ACK包，此时服务器进入SYN_RECV状态；


第三次握手：客户端收到服务器的SYN+ACK包，向服务器发送确认包ACK(ack=y+1），此包发送完毕，客户端和服务器进入ESTABLISHED（TCP连接成功）状态，完成三次握手。

4.2 四次挥手#四次挥手

客户端进程发出连接释放报文，并且停止发送数据。释放数据报文首部，FIN=1，其序列号为seq=u（等于前面已经传送过来的数据的最后一个字节的序号加1），此时，客户端进入FIN-WAIT-1（终止等待1）状态。 TCP规定，FIN报文段即使不携带数据，也要消耗一个序号。


服务器收到连接释放报文，发出确认报文，ACK=1，ack=u+1，并且带上自己的序列号seq=v，此时，服务端就进入了CLOSE-WAIT（关闭等待）状态。TCP服务器通知高层的应用进程，客户端向服务器的方向就释放了，这时候处于半关闭状态，即客户端已经没有数据要发送了，但是服务器若发送数据，客户端依然要接受。这个状态还要持续一段时间，也就是整个CLOSE-WAIT状态持续的时间。


客户端收到服务器的确认请求后，此时，客户端就进入FIN-WAIT-2（终止等待2）状态，等待服务器发送连接释放报文（在这之前还需要接受服务器发送的最后的数据）。


服务器将最后的数据发送完毕后，就向客户端发送连接释放报文，FIN=1，ack=u+1，由于在半关闭状态，服务器很可能又发送了一些数据，假定此时的序列号为seq=w，此时，服务器就进入了LAST-ACK（最后确认）状态，等待客户端的确认。


客户端收到服务器的连接释放报文后，必须发出确认，ACK=1，ack=w+1，而自己的序列号是seq=u+1，此时，客户端就进入了TIME-WAIT（时间等待）状态。注意此时TCP连接还没有释放，必须经过2∗∗MSL（最长报文段寿命）的时间后，当客户端撤销相应的TCB后，才进入CLOSED状态。


服务器只要收到了客户端发出的确认，立即进入CLOSED状态。同样，撤销TCB后，就结束了这次的TCP连接。可以看到，服务器结束TCP连接的时间要比客户端早一些。

为什么需要四次挥手？​		TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工模式，这就意味着，当客户端发出FIN报文段时，只是表示客户端已经没有数据要发送了，客户端告诉服务器，它的数据已经全部发送完毕了；但是，这个时候客户端还是可以接受来自服务端的数据；当服务端返回ACK报文段时，表示它已经知道客户端没有数据发送了，但是服务端还是可以发送数据到客户端的；当服务端也发送了FIN报文段时，这个时候就表示服务端也没有数据要发送了，就会告诉客户端，我也没有数据要发送了，之后彼此就会愉快的中断这次TCP连接。简单地说，前 2 次挥手用于关闭一个方向的数据通道，后两次挥手用于关闭另外一个方向的数据通道。5、HTTP和HTTPS的区别#https和https主要存在以下的区别：

HTTPS 协议需要到 CA （Certificate Authority，证书颁发机构）申请证书，一般免费证书较少，因而需要一定费用。(以前的网易官网是http，而网易邮箱是 https 。)


HTTP 是超文本传输协议，信息是明文传输，HTTPS 则是具有安全性的 SSL 加密传输协议。


HTTP 和 HTTPS 使用的是完全不同的连接方式，用的端口也不一样，前者是80，后者是443。


HTTP 的连接很简单，是无状态的。HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议，比 HTTP 协议安全。(无状态的意思是其数据包的发送、传输和接收都是相互独立的。无连接的意思是指通信双方都不长久的维持对方的任何信息。)

6、HTTP1.0、HTTP1.1、HTTP2.0、HTTP3.0#

HTTP0.9
最简单的只有请求行 GET index.html


HTTP1.0
（1）增加请求头、响应头，让请求和相应都更清晰
（2）增加状态码，让响应更清晰
（3）增加缓存功能，已请求过的内容再次请求时就可直接使用缓存


HTTP1.1
（1）持久连接，多个http请求使用同一个tcp连接，减少了tcp建立连接时的开销
（2）客户端和服务器之间可以建立多个tcp连接以解决队头阻塞的问题
（3）响应体可以分块传输，无需一次传输全部内容
（4）响应头增加content-length字段满足动态内容无法一次计算出长度和无法一次传输完成的问题
（5）增加了安全机制和cookie机制


HTTP2.0
多路复用，客户端和服务器之间只建立一条tcp，每个http请求被切分成多帧，多个http的帧混合在一起在一个tcp连接上传送


HTTP3.0
不再使用tcp协议，因为tcp依然是顺序发送，顺序接收的，依然有队头堵塞问题，干掉tcp才能解决队头堵塞问题。google的QUIC就使用了udp协议。


数据结构#1、排序算法#排序算法的时空复杂度博客链接

冒泡排序：对相邻的元素进行两两比较，顺序相反则进行交换，这样，每一趟会将最小或最大的元素“浮”到顶端，最终达到完全有序。


插入排序：将数组分为排序和未排序两部分，每次从未排序部分选取一个元素，按顺序插入到已排序部分。


选择排序：首先在未排序序列中找到最小（大）元素，存放到排序序列的起始位置，然后，再从剩余未排序元素中继续寻找最小（大）元素，然后放到已排序序列的末尾。以此类推，直到所有元素均排序完毕。


快速排序：是选取一个元素，然后经过交换元素，保证选定元素的左边都小于它，右边元素都大于它。每次操作后，选定元素的位置就是排序后的位置。通过一趟排序将要排序的数据分割成独立的两部分，其中一部分的所有数据都比另外一部分的所有数据都要小，然后再按此方法对这两部分数据分别进行快速排序，整个排序过程可以递归进行，以此达到整个数据变成有序序列。


希尔排序（不重要）：把记录按下标的一定增量分组，对每组使用直接插入排序算法排序；随着增量逐渐减少，每组包含的关键词越来越多，当增量减至1时，整个文件恰被分成一组，算法便终止。


堆排序：将待排序序列构造成一个大顶堆，此时，整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换，此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆，这样会得到n个元素的次小值。如此反复执行，便能得到一个有序序列了


归并排序：是利用归并的思想实现的排序方法，该算法采用经典的分治（divide-and-conquer）策略。分为多个子数组，再合并的方法。

2、栈、队列、树#栈：先入后出队列：先入先出二叉树：二叉树特点是每个结点最多只能有两棵子树，且有左右之分3、平衡二叉树#3.1 介绍#​	平衡二叉树又被称为AVL树（有别于AVL算法），且具有以下性质：它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1，并且左右两个子树都是一棵平衡二叉树。这个方案很好的解决了二叉查找树退化成链表的问题，把插入，查找，删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间，不过相对二叉查找树来说，时间上稳定了很多。3.2 树的调整：调整的最小不平衡子树#左左：插入的节点是左子树的左孩子：直接右旋右右：插入的节点是右子树的右孩子：直接左旋左右：插入的节点是左子树的右孩子：先左旋再右旋**右左：插入的节点是右子树的左孩子：**先右旋再左旋3.3 二叉查找树的删除：#​	① 被删除节点没有儿子，即为叶节点。那么，直接将该节点删除就OK了。
​	② 被删除节点只有一个儿子。那么，直接删除该节点，并用该节点的唯一子节点顶替它的位置。
​	③ 被删除节点有两个儿子。那么，先找出它的后继节点；然后把“它的后继节点的内容”复制给“该节点的内容”；之后，删除“它的后继节点”。在这里，后继节点相当于替身，在将后继节点的内容复制给”被删除节点”之后，再将后继节点删除。这样就巧妙的将问题转换为”删除后继节点”的情况了，下面就考虑后继节点。 在”被删除节点”有两个非空子节点的情况下，它的后继节点不可能是双子非空。既然”的后继节点”不可能双子都非空，就意味着”该节点的后继节点”要么没有儿子，要么只有一个儿子。若没有儿子，则按”情况① “进行处理；若只有一个儿子，则按”情况② “进行处理。在平衡二叉树中，删除之后，按照上述规则调整树的平衡性。4、红黑树#​		 R-B Tree，全称是Red-Black Tree，又称为“红黑树”，它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色，可以是红(Red)或黑(Black)。红黑树的特性:
（1）每个节点或者是黑色，或者是红色。
（2）根节点是黑色。
（3）每个叶子节点（NIL）是黑色。 [注意：这里叶子节点，是指为空(NIL或NULL)的叶子节点！]
（4）如果一个节点是红色的，则它的子节点必须是黑色的。
（5）从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。一棵含有n个节点的红黑树的高度至多为2log(n+1).红黑树的插入​	第一步: 将红黑树当作一颗二叉查找树，将节点插入。​	第二步：将插入的节点着色为”红色”。​	第三步: 通过一系列的旋转或着色等操作，使之重新成为一颗红黑树。​		(Case 1)叔叔是红色1(01) 将“父节点”设为黑色。2(02) 将“叔叔节点”设为黑色。3(03) 将“祖父节点”设为“红色”。4(04) 将“祖父节点”设为“当前节点”(红色节点)；即，之后继续对“当前节点”进行操作。​		(Case 2)叔叔是黑色，且当前节点是右孩子1(01) 将“父节点”作为“新的当前节点”。2(02) 以“新的当前节点”为支点进行左旋。​		(Case 3)叔叔是黑色，且当前节点是左孩子1(01) 将“父节点”设为“黑色”。2(02) 将“祖父节点”设为“红色”。3(03) 以“祖父节点”为支点进行右旋红黑树的删除：第一步：将红黑树当作一颗二叉查找树，将节点删除。
这和”删除常规二叉查找树中删除节点的方法是一样的”。分3种情况：
① 被删除节点没有儿子，即为叶节点。那么，直接将该节点删除就OK了。
② 被删除节点只有一个儿子。那么，直接删除该节点，并用该节点的唯一子节点顶替它的位置。
③ 被删除节点有两个儿子。那么，先找出它的后继节点；然后把“它的后继节点的内容”复制给“该节点的内容”；之后，删除“它的后继节点”。在这里，后继节点相当于替身，在将后继节点的内容复制给”被删除节点”之后，再将后继节点删除。这样就巧妙的将问题转换为”删除后继节点”的情况了，下面就考虑后继节点。 在”被删除节点”有两个非空子节点的情况下，它的后继节点不可能是双子非空。既然”的后继节点”不可能双子都非空，就意味着”该节点的后继节点”要么没有儿子，要么只有一个儿子。若没有儿子，则按”情况① “进行处理；若只有一个儿子，则按”情况② “进行处理。第二步：通过”旋转和重新着色”等一系列来修正该树，使之重新成为一棵红黑树。5、B树与B+树#5.1 B树#B树,它是一颗多路平衡查找树。定义：m阶的B树，一个结点最多存m-1个元素，最少存m/2个元素，根结点可以只存一个，加点关键字从小到大排序。
每个节点最多有m-1个关键字（可以存有的键值对）。
根节点最少可以只有1个关键字。
非根节点至少有m/2个关键字。
每个节点中的关键字都按照从小到大的顺序排列，每个关键字的左子树中的所有关键字都小于它，而右子树中的所有关键字都大于它。
所有叶子节点都位于同一层，或者说根节点到每个叶子节点的长度都相同。
每个节点都存有索引和数据，也就是对应的key和value。
5.2 B+树#B+树其实和B树是非常相似的，我们首先看看相同点。
根节点至少一个元素
非根节点元素范围：m/2 &amp;lt;= k &amp;lt;= m-1
不同点。
B+树有两种类型的节点：内部结点（也称索引结点）和叶子结点。内部节点就是非叶子节点，内部节点不存储数据，只存储索引，数据都存储在叶子节点。
内部结点中的key都按照从小到大的顺序排列，对于内部结点中的一个key，左树中的所有key都小于它，右子树中的key都大于等于它。叶子结点中的记录也按照key的大小排列。
每个叶子结点都存有相邻叶子结点的指针，叶子结点本身依关键字的大小自小而大顺序链接。
父节点存有右孩子的第一个元素的索引。
6、跳表#何为跳表？(关于redis中zset底层跳表的理解)

跳表是一个随机化的数据结构，实质就是一种可以进行二分查找的有序链表。


跳表在原有的有序链表上面增加了多级索引，通过索引来实现快速查找。


跳表不仅能提高搜索性能，同时也可以提高插入和删除操作的性能。

6.1 跳表的查找#​	考虑一个有序链表，我们要查找3、7、17这几个元素，我们只能从头开始遍历链表，直到查找到元素为止。​	上述这个链表是有序的，但是不能使用二分查找，是不是很捉急？（P.S.数组可以实现二分查找）​	那么，有没有什么方法可以实现有序链表的二分查找呢？​	答案是肯定的，那就是我们将要介绍的这种数据结构——跳表。​	我们把一些节点从有序表中提取出来，缓存一级索引，就组成了下面这样的结构：​	现在，我们要查找17这个元素是不是要快很多呢？​	我们只要从一级索引往后遍历即可，只需要经过1、6、15、17这几个元素就可以找到17了。​	那么，我们要查找11这个元素呢？​	我们从一级索引的1开始，向右到6，再向右发现是15，它比11大，此路不通，从6往下走，再从下面的6往右走，到7，再到11。​	同样地，一级索引也可以往上再提取一层，组成二级索引，如下：​	这时候我们再查找17这个元素呢？​	只需要经过6、15、17这几个元素就可以找到17了。​	这基本上就是跳表的核心思想了，其实这也是一个“空间换时间”的算法，通过向上提取索引增加了查找的效率。6.2 跳表的插入#​	上面讲的都是跳表的查询，那么，该如何向跳表中插入元素呢？​	比如，我们要向上面这个跳表添加一个元素8。​	首先，我们先根据投硬币的方式，决定8这个元素要占据的层数，没错就是扔硬币，是不是很好玩儿^^​	比如，层数level=2。​	然后，找到8这个元素在下面两层的前置节点。​	接着，就是链表的插入元素操作了，比较简单。​	最后，就像下面这样：6.3 跳表的删除#​	查询、插入元素都讲了，下面我们就来说说怎么删除元素。​	首先，找到各层中包含元素x的节点。​	然后，使用标准的链表删除元素的方法删	除即可。​	比如，要删除17这个元素。6.4 标准化跳表#我们每两个元素提取一个元素作为上一级的索引，很像平衡二叉树。​	（1）跳表是可以实现二分查找的有序链表；​	（2）每个元素插入时随机生成它的level；​	（3）最低层包含所有的元素；​	（4）如果一个元素出现在level(x)，那么它肯定出现在x以下的level中；​	（5）每个索引节点包含两个指针，一个向下，一个向右；​	（6）跳表查询、插入、删除的时间复杂度为O(log n)，与平衡二叉树接近；6.5 Redis为什么用跳表而不是用红黑树？#​	首先，我们来分析下Redis的有序集合支持的操作：​	1）插入元素​	2）删除元素​	3）查找元素​	4）有序输出所有元素​	5）查找区间内所有元素​	其中，前4项红黑树都可以完成，且时间复杂度与跳表一致。​	但是，最后一项，红黑树的效率就没有跳表高了。​	在跳表中，要查找区间的元素，我们只要定位到两个区间端点在最低层级的位置，然后按顺序遍历元素就可以了，非常高效。​	而红黑树只能定位到端点后，再从首位置开始每次都要查找后继节点，相对来说是比较耗时的。​	此外，跳表实现起来很容易且易读，红黑树实现起来相对困难，所以Redis选择使用跳表来实现有序集合。
设计模式#1、六大设计原则#1.1 单一职责原则#​		一个类只做它该做的事情。（单一职责原则想表达的就是”高内聚”，写代码最终极的原则只有六个字”高内聚、低耦合”，所谓的高内聚就是一个代码模块只完成一项功能，在面向对象中，如果只让一个类完成它该做的事，而不涉及与它无关的领域就是践行了高内聚的原则，这个类就只有单一职责。另一个是模块化，好的自行车是组装车，从减震叉、刹车到变速器，所有的部件都是可以拆卸和重新组装的，好的乒乓球拍也不是成品拍，一定是底板和胶皮可以拆分和自行组装的，一个好的软件系统，它里面的每个功能模块也应该是可以轻易的拿到其他系统中使用的，这样才能实现软件复用的目标。）1.2 里氏替换原则#​		任何时候都可以用子类型替换掉父类型。（关于里氏替换原则的描述，Barbara Liskov女士的描述比这个要复杂得多，但简单的说就是能用父类型的地方就一定能使用子类型。里氏替换原则可以检查继承关系是否合理，如果一个继承关系违背了里氏替换原则，那么这个继承关系一定是错误的，需要对代码进行重构。例如让猫继承狗，或者狗继承猫，又或者让正方形继承长方形都是错误的继承关系，因为你很容易找到违反里氏替换原则的场景。需要注意的是：子类一定是增加父类的能力而不是减少父类的能力，因为子类比父类的能力更多，把能力多的对象当成能力少的对象来用当然没有任何问题。）​	优点：

代码共享，减少创建类的工作量，每个子类都拥有父类的方法和属性；


提高代码的可重用性；


提高代码的可扩展性；


提高产品或项目的开放性。

1.3 依赖倒置原则#​		面向接口编程。（该原则说得直白和具体一些就是声明方法的参数类型、方法的返回类型、变量的引用类型时，尽可能使用抽象类型而不用具体类型，因为抽象类型可以被它的任何一个子类型所替代，请参考下面的里氏替换原则。）依赖倒置原则可以减少类间的耦合性，提高系统的稳定性，降低并行开发引起的风险，提高代码的可读性和可维护性。依赖倒置原则是JavaBean、EJB和COM等组件设计模型背后的基本原则。1.4 接口隔离原则#​		接口要小而专，绝不能大而全。（臃肿的接口是对接口的污染，既然接口表示能力，那么一个接口只应该描述一种能力，接口也应该是高度内聚的。例如，琴棋书画就应该分别设计为四个接口，而不应设计成一个接口中的四个方法，因为如果设计成一个接口中的四个方法，那么这个接口很难用，毕竟琴棋书画四样都精通的人还是少数，而如果设计成四个接口，会几项就实现几个接口，这样的话每个接口被复用的可能性是很高的。Java中的接口代表能力、代表约定、代表角色，能否正确的使用接口一定是编程水平高低的重要标识。）1.5 迪米特法则#​		**迪米特法则又叫最少知识原则，一个对象应当对其他对象有尽可能少的了解。**再复杂的系统都可以为用户提供一个简单的门面，Java Web开发中作为前端控制器的Servlet或Filter不就是一个门面吗，浏览器对服务器的运作方式一无所知，但是通过前端控制器就能够根据你的请求得到相应的服务。调停者模式也可以举一个简单的例子来说明，例如一台计算机，CPU、内存、硬盘、显卡、声卡各种设备需要相互配合才能很好的工作，但是如果这些东西都直接连接到一起，计算机的布线将异常复杂，在这种情况下，主板作为一个调停者的身份出现，它将各个设备连接在一起而不需要每个设备之间直接交换数据，这样就减小了系统的耦合度和复杂度。1.6 开闭原则#​		软件实体应当对扩展开放，对修改关闭。（在理想的状态下，当我们需要为一个软件系统增加新功能时，只需要从原来的系统派生出一些新类就可以，不需要修改原来的任何一行代码。要做到开闭有两个要点：①抽象是关键，一个系统中如果没有抽象类或接口系统就没有扩展点；②封装可变性，将系统中的各种可变因素封装到一个继承结构中，如果多个可变因素混杂在一起，系统将变得复杂而换乱，如果不清楚如何封装可变性，可以参考《设计模式精解》一书中对桥梁模式的讲解的章节。）1.7 合成复用原则#​		优先使用聚合或合成关系复用代码。（通过继承来复用代码是面向对象程序设计中被滥用得最多的东西，因为所有的教科书都无一例外的对继承进行了鼓吹从而误导了初学者，类与类之间简单的说有三种关系，Is-A关系、Has-A关系、Use-A关系，分别代表继承、关联和依赖。其中，关联关系根据其关联的强度又可以进一步划分为关联、聚合和合成，但说白了都是Has-A关系，合成聚合复用原则想表达的是优先考虑Has-A关系而不是Is-A关系复用代码，原因嘛可以自己从百度上找到一万个理由，需要说明的是，即使在Java的API中也有不少滥用继承的例子，例如Properties类继承了Hashtable类，Stack类继承了Vector类，这些继承明显就是错误的，更好的做法是在Properties类中放置一个Hashtable类型的成员并且将其键和值都设置为字符串来存储数据，而Stack类的设计也应该是在Stack类中放一个Vector对象来存储数据。记住：任何时候都不要继承工具类，工具是可以拥有并可以使用的，而不是拿来继承的。）2、设计模式#​	设计模式（Design pattern）是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问，设计模式于己于他人于系统都是多赢的，设计模式使代码编制真正工程化，设计模式是软件工程的基石，如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题，每种模式在现在中都有相应的原理来与之对应，每一个模式描述了一个在我们周围不断重复发生的问题，以及该问题的核心解决方案，这也是它能被广泛应用的原因。​	模式：在某些场景下，针对某类问题的某种通用的解决方案。设计模式的三个分类​	创建型模式：对象实例化的模式，创建型模式用于解耦对象的实例化过程。​	结构型模式：把类或对象结合在一起形成一个更大的结构。​	行为型模式：类和对象如何交互，及划分责任和算法。设计模式2.1 单例模式:某个类只能有一个实例，提供一个全局的访问点#​	单例模式，它的定义就是确保某一个类只有一个实例，并且提供一个全局访问点。​	单例模式具备典型的3个特点：1、只有一个实例。 2、自我实例化。 3、提供全局访问点。​	单例模式的主要优点就是节约系统资源、提高了系统效率，同时也能够严格控制客户对它的访问。也许就是因为系统中只有一个实例，这样就导致了单例类的职责过重，违背了“单一职责原则”，同时也没有抽象类，所以扩展起来有一定的困难。​	单例模式的四种写法：1public class Single {2    //饿汉式 直接构建3    private static Single single = new Single();4    public static Single getInstance(){5        return single;6    }7
8    //懒汉式 不安全9    private static Single single = null;10    public static Single getInstance(){11        if(single==null){12            single = new Single();13        }14        return single;15    }16
17    //懒汉式 安全18    public static synchronized Single getInstance(){19        if(single==null){20            single = new Single();21        }22        return single;23    }24
25    //懒汉式 双重检验 锁26    public static Single getInstance(){27        if(single==null){28            synchronized (Single.class){29                if(single==null){30                    single = new Single();31                }32            }33        }34        return single;35    }36}显示更多显示更少2.2 简单工厂：一个工厂类根据传入的参量决定创建出那一种产品类的实例#​	作为抽象工厂模式的孪生兄弟，工厂方法模式定义了一个创建对象的接口，但由子类决定要实例化的类是哪一个，也就是说工厂方法模式让实例化推迟到子类。2.3 工厂方法：定义一个创建对象的接口，让子类决定实例化那个类#2.4 抽象工厂：创建相关或依赖对象的家族，而无需明确指定具体类#
常考算法#1、数组#1.1 二分查找#1/*2请实现有重复数字的升序数组的二分查找3给定一个 元素有序的（升序）整型数组 nums 和一个目标值 target  ，写一个函数搜索 nums 中的第一个出现的target，如果目标值存在返回下标，否则返回 -14*/5public int search (int[] nums, int target) {6    // write code here7    int left = 0, right = nums.length - 1;8    int idx = -1;9    while (left &amp;lt;= right) {10        int mid = (left + right) &amp;gt;&amp;gt; 1;11        if (nums[mid] == target) {12            idx = mid;13            right = mid - 1;14        } else if (nums[mid] &amp;gt; target) {15            right = mid - 1;16        } else {17            left = mid + 1;18        }19    }20    return idx;21}显示更多显示更少1.2 快排#1public class QuickSort {2    public static void main(String[] args) {3        int[] num = {1,3,4,6,7,8,9,5,2};4        quickSort(num,0,num.length-1);5        for (int i = 0; i &amp;lt; num.length; i++) {6            System.out.println(num[i]);7        }8    }9    public static void quickSort(int[] nums,int low,int high){10        if(low&amp;lt;high){11            int mid = getIndex(nums,low,high);12            quickSort(nums,low,mid-1);13            quickSort(nums,mid+1,high);14        }15    }16
17    //返回基准元素的位置18    public static int getIndex(int[] nums,int low,int high){19        int prev = nums[low];20        while(low&amp;lt;high){21            while(nums[high]&amp;gt;=prev &amp;amp;&amp;amp; high&amp;gt;low){22                high--;23            }24            nums[low] = nums[high];25            while(nums[low]&amp;lt;=prev &amp;amp;&amp;amp; low&amp;lt;high){26                low++;27            }28            nums[high] = nums[low];29        }30        nums[low] = prev;31        return low;32    }33}显示更多显示更少1.3 归并排序#1public class MergeSort {2    public static void main(String[] args) {3        MergeSort mergeSort = new MergeSort();4        int []arr = {9,8,7,6,5,4,3,2,1};5        mergeSort.sort(arr,0,arr.length-1);6        for(int num:arr){7            System.out.print(num+&quot; &quot;);8        }9    }10
11    public void sort(int[] arr,int left,int  right){12        if(left&amp;lt;right){13            int mid = (left+right)/2;14            sort(arr,left,mid);15            sort(arr,mid+1,right);16            merge(arr,left,mid,right);17        }18    }19
20    public void merge(int[] arr,int left,int mid,int right){21        int[] tmp = new int[right-left+1];22        int i = left;//左边数组的初始位置23        int j = mid+1;//右边数组的初始位置24        int t = 0;//临时数组的初始位置25        while(i&amp;lt;=mid &amp;amp;&amp;amp; j&amp;lt;=right){26            if(arr[i]&amp;lt;=arr[j]){27                tmp[t++] = arr[i++];28            }else{29                tmp[t++] = arr[j++];30            }31        }32        while(i&amp;lt;=mid){33            tmp[t++] = arr[i++];34        }35        while(j&amp;lt;=right){36            tmp[t++] = arr[j++];37        }38        t=0;39        while(left&amp;lt;=right){40            arr[left++] = tmp[t++];41        }42    }43}显示更多显示更少1.4 堆排序#1public class HeapSort {2
3}1.5 用双栈实现队列#1public class Solution {2    Stack&amp;lt;Integer&amp;gt; stack1 = new Stack&amp;lt;Integer&amp;gt;();3    Stack&amp;lt;Integer&amp;gt; stack2 = new Stack&amp;lt;Integer&amp;gt;();4
5    public void push(int node) {6        stack1.push(node);7    }8
9    public int pop() {10        if(stack2.isEmpty()){11            if(stack1.isEmpty()){12                throw new NullPointerException(&quot;队列空,无法取值&quot;);13            }14            while (!stack1.isEmpty()){15                stack2.push(stack1.pop());16            }17        }18        return stack2.pop();19    }20}显示更多显示更少1.6 最长无重复子数组#1/*2给定一个数组arr，返回arr的最长无重复元素子数组的长度，无重复指的是所有数字都不相同。3子数组是连续的，比如[1,3,5,7,9]的子数组有[1,3]，[3,5,7]等等，但是[1,3,7]不是子数组4思想：使用一个队列实现存储最长无重复元素子数组，一个一个元素添加，添加之前判断该元素是否已经存在，如果不存在，直接添加，如果存在，删除这个元素之前的所有元素，也就是循环删除直到队列中不含这个元素后再将这个元素添加进去。5*/6public class Solution {7    /**8     *9     * @param arr int整型一维数组 the array10     * @return int整型11     */12    public int maxLength (int[] arr) {13        // write code here14        if(arr==null ||arr.length==0){15            return 0;16        }17        Queue&amp;lt;Integer&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();18        int max = 0;19        for(int c:arr){20            while(queue.contains(c)){21                queue.poll();22            }23            queue.add(c);24            max = Math.max(max,queue.size());25        }26        return max;27    }28}显示更多显示更少1.7 合并两个有序数组(双指针)#1public class Solution {2    public void merge(int A[], int m, int B[], int n) {3        int n1 = m-1;4        int n2 = n-1;5        int index = m+n-1;6        while(n1&amp;gt;=0&amp;amp;&amp;amp;n2&amp;gt;=0){7            if(A[n1]&amp;gt;=B[n2]){8                A[index--] = A[n1--];9            }else{10                A[index--] = B[n2--];11            }12        }13        while(n2&amp;gt;=0){14            A[index--] = B[n2--];15        }16    }17}显示更多显示更少1.8 寻找第K大(快排)#1/*2有一个整数数组，请你根据快速排序的思路，找出数组中第K大的数。3给定一个整数数组a,同时给定它的大小n和要找的K(1&amp;lt;=K&amp;lt;=n)，请返回第K大的数(包括重复的元素，不用去重)，保证答案存在。4*/5public class Solution {6    public int findKth(int[] a, int n, int K) {7        // write code here8        //要找的元素的下标是k-1 快排逆序排9        return quickSort(a,0,n-1,K);10    }11    public int quickSort(int[] a,int start,int end,int k){12        int pre = a[start];13        int low = start;14        int high = end;15        while(low&amp;lt;high){16            while(a[high]&amp;lt;=pre &amp;amp;&amp;amp; low&amp;lt;high){17                high--;18            }19            a[low] = a[high];20            while(a[low]&amp;gt;=pre &amp;amp;&amp;amp; low&amp;lt;high){21                low++;22            }23            a[high] = a[low];24        }25        a[low]=pre;26        if(low==k-1){27            return a[low];28        }else if(low&amp;gt;k-1){29            return quickSort(a,start,low-1,k);30        }else{31            return quickSort(a,low+1,end,k);32        }33    }34}显示更多显示更少1.9 接雨水问题#1/*2给定一个整形数组arr，已知其中所有的值都是非负的，将这个数组看作一个柱子高度图，计算按此排列的柱子，下雨之后能接多少雨水。3输入：4[3,1,2,5,2,4]5返回值：56说明：7数组 [3,1,2,5,2,4] 表示柱子高度图，在这种情况下，可以接 5个单位的雨水，蓝色的为雨水 ：接的水[0,2,1,0,2]8*/9public class Solution {10    public long maxWater (int[] arr) {11        // write code here12        if(arr==null||arr.length&amp;lt;=2){13            return 0;14        }15        int left = 0;16        int right = arr.length-1;17        long res = 0L;18        int minSide = Math.min(arr[left],arr[right]);19        while(left&amp;lt;right){20            if(arr[left]&amp;lt;arr[right]){21                left++;22                if(arr[left]&amp;lt;minSide){23                    res+=minSide-arr[left];24                }else{25                    minSide = Math.min(arr[left],arr[right]);26                }27            }else{28                right--;29                if(arr[right]&amp;lt;minSide){30                    res+=minSide-arr[right];31                }else{32                    minSide = Math.min(arr[left],arr[right]);33                }34            }35        }36        return res;37    }38}显示更多显示更少1.10 数组中的逆序对（归并排序）#1/*2在数组中的两个数字，如果前面一个数字大于后面的数字，则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%10000000073*/4public class Solution {5    public int InversePairs(int [] array) {6        return (int)sort(array,0,array.length-1);7    }8
9    public long sort(int[] arr,int left,int  right){10        if(left&amp;lt;right){11            int mid = (left+right)/2;12            long l = sort(arr,left,mid);13            long r =sort(arr,mid+1,right);14            long count = merge(arr,left,mid,right);15            return (l+r+count)%1000000007;16        }17        return 0;18    }19
20    public long merge(int[] arr,int left,int mid,int right){21        int[] tmp = new int[right-left+1];22        int i = left;//左边数组的初始位置23        int j = mid+1;//右边数组的初始位置24        int t = 0;//临时数组的初始位置25        long count = 0;26        while(i&amp;lt;=mid &amp;amp;&amp;amp; j&amp;lt;=right){27            if(arr[i]&amp;lt;=arr[j]){28                tmp[t++] = arr[i++];29            }else{30                count+=mid-i+1;31                tmp[t++] = arr[j++];32            }33        }34        while(i&amp;lt;=mid){35            tmp[t++] = arr[i++];36        }37        while(j&amp;lt;=right){38            tmp[t++] = arr[j++];39        }40        t=0;41        while(left&amp;lt;=right){42            arr[left++] = tmp[t++];43        }44        return count;45    }46}显示更多显示更少1.11 数字在升序数组中出现的次数#1/*2使用二分法3*/4public class Solution {5    public int GetNumberOfK(int [] array , int k) {6        int left = 0;7        int right = array.length-1;8        int count = 0;9        while(left&amp;lt;right){10            int mid = (left+right)/2;11            if(k==array[mid]){12                right = mid;13            }else if(k&amp;lt;array[mid]){14                right = mid-1;15            }else{16                left = mid+1;17            }18        }19        while(left&amp;lt;array.length &amp;amp;&amp;amp; array[left]==k){20            count++;21            left++;22        }23        return count;24    }25}显示更多显示更少1.12 数组中只出现一次的两个数字#1public class Solution {2    public int[] FindNumsAppearOnce (int[] array) {3        // write code here4        int n = array.length;5        if(n&amp;lt;=1){6            return new int[0];7        }8        Map&amp;lt;Integer,Integer&amp;gt; map = new HashMap&amp;lt;&amp;gt;();9        for(int i=0;i&amp;lt;n;i++){10            int value = map.getOrDefault(array[i],0);11            value=value+1;12            map.put(array[i],value);13        }14        int[] res = new int[2];15        int count=0;16        for(int i=0;i&amp;lt;n;i++){17            if(map.get(array[i])==1){18                res[count++] = array[i];19            }20        }21        Arrays.sort(res);22        return res;23    }24}显示更多显示更少1.13 和为S的连续正数序列#1/*2输出所有和为S的连续正数序列。序列内按照从小至大的顺序，序列间按照开始数字从小到大的顺序3方法：使用滑动窗口4  初始化，i=1,j=1, 表示窗口大小为05  如果窗口中值的和小于目标值sum， 表示需要扩大窗口，j += 16  否则，如果狂口值和大于目标值sum，表示需要缩小窗口，i += 17  否则，等于目标值，存结果，缩小窗口，继续进行步骤2,3,48*/9public class Solution {10    public ArrayList&amp;lt;ArrayList&amp;lt;Integer&amp;gt; &amp;gt; FindContinuousSequence(int sum) {11        ArrayList&amp;lt;ArrayList&amp;lt;Integer&amp;gt; &amp;gt; res = new ArrayList&amp;lt;&amp;gt;();12        ArrayDeque&amp;lt;Integer&amp;gt; queue = new ArrayDeque&amp;lt;&amp;gt;();13        int cursum = 0;14        for(int i=1;i&amp;lt;=(sum+1)/2;i++){15            queue.add(i);16            cursum = cursum+i;17            if(cursum&amp;gt;sum){18                while(cursum&amp;gt;sum){19                    int value = queue.poll();20                    cursum = cursum - value;21                }22            }23            if(cursum==sum &amp;amp;&amp;amp; queue.size()&amp;gt;=2){24                res.add(new ArrayList(queue));25            }26        }27        return res;28    }29}显示更多显示更少1.14 扑克牌顺子#1public class Solution {2    public boolean IsContinuous(int [] numbers) {3        if(numbers==null||numbers.length==0) return false;4        int zeroCount = 0;5        int max = 0;6        int min = 14;7        Set&amp;lt;Integer&amp;gt; set = new HashSet&amp;lt;&amp;gt;();8        for(int c:numbers){9            if(c&amp;gt;0){10                if(set.contains(c)){11                    return false;12                }13                set.add(c);14                max = c&amp;gt;max? c:max;15                min = c&amp;lt;min? c:min;16            }17        }18        return max-min&amp;lt;5;19    }20}显示更多显示更少1.15 构建乘积数组#1//分两步计算乘积，先计算坐边元素乘积，再计算右边元素乘积2public class Solution {3    public int[] multiply(int[] A) {4        if(A==null||A.length&amp;lt;=1){5            return new int[0];6        }7        int[] res = new int[A.length];8        int mult = 1;9        res[0] = 1;10        for(int i=1;i&amp;lt;A.length;i++){11            mult = mult*A[i-1];12            res[i] = mult;13        }14        mult = 1;15        for(int i=A.length-2;i&amp;gt;=0;i--){16            mult = mult * A[i+1];17            res[i] = res[i] * mult;18        }19        return res;20    }21}显示更多显示更少1.16 最小的k个数(大根堆)(快排)#1/*2最小的k个数，使用大根堆实现，大根堆代表堆顶为最大的数，我们一个数一个数的往堆里添加，如果堆满了，再添加就要判断当前数与堆顶的大小，如果比堆顶大，证明这个数肯定不是最小的k个数，因为堆里已经有k个数了，如果当前数比堆顶小，当前数可能是最小的k个数之一，则堆顶出堆，当前数入堆。3*/4public class Solution {5    public ArrayList&amp;lt;Integer&amp;gt; GetLeastNumbers_Solution(int [] input, int k) {6        int n = input.length;7        ArrayList&amp;lt;Integer&amp;gt; res = new ArrayList&amp;lt;&amp;gt;();8        if(n==0||k==0)9            return res;10        Queue&amp;lt;Integer&amp;gt; queue = new PriorityQueue&amp;lt;&amp;gt;(k,(a,b)-&amp;gt;(b-a));11        for(int i=0;i&amp;lt;n;i++){12            if(queue.size()&amp;lt;k){13                queue.offer(input[i]);14            }else{15                if(input[i]&amp;lt;queue.peek()){16                    queue.poll();17                    queue.offer(input[i]);18                }19            }20        }21        res.addAll(queue);22        return res;23    }24}显示更多显示更少2、链表#1class ListNode{2    int val;3    ListNode next;4    public ListNode(){5    }6    public ListNode(int val){7        this.val = val;8    }9    public ListNode(int val,ListNode next){10        this.val = val;11        this.next = next;12    }13}2.1 反转链表#1//首先保存当前节点的下一个节点，然后将当前节点的next指向前一个节点2//初始状态下，前一个节点为null，当前节点为首节点3public class Solution {4    public ListNode ReverseList(ListNode head) {5        ListNode p = null;6        ListNode q = null;7        while(head!=null){8            q = head.next;9            head.next=p;10            p = head;11            head = q;12        }13        return p;14    }15}显示更多显示更少2.2 判断链表是否有环(双指针)#1//使用双指针 快慢指针，如果没环 快指针先到链表尾部，为null，如果有环，两个指针会在环中的某个节点相遇。2public class Solution {3    public boolean hasCycle(ListNode head) {4        if(head==null||head.next==null){5            return false;6        }7        ListNode low = head;8        ListNode fast = head.next;9        while(low!=fast){10            if(fast==null||fast.next==null){11                return false;12            }13            low = low.next;14            fast = fast.next.next;15        }16        return true;17    }18}显示更多显示更少2.3 相交链表(双指针)#1/* 双指针2情况一：两个链表相交3
4链表 headA 和headB 的长度分别是 m 和 n。假设链表 headA 的不相交部分有 a 个节点，链表headB 的不相交部分有 b 个节点，两个链表相交的部分有 c 个节点，则有 a+c=m，b+c=n。5如果 a=b，则两个指针会同时到达两个链表相交的节点，此时返回相交的节点；6如果 a!=b，则指针 pA 会遍历完链表headA，指针pB 会遍历完链表 headB，两个指针不会同时到达链表的尾节点，然后指针pA 移到链表 headB 的头节点，指针pB 移到链表 headA 的头节点，然后两个指针继续移动，在指针 pA 移动了 a+c+b 次、指针pB 移动了b+c+a 次之后，两个指针会同时到达两个链表相交的节点，该节点也是两个指针第一次同时指向的节点，此时返回相交的节点。7
8情况二：两个链表不相交9链表headA 和headB 的长度分别是 m 和 n。考虑当 m=n 和 m!=n 时，两个指针分别会如何移动：10如果 m=n，则两个指针会同时到达两个链表的尾节点，然后同时变成空值null，此时返回null；11如果 m!=n，则由于两个链表没有公共节点，两个指针也不会同时到达两个链表的尾节点，因此两个指针都会遍历完两个链表，在指针 pA 移动了m+n 次、指针 pB 移动了n+m 次之后，两个指针会同时变成空值null，此时返回 null。12*/13public class Solution {14    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {15        if (headA == null || headB == null) {16            return null;17        }18        ListNode pA = headA, pB = headB;19        while (pA != pB) {20            pA = pA == null ? headB : pA.next;21            pB = pB == null ? headA : pB.next;22        }23        return pA;24    }25}显示更多显示更少2.4 设计LRU缓存结构#1public class LRUCache{2    private int capacity;// 缓存区大小3    private LinkedList&amp;lt;Integer&amp;gt; buffer;// 存放最近操作key的集合4    private Map&amp;lt;Integer,Integer&amp;gt; cache;// 存放缓存的集合5
6    public LRUCache(int capacity)7    {8        this.capacity=capacity;9        buffer=new LinkedList&amp;lt;Integer&amp;gt;();10        cache=new HashMap&amp;lt;Integer,Integer&amp;gt;();11    }12
13    public int get(int key)14    {15        int value=cache.getOrDefault(key,-1);16        /* 更新最近操作的集合（以下相同）*/17        if(value!=-1){18            updateBuffer(key);19        }20        return value;21    }22
23    public void set(int key, int value)24    {25        if(cache.containsKey(key))// 如果msg中存在了key，直接更新value，不进行LRU策略26        {27            cache.put(key,value);28            updateBuffer(key);29        }30        else// key第一次出现，视情况进行LRU策略31        {32            if(cache.size()&amp;lt;capacity)// 容量足够，不进行LRU策略33            {34                cache.put(key,value);35                updateBuffer(key);36            }37            else// 缓存区满,进行LRU策略38            {39                int replace = buffer.removeLast();40                cache.remove(replace);41                cache.put(key,value);42                updateBuffer(key);43            }44        }45    }46    public void updateBuffer(int key)// 封装重复的更新最近访问缓冲区方法47    {48        if(buffer.contains(key))49        {50            buffer.remove(Integer.valueOf(key));51            buffer.addFirst(key);52        }53        else54        {55            buffer.addFirst(key);56        }57    }58}显示更多显示更少2.5 两个链表的第一个公共结点（蔚来一面）#1/*2使用双指针判断，如果有相交结点会在相交结点相遇，如果没有相交结点，最后p1==p2==null结束3*/4public class Solution {5    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {6        if(pHead1==null||pHead2==null){7            return null;8        }9        ListNode p1 = pHead1;10        ListNode p2 = pHead2;11        while(p1!=p2){12            p1=p1==null? pHead2:p1.next;13            p2=p2==null? pHead1:p2.next;14        }15        return p1;16    }17}显示更多显示更少2.6 圆圈中最后剩下的数-约瑟夫环#1class Solution {2    public int lastRemaining(int n, int m) {3        return f(n, m);4    }5    public int f(int n, int m) {6        if (n == 1) {7            return 0;8        }9        int x = f(n - 1, m);10        return (m + x) % n;11    }12}13
14
15public class Solution {//超时16    class ListNode{17        int val;18        ListNode next;19        ListNode(){}20        ListNode(int val){21            this.val = val;22        }23    }24    public int LastRemaining_Solution(int n, int m) {25        if(n==0) return -1;26        ListNode head = new ListNode(0);27        ListNode p = head;28        for(int i=1;i&amp;lt;n;i++){//创建n个小朋友手拉手的循环链表-圈29            ListNode node = new ListNode(i);30            p.next = node;31            p = node;32        }33        p.next = head;34        int count = 0;35        while(head.next!=head){//每数到m-1则出圈一人，就从链表删除该节点，当最后链表只剩一个节点即为最后剩下的人36            p = head;37            head = head.next;38            count++;39            if(count==m-1){40                p.next = head.next;41                head = p.next;42                count = 0;43            }44        }45        return head.val;46    }47}显示更多显示更少3、字符串#3.1 反转字符串#1public class Solution {2    /**3     * 反转字符串4     */5    public String solve (String str) {6        // write code here7        char[] chars = str.toCharArray();8        StringBuilder sb = new StringBuilder();9        for(int i=chars.length-1;i&amp;gt;=0;i--){10            sb.append(chars[i]);11        }12        return new String(sb);13    }14}3.2 大数加法#1public class Solution {2    public String solve (String s, String t) {3        // write code here4        if(s==null&amp;amp;&amp;amp;t==null){5            return null;6        }else if(s==null){7            return t;8        }else if(t==null){9            return s;10        }11        int ls = s.length();12        int lt = t.length();13        String s1 = reverse(s);14        String t1 = reverse(t);15        int maxLen = ls&amp;gt;lt ? ls:lt;16        if(ls &amp;gt; lt){17            for(int i = lt; i &amp;lt; ls; i++){18                t1 += &quot;0&quot;;19            }20        }else{21            for(int i = ls; i &amp;lt; lt; i++){22                s1 += &quot;0&quot;;23            }24        }25        StringBuffer res = new StringBuffer(); //存放返回结果26        int ans = 0;  //进位27
28        for(int i = 0; i &amp;lt; maxLen; i++){29            int sum = Integer.parseInt(s1.charAt(i)+&quot;&quot;) + Integer.parseInt(t1.charAt(i)+&quot;&quot;)+ans;30            res.append(sum%10);31            ans = sum/10;  //求出进位数32        }33        if(ans &amp;gt; 0){34            res.append(ans);35        }36        return reverse(new String(res));37    }38
39    public String reverse(String str){40        char[] chars = str.toCharArray();41        int l = 0, r = chars.length-1;42        while(l &amp;lt; r){43            char c = chars[l];44            chars[l] = chars[r];45            chars[r] = c;46            l++;47            r--;48        }49        return new String(chars);50    }51}显示更多显示更少3.3 左旋转字符串#1/*2汇编语言中有一种移位指令叫做循环左移（ROL），现在有个简单的任务，就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列 S，请你把其循环左移 K 位后的序列输出（保证 K 小于等于 S 的长度）。例如，字符序列S=”abcXYZdef”,要求输出循环左移 3 位后的结果，即“XYZdefabc”。是不是很简单？OK，搞定它！3*/4public class Solution {5    public String LeftRotateString(String str,int n) {6        if(str==null||str.length()==0){7            return &quot;&quot;;8        }9        StringBuilder s = new StringBuilder();10        for(int i=n;i&amp;lt;str.length();i++){11            s.append(str.charAt(i));12        }13        for(int i=0;i&amp;lt;n;i++){14            s.append(str.charAt(i));15        }16        return new String(s);17    }18}显示更多显示更少3.4 翻转单词序列#1/*2输入：&quot;nowcoder. a am I&quot;3返回值：&quot;I am a nowcoder.&quot;4*/5public class Solution {6    public String ReverseSentence(String str) {7        if(str==null||str.length()==0){8            return str;9        }10        String[] strs = str.split(&quot; &quot;);11        StringBuilder s = new StringBuilder();12        for(int i=strs.length-1;i&amp;gt;=0;i--){13            s.append(strs[i]+&quot; &quot;);14        }15        s.deleteCharAt(str.length());16        return new String(s);17    }18}显示更多显示更少3.5 把字符串转换成整数#1public class Solution {2    public int StrToInt(String str) {3        if(str==null||str.length()==0){4            return 0;5        }6        str = str.trim();//删除首尾空格7        boolean flag = true;8        char[] chars = str.toCharArray();9        int res = 0;10        if(chars[0]==&apos;+&apos;){11            flag = true;12        }else if(chars[0]==&apos;-&apos;){13            flag = false;14        }else if(chars[0]&amp;gt;=&apos;0&apos;&amp;amp;&amp;amp;chars[0]&amp;lt;=&apos;9&apos;){15            res = chars[0]-&apos;0&apos;;16        }else{17            return 0;18        }19        for(int i=1;i&amp;lt;chars.length;i++){20            if(chars[i]&amp;gt;=&apos;0&apos;&amp;amp;&amp;amp;chars[i]&amp;lt;=&apos;9&apos;){21                res = res*10+(chars[i]-&apos;0&apos;);22            }else{23                return 0;24            }25        }26        return flag? res:-res;27    }28}显示更多显示更少4、二叉树#1class TreeNode{2    int val;3    TreeNode left;4    TreeNode right;5    public TreeNode(){}6    public TreeNode(int val){7        this.val = val;8    }9    public TreeNode(int val,TreeNode left,TreeNode right){10        this.val = val;11        this.left = left;12        this.right = right;13    }14}4.1 二叉树的层序遍历#1//使用LinkedList存储TreeNode 使用LinkedList的队列属性，先进先出2//每一层的节点数使用一个变量levalCount来计算3public ArrayList&amp;lt;ArrayList&amp;lt;Integer&amp;gt;&amp;gt; levelOrder (TreeNode root) {4    // write code here5    ArrayList&amp;lt;ArrayList&amp;lt;Integer&amp;gt;&amp;gt; res = new ArrayList&amp;lt;&amp;gt;();6    if(root==null){7        return res;8    }9    LinkedList&amp;lt;TreeNode&amp;gt; list = new LinkedList&amp;lt;&amp;gt;();10    list.addLast(root);11    int levalCount = 1;12    while(!list.isEmpty()){13        ArrayList&amp;lt;Integer&amp;gt; ans = new ArrayList&amp;lt;&amp;gt;();14        int count=0;15        for(int i =0;i&amp;lt;levalCount;i++){16            TreeNode node = list.removeFirst();17            ans.add(node.val);18            if(node.left!=null){19                list.addLast(node.left);20                count++;21            }22            if(node.right!=null){23                list.addLast(node.right);24                count++;25            }26        }27        res.add(ans);28        levalCount = count;29    }30    return res;31}显示更多显示更少4.2  在二叉树中找到两个节点的最近公共祖先#1public class Solution {2    /**3     *4     * @param root TreeNode类5     * @param o1 int整型6     * @param o2 int整型7     * @return int整型8     */9    public int lowestCommonAncestor (TreeNode root, int o1, int o2) {10        // write code here11        return postOrder(root,o1,o2).val;12    }13
14    public TreeNode postOrder(TreeNode node, int o1, int o2){15        if(node==null||node.val==o1||node.val==o2){16            // 超过叶子节点，或者root为p、q中的一个直接返回17            return node;18        }19        TreeNode left = postOrder(node.left,o1,o2);20        TreeNode right = postOrder(node.right,o1,o2);21        if(left==null){// 都在右侧22            return right;23        }24        if(right==null){// 都在左侧25            return left;26        }27        return node;28    }29}显示更多显示更少4.3 实现二叉树先序，中序和后序遍历#1public class Solution {2    /**3     *4     * @param root TreeNode类 the root of binary tree5     * @return int整型二维数组6     */7    public int[][] threeOrders (TreeNode root) {8        // write code here9        List&amp;lt;Integer&amp;gt; pre = new ArrayList&amp;lt;&amp;gt;();10        List&amp;lt;Integer&amp;gt; in = new ArrayList&amp;lt;&amp;gt;();11        List&amp;lt;Integer&amp;gt; post = new ArrayList&amp;lt;&amp;gt;();12        pre = preOrder(root,pre);13        in = inOrder(root,in);14        post = postOrder(root,post);15        int n = pre.size();16        int[][] res = new int[3][n];17        for(int i=0;i&amp;lt;n;i++){18            res[0][i] = pre.get(i);19            res[1][i] = in.get(i);20            res[2][i] = post.get(i);21        }22        return  res;23    }24    public List&amp;lt;Integer&amp;gt; preOrder(TreeNode node,List&amp;lt;Integer&amp;gt; list){25        if(node==null){26            return list;27        }28        list.add(node.val);29        preOrder(node.left,list);30        preOrder(node.right,list);31        return list;32    }33    public List&amp;lt;Integer&amp;gt; inOrder(TreeNode node,List&amp;lt;Integer&amp;gt; list){34        if(node==null){35            return list;36        }37        inOrder(node.left,list);38        list.add(node.val);39        inOrder(node.right,list);40        return list;41    }42    public List&amp;lt;Integer&amp;gt; postOrder(TreeNode node,List&amp;lt;Integer&amp;gt; list){43        if(node==null){44            return list;45        }46        postOrder(node.left,list);47        postOrder(node.right,list);48        list.add(node.val);49        return list;50    }51}显示更多显示更少4.4 求一颗树的最大哈夫曼树子树#1int res = 0;2public int maxHuffman(TreeNode node){3    if(root==null){4        return 0;5    }6    if(isHuffman(root)){7        res = Math.max(res,getCount(root));8    }else{9        maxHuffman(root.left);10      maxHuffman(root.right);11    }12    return res;13}14public int getCount(TreeNode node){15    if(node==null){16        return 0;17    }18    return getCount(node.left)+getCount(node.right)+1;19}20public boolean isHuffman(TreeNode node){21    if(node==null||(node.left==null&amp;amp;&amp;amp;node.right==null)){22        return true;23    }else if(node.left==null||node.right==null){24        return false;25    }26    if(node.val!=node.left.val+node.right.val){27        return false;28    }29    return isHuffman(node.left)&amp;amp;&amp;amp;isHuffman(node.rig;ht)30}显示更多显示更少5、动态规划#5.1 跳台阶#1/*2一只青蛙一次可以跳上1级台阶，也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法（先后次序不同算不同的结果）。3*/4public class Solution {5    public int jumpFloor(int target) {6        if(target&amp;lt;=2) return target;7        int[] dp = new int[target+1];8        dp[1] = 1;9        dp[2] = 2;10        for(int i=3;i&amp;lt;=target;i++){11            dp[i] = dp[i-1]+dp[i-2];//斐波那契数 当前数=前一个+前前一个12        }13        return dp[target];14    }15}显示更多显示更少扩展：⼀步⼀个台阶，两个台阶，三个台阶，直到 m个台阶，有多少种⽅法爬到n阶楼顶1public class Main{2    public int jumpFloor(int n，int m){3        int[] dp = new int[n+1];4        dp[0] = 1;5        for(int i=1;i&amp;lt;=n;i++){//求排列：总共需要走n阶6            for(int j=1;j&amp;lt;=m;j++){ //物品放在内层 在这里就是台阶 每次可以走 1...m7                if(i-j&amp;gt;=0){8                    dp[i]+=dp[i-j];9                }10            }11        }12        return dp[n];13    }14}使用最小花费爬楼梯：1public class Main{2    public int jumpFloor(int[] cost){3        int n = cost.length;4        int[] dp = new int[n];5        dp[0] = cost[0];6        dp[1] = cost[1];7        for(int i=2;i&amp;lt;=n;i++){8            dp[i] = Math.min(dp[i-1]+cost[i],dp[i-2]+cost[i]);9        }10        return Math.min(dp[n-1],dp[n-2]);11    }12}5.2 子数组的最大累加和问题#1/*2给定一个数组arr，返回子数组的最大累加和3例如，arr = [1, -2, 3, 5, -2, 6, -1]，所有子数组中，[3, 5, -2, 6]可以累加出最大的和12，所以返回12.4题目保证没有全为负数的数据5
6*/7public class Solution {8    public int maxsumofSubarray (int[] arr) {9        // write code here10        int pre =0;11        int max = arr[0];12        for(int c:arr){13            pre = Math.max(pre+c,c);14            max = Math.max(max,pre);15        }16        return max;17    }18}显示更多显示更少5.3 最长回文子串#1public class Solution {2    public int getLongestPalindrome(String A, int n) {3        // write code here4        if(n==0){5            return 0;6        }7        if(n==1){8            return 1;9        }10        char[] chars = A.toCharArray();11        boolean[][] dp = new boolean[n][n];12        for(int i=0;i&amp;lt;n;i++){13            dp[i][i] = true;14        }15        int max=1;16        for(int j=1;j&amp;lt;n;j++){//i在左边，j在右边  从i到j17            for(int i=0;i&amp;lt;j;i++){18                if(chars[i]==chars[j]){19                    if(j-i&amp;lt;3){20                        dp[i][j]=true;21                        max = Math.max(max,j-i+1);22                    }else{23                        dp[i][j] = dp[i+1][j-1];24                        if(dp[i][j]){25                            max = Math.max(max,j-i+1);26                        }27                    }28                }else{29                    dp[i][j] = false;30                }31            }32        }33        return max;34    }35}显示更多显示更少5.4 丑数#1/*2把只包含质因子2、3和5的数称作丑数（Ugly Number）。例如6、8都是丑数，但14不是，因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。3解法：使用三指针+动态规划，将2,3,5的倍数根据大小依次加入如：2,3,4,5,6,8,9,10.....4*/5public class Solution {6    public int GetUglyNumber_Solution(int index) {7        if(index==0) return 0;8        int[] dp = new int[index+1];9        dp[1] = 1;10        int p2 = 1;11        int p3 = 1;12        int p5 = 1;13        for(int i=2;i&amp;lt;=index;i++){14            int num2 = dp[p2]*2;15            int num3 = dp[p3]*3;16            int num5 = dp[p5]*5;17            dp[i] = Math.min(Math.min(num2,num3),num5);18            if(dp[i]==num2){19                p2++;20            }21            if(dp[i]==num3){22                p3++;23            }24            if(dp[i]==num5){25                p5++;26            }27        }28        return dp[index];29    }30}显示更多显示更少5.5 0-1背包#1/*2给定N件物品和一个容量为V的背包。放入第i件物品耗费的空间为C[i] ，得到的价值是 W[i] 。3问：哪些物品装入背包可使价值总和最大？最大是多少？4每种物品只有一个选择放或不放，声明一个二维数组F[N+1,V+1] ，F[i][v] 表示前i件物品恰放入一个容量恰为v的背包可以获得的最大价值。5当 v &amp;lt; W[i] 时， 说明背包容量不足以放下第i件物品，只能选择不拿，此时： F[i][v] = F[i-1][v]6当 v &amp;gt;= W[i] 时，这是背包容量可以放下第i件物品，可以选择拿还是不拿7  拿： F[i][v] = F[i-1][v-C[i]]+W[i]；8  不拿： F[i][v] = F[i-1][v]。9*/10class Main{11    //V是物体所占空间或者重量 W是物体的价值12    public static int back0_1(int V,int[] weight,int[] value){13        int n = weight.length;14        int[][] dp = new int[n+1][V+1];15        for(int j=V;j&amp;gt;=weight[0];j--){16            dp[0][j] = dp[0][j-weight[0]]+value[0];17        }18        for(int i=1;i&amp;lt;n;i++){19            for(int j=0;j&amp;lt;=V;j++){20                if(j&amp;gt;=weight[i]){21                    dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);22                }23            }24        }25        return dp[n-1][V];26    }27    //优化空间复杂度 使用滚动数组28    public static int back0_1(int V,int[] weight,int[] value){29        int n = weight.length;30        int[] dp = new int[V+1];31        for(int i=1;i&amp;lt;n;i++){32            for(int j=V;j&amp;gt;=weight[i];j--){//倒序保证每件物品只取一遍33                dp[j] = Math.max(dp[j],dp[j-weight[i]]+value[i]);34            }35        }36        return dp[V];37    }38}显示更多显示更少5.6 完全背包问题（物品数量无限）#1/*2背包空间的循环与0-1背包的区别是：在这里是正序3举⼀个例⼦：物品0的重量weight[0] = 1，价值value[0] = 154  如果正序遍历5    dp[1] = dp[1 - weight[0]] + value[0] = 156    dp[2] = dp[2 - weight[0]] + value[0] = 307  此时dp[2]就已经是30了，意味着物品0，被放⼊了两次，所以不能正序遍历。8  为什么倒叙遍历，就可以保证物品只放⼊⼀次呢？9  倒叙就是先算dp[2]10    dp[2] = dp[2 - weight[0]] + value[0] = 15 （dp数组已经都初始化为0）11    dp[1] = dp[1 - weight[0]] + value[0] = 1512  所以从后往前循环，每次取得状态不会和之前取得状态重合，这样每种物品就只取⼀次了。13*/14public class BackMuti {15    /**16     * 滚动数组解决完全背包问题17     */18    public static int back(int V,int[] weight,int[] value){//滚动数组19        int n = weight.length;20        //dp[i][j] 表示从下标为[0-i]的物品⾥任意取，放进容量为j的背包，价值总和最⼤是多少21        int[] dp = new int[V+1];22        for(int i=0;i&amp;lt;n;i++){23            for(int j=weight[i];j&amp;lt;=V;j++){//正序 每件无序无数量限制 可以取多件24                dp[j] = Math.max(dp[j],dp[j-weight[i]]+value[i]);25            }26        }27        return dp[V];28    }29}显示更多显示更少如果求组合数就是外层for循环遍历物品，内层for遍历背包。（含有元素相同就相同）
如果求排列数就是外层for遍历背包，内层for循环遍历物品。（不同的顺序算不同）1/**2     * 给定⼀个由正整数组成且不存在重复数字的数组，找出和为给定⽬标正整数的组合的个数3     * @param nums 正整数组成且不存在重复数字的数组4     * @param target 目标数组5     * @return 组合的个数6     */7    public int combinationSum4(int[] nums,int target){8        int n=  nums.length;9        int[] dp = new int[target+1];//dp[i]: 凑成⽬标正整数为i的排列个数为dp[i]10        dp[0] = 1;11        for(int i=0;i&amp;lt;=target;i++){12            for(int j=0;j&amp;lt;n;j++){13                if(i-nums[j]&amp;gt;=0){14                    dp[i] +=dp[i-nums[j]];15                }16            }17        }18        return dp[target];19    }显示更多显示更少5.7 多重背包问题#5.8 不同路径#1//深度搜索2public class Main{3    public int uniquePath(int m,int n){4        return dfs(1,1,m,n);5    }6    public int dfs(int i,int j,int m,int n){7        if(i&amp;gt;m||j&amp;gt;n){// 越界了8            return 0;9        }10        if(i==m &amp;amp;&amp;amp; j==n){// 找到⼀种⽅法，相当于找到了叶⼦节点11            return 1;12        }13        return dfs(i+1,j,m,n)+dfs(i,j+1,m,n);14    }15}16//动态规划17public class Main{18    public int uniquePath(int m,int n){19        int[][] dp = new int[m][n];20        for(int i=0;i&amp;lt;m;i++){//初始化第一列21            dp[i][0] = 1;22        }23        for(int j=0;j&amp;lt;n;j++){//初始化第一行24            dp[0][j] = 1;25        }26        for(int i=1;i&amp;lt;m;i++){27            for(int j=1;j&amp;lt;n;j++){28                dp[i][j] = dp[i-1][j]+dp[i][j-1];29            }30        }31        return dp[m-1][n-1];32    }33}34//数学  组合问题 给你m + n - 2个不同的数，随便取m - 1个数，有⼏种取法。35public class Main{36    public int uniquePath(int m,int n){37        long numerator = 1; //分子38        int denominator = m - 1; //分母39        int count = m-1;40        int t = m+n-2;41        while(count--){ // 计算分⼦，此时分⼦就会溢出42            numerator *= (t--);43       while (denominator != 0 &amp;amp;&amp;amp; numerator % denominator == 0) {44         numerator /= denominator;45         denominator--;46       }47     }48        return numerator;49    }50}显示更多显示更少扩展：路径中有障碍物用1表示障碍物1public class Solution {2    public int uniquePathsWithObstacles(int[][] obstacleGrid) {3        int m = obstacleGrid.length;4        int n = obstacleGrid[0].length;5        int[][] dp = new int[m][n];6        for(int i=0;i&amp;lt;m;i++){7            if(obstacleGrid[i][0]==1){8                break;9            }10            dp[i][0] = 1;11        }12        for(int j=0;j&amp;lt;n;j++){13            if(obstacleGrid[0][j]==1){14                break;15            }16            dp[0][j] = 1;17        }18        for(int i=1;i&amp;lt;m;i++){19            for(int j=1;j&amp;lt;n;j++){20                if(obstacleGrid[i][j]==1){21                    continue;22                }23                dp[i][j] = dp[i-1][j]+dp[i][j-1];24            }25        }26        return dp[m-1][n-1];27    }28}显示更多显示更少5.9 整数拆分#1public class Main{2    public int integerBreak(int n){3        int[] dp = new int[n+1];//数字i，可以得到的最⼤乘积为dp[i]。4        dp[2] = 1;//2可以拆分成1*1（0和1不可拆分，所以初始化2）5        for(int i=3;i&amp;lt;=n;i++){6            for(int j=1;j&amp;lt;i-1;j++){7                dp[i] = Math.max(j*dp[i-j],j*(i-j));8            }9        }10        return dp[n];11    }12}6、数学#6.1 求1+2+3+…+n#1/*2求1+2+3+...+n，要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句（A?B:C）3*/4public class Solution {5    int res = 0;6    public int Sum_Solution(int n) {7        boolean x = n &amp;gt; 1 &amp;amp;&amp;amp; Sum_Solution(n - 1) &amp;gt; 0;8        res += n;9        return res;10
11    }12}6.2 不用加减乘除做加法#1public class Solution {2    public int Add(int num1,int num2) {3        while(num2 != 0) { // 当进位为 0 时跳出4            int c = (num1 &amp;amp; num2) &amp;lt;&amp;lt; 1;  // c = 进位5            num1 ^= num2; // a = 非进位和6            num2 = c;7        }8        return num1;9    }10}6.3 位乘#1/*2给一个9进制的字符串例如“80”，求每一位+1之后相乘的结果，最后结果是一位，如果不存在那么返回-13“80”=(8+1)*(0+1)=(9)-&amp;gt;(10)=(1+1)*(0+1)=24*/5public int mulDigits(String num){6    if(num==null||num.length()==0){7        return -1;8    }9    int n = num.length();10    Stack&amp;lt;Integer&amp;gt; stack = new Stack&amp;lt;&amp;gt;();11    //每一位入栈12    for(int i=0;i&amp;lt;n;i++){13        stack.push(num.charAt(i)-&apos;0&apos;+1);14    }15    int pre = -1;16    while(!stack.isEmpty()){17        int val = stack.pop();18        if(val&amp;gt;=9){19            stack.push(val/9+1);20            stack.push(val%9+1);21        }else{22            if(pre==-1){23                pre = val;24            }else{25                int mutl = pre * val;26                if(mutl&amp;gt;=9 &amp;amp;&amp;amp; mutl==(mutl/9+1)*(mutl%9+1)){27                    return -1;28                }29                stack.push(mutl);30                pre = -1;31            }32        }33    }34    return pre;35}36//通过率40%显示更多显示更少6.4 小强去春游#1/**2 * 阿里巴巴 2021 校招笔试真题--小强去春游3 */4public class TreeMinLink {5    public static void main(String[] args) {6        Scanner scanner = new Scanner(System.in);7        int t = scanner.nextInt();8        while((t--)&amp;gt;=0){9            int n = scanner.nextInt();10            int[] weight = new int[n];11            for (int i = 0; i &amp;lt; n; i++) {12                weight[i] = scanner.nextInt();13            }14            Arrays.sort(weight);15            long res = 0;16            while(n&amp;gt;=4){17                //当人数大于4时，过河时先将最重的两个渡过去，此时有两种思路，18                // 一种是最轻的人走两次，每次带一个19                //另一种是最轻和次轻先过去最轻回来，最重和次重过去，次轻回来。20                res+=Math.min(weight[0]*2+weight[n-1]+weight[n-2],21                        weight[0]+2*weight[1]+weight[n-1]);22                n-=2;23            }24            if(n==3){//三个人就是最轻的和最重的先过去，最轻的回来，然后和次轻的一起过去25                res+=weight[0]+weight[1]+weight[2];26            } else if(n==2){27                res+=weight[1];28            }else if(n==1){29                res+=weight[0];30            }31            System.out.println(res);32        }33    }34}显示更多显示更少7、回溯#7.1 组合总和#1public static void main(String[] args) {2        Scanner scanner = new Scanner(System.in);3        int totalMoney = scanner.nextInt();4        int number = scanner.nextInt();5        int[] price = new int[number];6        for(int i=0;i&amp;lt;number;i++){7            price[i] = scanner.nextInt();8        }9        Arrays.sort(price);10        List&amp;lt;Integer&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();11        List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; res = new ArrayList&amp;lt;&amp;gt;();12        backtrack(price,totalMoney,0,0,list,res);13        if(res.size()==0){14            System.out.println(-1);15        }else{16            System.out.println(res.size());17            System.out.println(res);18        }19    }20  //回溯法21    public static void backtrack(int[] price, int totalMoney, int curMoney, int index,22                                 List&amp;lt;Integer&amp;gt; list,List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; res){23        if(curMoney==totalMoney){24            res.add(new ArrayList&amp;lt;&amp;gt;(list));25            return;26        }27        for(int i=index;i&amp;lt;price.length;i++){28            if(i &amp;gt; index &amp;amp;&amp;amp; price[i] == price[i-1]) continue;//如果有重复元素加上这句29            if(price[i] + curMoney &amp;lt;= totalMoney){30                list.add(price[i]);31                backtrack(price,totalMoney,curMoney+price[i],i+1,list,res);32                list.remove(list.size()-1);33            }else{34                break;35            }36        }37    }显示更多显示更少7.2 全排列#1/*2给定一个不含重复数字的数组 nums ，返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。3*/4class Solution {5    LinkedList&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; res = new LinkedList&amp;lt;&amp;gt;();6    List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; permute(int[] nums) {7        LinkedList&amp;lt;Integer&amp;gt; track = new LinkedList&amp;lt;&amp;gt;();8        backtrack(nums,track);9        return res;10    }11
12    void backtrack(int[] nums, LinkedList&amp;lt;Integer&amp;gt; track){13        if(track.size()==nums.length){14            res.add(new LinkedList(track));15            return;16        }17        for(int i = 0;i&amp;lt;nums.length;i++){18            if(track.contains(nums[i])){19                continue;20            }21            track.add(nums[i]);22            backtrack(nums,track);23            track.removeLast();24        }25    }26}显示更多显示更少1/*2给定一个含重复数字的数组 nums ，返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。3*/4class Solution {5    boolean[] vis;6
7    public List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; permuteUnique(int[] nums) {8        List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; res = new ArrayList&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt;();9        List&amp;lt;Integer&amp;gt; per = new ArrayList&amp;lt;Integer&amp;gt;();10        vis = new boolean[nums.length];11        Arrays.sort(nums);12        backtrack(nums, res, 0, per);13        return res;14    }15
16    public void backtrack(int[] nums, List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; res, int index, List&amp;lt;Integer&amp;gt; per) {17        if (index == nums.length) {18            res.add(new ArrayList&amp;lt;Integer&amp;gt;(per));19            return;20        }21        for (int i = 0; i &amp;lt; nums.length; ++i) {22            if (vis[i] || (i &amp;gt; 0 &amp;amp;&amp;amp; nums[i] == nums[i - 1] &amp;amp;&amp;amp; !vis[i - 1])) {23                continue;24            }25            per.add(nums[i]);26            vis[i] = true;27            backtrack(nums, ans, index + 1, per);28            vis[i] = false;29            per.remove(index);30        }31    }32}显示更多显示更少7.3 划分为k个相等的子集#1class Solution {2    public boolean canPartitionKSubsets(int[] nums, int k) {3        int sum=0;4        boolean[] used=new boolean[nums.length];5        Arrays.sort(nums);6        for(int i=0;i&amp;lt;nums.length;i++)7        {8            sum+=nums[i];9        }10        if(sum%k!=0)11            return false;12        int target=sum/k;13        if(nums[nums.length-1]&amp;gt;target)14            return false;15        return dfs(nums,nums.length-1,target,0,k,used);16    }17
18    public static boolean dfs(int[] nums,int begin,int target,int curSum,int k,boolean[] used)19    {20        //剪枝121        if(k==1)22            return true;23        if(curSum==target)24            return dfs(nums,nums.length-1,target,0,k-1,used);//找到了一个组合,还有k-1个.25        //剪枝426        for(int i=begin;i&amp;gt;=0;i--)27        {28            //使用过的元素就不能再使用了29            if(used[i])30                continue;31            //剪枝232            if(curSum+nums[i]&amp;gt;target)33                continue;34            used[i]=true;//添加元素nums[i]35            if(dfs(nums,i-1,target,curSum+nums[i],k,used))36                return true;//如果添加这个元素后，完成了题目要求，就return true.37            used[i]=false;//回溯38            while(i&amp;gt;0&amp;amp;&amp;amp;nums[i-1]==nums[i])//剪枝339                i--;40        }41        return false;42    }43}显示更多显示更少8、图#8.1 树上最短链#1/**2 * 阿里巴巴 2021 校招笔试真题--树上最短链3 */4public class TreeMinLink {5    public static int[] grade = null;6    public static Map&amp;lt;Integer,List&amp;lt;Integer&amp;gt;&amp;gt; edge = null;7    public static int res = Integer.MAX_VALUE;8    public static int root = 0;9    public static int n = 0;10    public static void main(String[] args) {11        Scanner scanner = new Scanner(System.in);12        n = scanner.nextInt();13        grade = new int[n+1];14        for (int i = 1; i &amp;lt;= n; i++) {15            grade[i] = scanner.nextInt();16        }17        edge = new HashMap&amp;lt;&amp;gt;();18        for (int i = 1; i &amp;lt; n; i++) {19            int x = scanner.nextInt();20            int y = scanner.nextInt();21            List&amp;lt;Integer&amp;gt; list;22            list = edge.getOrDefault(x,new ArrayList&amp;lt;&amp;gt;());23            list.add(y);24            edge.put(x,list);25            list = edge.getOrDefault(y,new ArrayList&amp;lt;&amp;gt;());26            list.add(x);27            edge.put(y,list);28        }29        for (int i = 1; i &amp;lt;= n; i++) {30            root = i;31            dfs(i,0,0);32        }33        System.out.println(res==Integer.MAX_VALUE? -1:res);34    }35
36    /**37     *38     * @param r 当前节点39     * @param pre 上一个dfs的节点 当前节点是从上个节点的链路中找到的，40     *          所以当前节点肯定有上一个节点的边，因此需要排除在外41     * @param path 当前路径长度42     */43    public static void dfs(int r,int pre,int path){44        if(r!=root &amp;amp;&amp;amp; grade[r]==grade[root]){//如果当前节点的等级和起点相同 并且不是起点 找到45            res = Math.min(res,path);46        }47        for(int i=0;i&amp;lt;edge.get(r).size();i++){48            if(edge.get(r).get(i)!=pre){//防止死循环 上一轮遍历的节点49                dfs(edge.get(r).get(i),r,path+1);50            }51        }52    }53}显示更多显示更少
讨论社区项目#1、Mybatis#2、Spring#3、SpringMVC#4、SpringBoot#5、登录#6、验证码#7、邮件#8、拦截器#9、敏感词过滤#10、Spring事务#11、redis事务#
杂项#1、语法分析#语法分析&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>kafka相关必须掌握概念</title><link>https://www.liuguang.top/posts/kafka/article-20260501-kafka%E7%9B%B8%E5%85%B3%E9%9D%A2%E8%AF%95/</link><guid isPermaLink="true">https://www.liuguang.top/posts/kafka/article-20260501-kafka%E7%9B%B8%E5%85%B3%E9%9D%A2%E8%AF%95/</guid><description>一些比较基础的kafka概念。</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;1.Kafka 的核心架构组件有哪些？各自的作用是什么？
2. Kafka 的主题（Topic）、分区（Partition）、副本（Replica）的关系是什么？
3. 为什么 Kafka 要设计分区？分区的作用有哪些？
4. Kafka 的副本机制原理是什么？ISR 副本集的作用？
5. Kafka 的 Leader 副本和 Follower 副本的职责区别？Follower 同步失败会怎样？
6. Kafka 的生产者（Producer）发送消息的流程是什么？
7. Kafka 生产者的 ACK 机制（0、1、-1/all）分别代表什么？各自的可靠性和性能特点？
8. Kafka 生产者如何保证消息有序？分区内有序和全局有序的实现方式？
9. Kafka 生产者如何解决消息重复发送的问题？幂等性和事务的区别？
10. Kafka 消费者（Consumer）的消费模式有哪些？消费者组（Consumer Group）的作用？
11. Kafka 的 offset 是什么？offset 的提交方式（自动 / 手动）及优缺点？
12. 如何解决 Kafka 消费过程中的重复消费和漏消费问题？
13. Kafka 的消费者 Rebalance 机制是什么？触发条件有哪些？如何避免 Rebalance 频繁发生？
14. Kafka 的消息存储机制？为什么顺序写磁盘性能高？
15. Kafka 的日志分段（Log Segment）原理？日志清理策略（删除 / 压缩）？
16. Kafka 的零拷贝（Zero Copy）原理？如何提升数据传输效率？
17. Kafka 如何保证消息不丢失？（从生产、存储、消费三个环节分析）
18. Kafka 如何保证消息不重复？（生产端、消费端分别如何处理）
19. Kafka 的高可用机制？集群中某个 Broker 宕机后如何保证服务可用？
20. Kafka 的分区分配策略有哪些？（Range、RoundRobin、Sticky 等）各自的适用场景？
21. Kafka 的延时队列如何实现？
22. Kafka 的死信队列（DLQ）作用？如何配置和使用？
23. Kafka 与 RabbitMQ 的核心区别？各自的适用场景？
24. Kafka 的吞吐量为什么高？（从架构、存储、网络等角度分析）
25. Kafka 的数据积压问题如何排查和解决？
26. Kafka 的分区数设置有什么原则？分区数过多 / 过少会有什么问题？
27. Kafka 的事务消息原理？如何实现生产端和消费端的事务一致性？
28. Kafka 的 Compact 日志清理策略适用场景？如何避免数据膨胀？
29. Kafka 的监控指标有哪些核心项？（如生产 / 消费速率、分区副本同步状态、UnderReplicatedPartitions 等）
30. Kafka 的跨集群数据同步如何实现？
31. Kafka 的消费者心跳机制（Heartbeat）原理？session.timeout.ms 的作用？
32. Kafka 的消息压缩机制？支持哪些压缩算法？压缩的优缺点？
33. Kafka 的 Broker 如何选举 Controller？Controller 的核心职责？
34. Kafka 在分布式事务中如何实现最终一致性？
35. Kafka 的 Zookeeper （或 KRaft）的作用？KRaft 相比 Zookeeper 有什么优势？&lt;/p&gt;</content:encoded></item><item><title>分布式相关理论</title><link>https://www.liuguang.top/posts/microservices/article-20260501-%E5%88%86%E5%B8%83%E5%BC%8F%E7%9B%B8%E5%85%B3%E7%90%86%E8%AE%BA/</link><guid isPermaLink="true">https://www.liuguang.top/posts/microservices/article-20260501-%E5%88%86%E5%B8%83%E5%BC%8F%E7%9B%B8%E5%85%B3%E7%90%86%E8%AE%BA/</guid><description>分布式相关理论。</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;1.什么是分布式系统？核心目标和挑战分别是什么？
2. 请解释 CAP 定理的 C、A、P 分别代表什么？为什么分布式系统只能在 C 和 A 之间二选一？
3. BASE 理论的核心思想是什么？与 CAP 定理的关系是什么？
4. 什么是最终一致性？强一致性、弱一致性、最终一致性的区别是什么？
5. Paxos 算法的核心思想是什么？解决了分布式系统的什么问题？
6. Raft 算法相比 Paxos 算法的优势是什么？核心流程（Leader 选举、日志复制）是怎样的？
7. ZooKeeper 的 ZAB 协议原理是什么？与 Raft 算法的异同点？
8. 什么是分布式事务？分布式事务的核心难点是什么？
9. 2PC（两阶段提交）的流程是什么？存在哪些缺点？
10. 3PC（三阶段提交）相比 2PC 做了哪些改进？是否完全解决了 2PC 的问题？
11. TCC 分布式事务的 Try、Confirm、Cancel 阶段分别做什么？有哪些优缺点和注意事项？
12. ##### &lt;a href=&quot;#####%E4%B8%80%E3%80%81%E5%85%88%E7%90%86%E8%A7%A3%EF%BC%9A%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E7%9A%84%E6%A0%B8%E5%BF%83%E9%97%AE%E9%A2%98&quot;&gt;基于消息队列的最终一致性分布式事务实现原理是什么？如何保证消息可靠投递和消费幂等？&lt;/a&gt;
13. 什么是分布式锁？分布式锁需要满足哪些核心特性？
14. Redis 分布式锁的实现原理是什么？存在哪些问题（如锁超时、误删）及解决方案？
15. ZooKeeper 分布式锁的实现原理是什么？与 Redis 分布式锁的区别及适用场景？
16. 什么是分布式 ID？分布式 ID 的生成方案有哪些？各自的优缺点？
17. 雪花算法（Snowflake）的结构是什么？存在哪些问题（如时钟回拨）及解决办法？
18. 什么是服务注册与发现？主流的服务注册中心（Eureka、Nacos、ZooKeeper）的区别是什么？
19. 什么是负载均衡？客户端负载均衡和服务端负载均衡的区别是什么？
20. 常见的负载均衡算法有哪些？各自的适用场景？
21. 什么是服务熔断和服务降级？两者的区别是什么？
22. 什么是服务雪崩？如何避免服务雪崩？
23. ##### [什么是分布式会话？分布式会话的实现方案有哪些？](#####一、先理解：传统 Session 分布式会话的痛点)
24. JWT 实现分布式会话的原理是什么？有哪些优缺点？
25. 什么是分布式链路追踪？核心原理是什么？常用的链路追踪工具（SkyWalking、Zipkin）的区别？
26. 什么是一致性哈希算法？解决了什么问题？虚拟节点的作用是什么？
27. 什么是脑裂现象？分布式系统中如何避免脑裂？
28. 什么是数据分片（Sharding）？水平分片和垂直分片的区别是什么？
29. 分布式系统中数据一致性的保障方案有哪些？
30. 什么是幂等性？分布式系统中如何实现接口幂等性？&lt;/p&gt;
&lt;section&gt;&lt;h3&gt;一、先理解：分布式事务的核心问题&lt;a href=&quot;#一先理解分布式事务的核心问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;在微服务场景中（如 “下单扣库存”），订单服务和库存服务是独立的数据库，若用传统单机事务（ACID），无法跨库保证 “订单创建成功则库存必须扣减成功”；若仅简单调用库存接口，可能出现：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;订单创建成功，但库存服务调用失败 → 数据不一致；&lt;/li&gt;
&lt;li&gt;库存扣减成功，但订单服务保存失败 → 数据不一致；&lt;/li&gt;
&lt;li&gt;网络超时，无法确定库存是否扣减成功 → 数据不一致。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;最终一致性的核心思路&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;放弃 “实时强一致”，追求 “最终数据一致”；&lt;/li&gt;
&lt;li&gt;以消息队列为桥梁，将跨服务操作拆分为 “本地事务 + 异步消息通知”；&lt;/li&gt;
&lt;li&gt;即使中间步骤失败，通过重试、补偿机制，最终让所有服务的数据达成一致。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、基于消息队列的最终一致性实现原理（核心模式：本地消息表 + 可靠消息投递）&lt;a href=&quot;#二基于消息队列的最终一致性实现原理核心模式本地消息表--可靠消息投递&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;最经典、落地性最强的实现模式是 &lt;strong&gt;“本地消息表 + 消息队列”&lt;/strong&gt;（也叫 “事务消息”），以 “下单扣库存” 为例，完整流程如下：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;1. 核心流程拆解&lt;a href=&quot;#1-核心流程拆解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;预览&lt;/p&gt;&lt;p&gt;查看代码&lt;/p&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;用户下单&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;订单服务执行本地事务&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;本地事务成功？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回下单失败&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;订单服务：在本地消息表插入“扣减库存”消息（状态：待发送）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;订单服务：提交本地事务（订单创建 + 消息插入 原子性）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;订单服务启动定时任务/消息发送线程，扫描本地消息表“待发送”消息&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;发送消息到MQ（如RocketMQ/Kafka）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;MQ确认接收？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;订单服务：更新本地消息表状态为“已发送”&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;定时任务重试发送（最多N次），仍失败则告警人工介入&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;库存服务监听MQ消息&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;库存服务执行扣减库存本地事务&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;库存扣减成功？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;库存服务：向MQ发送“扣减成功”确认消息（可选）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;重试扣减（最多N次），仍失败则告警/触发补偿逻辑&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;订单服务监听“扣减成功”消息，更新订单状态为“已扣库存”（可选）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;graph TD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A[用户下单] --&amp;gt; B[订单服务执行本地事务]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;B --&amp;gt; C{本地事务成功？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C -- 否 --&amp;gt; D[返回下单失败]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C -- 是 --&amp;gt; E[订单服务：在本地消息表插入“扣减库存”消息（状态：待发送）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E --&amp;gt; F[订单服务：提交本地事务（订单创建 + 消息插入 原子性）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;F --&amp;gt; G[订单服务启动定时任务/消息发送线程，扫描本地消息表“待发送”消息]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;G --&amp;gt; H[发送消息到MQ（如RocketMQ/Kafka）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;H --&amp;gt; I{MQ确认接收？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;I -- 是 --&amp;gt; J[订单服务：更新本地消息表状态为“已发送”]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;I -- 否 --&amp;gt; K[定时任务重试发送（最多N次），仍失败则告警人工介入]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;H --&amp;gt; L[库存服务监听MQ消息]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;L --&amp;gt; M[库存服务执行扣减库存本地事务]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;M --&amp;gt; N{库存扣减成功？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;N -- 是 --&amp;gt; O[库存服务：向MQ发送“扣减成功”确认消息（可选）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;N -- 否 --&amp;gt; P[重试扣减（最多N次），仍失败则告警/触发补偿逻辑]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;O --&amp;gt; Q[订单服务监听“扣减成功”消息，更新订单状态为“已扣库存”（可选）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;用户下单&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;订单服务执行本地事务&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;本地事务成功？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回下单失败&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;订单服务：在本地消息表插入“扣减库存”消息（状态：待发送）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;订单服务：提交本地事务（订单创建 + 消息插入 原子性）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;订单服务启动定时任务/消息发送线程，扫描本地消息表“待发送”消息&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;发送消息到MQ（如RocketMQ/Kafka）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;MQ确认接收？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;订单服务：更新本地消息表状态为“已发送”&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;定时任务重试发送（最多N次），仍失败则告警人工介入&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;库存服务监听MQ消息&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;库存服务执行扣减库存本地事务&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;库存扣减成功？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;库存服务：向MQ发送“扣减成功”确认消息（可选）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;重试扣减（最多N次），仍失败则告警/触发补偿逻辑&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;订单服务监听“扣减成功”消息，更新订单状态为“已扣库存”（可选）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 核心原理说明&lt;a href=&quot;#2-核心原理说明&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;本地事务原子性&lt;/strong&gt;：订单创建和 “扣减库存” 消息插入本地消息表，在同一个数据库事务中完成 —— 要么都成功，要么都失败，保证 “有订单必有消息，有消息必有订单”；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;消息可靠投递&lt;/strong&gt;：通过定时任务重试发送本地消息表中的 “待发送” 消息，确保消息最终能投递到 MQ；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最终一致性&lt;/strong&gt;：库存服务消费消息后，重试扣减库存，直到成功；即使库存服务宕机，重启后仍能消费消息，最终完成扣减，达成数据一致。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 简化模式：MQ 事务消息（如 RocketMQ 原生支持）&lt;a href=&quot;#3-简化模式mq-事务消息如-rocketmq-原生支持&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;主流 MQ（如 RocketMQ）内置了 “事务消息” 功能，无需手动维护本地消息表，核心流程：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;订单服务发送 “半消息” 到 MQ（半消息对消费者不可见）；&lt;/li&gt;
&lt;li&gt;订单服务执行本地事务（创建订单）；&lt;/li&gt;
&lt;li&gt;若本地事务成功，向 MQ 发送 “提交” 指令，半消息变为可见；若失败，发送 “回滚” 指令，MQ 删除半消息；&lt;/li&gt;
&lt;li&gt;MQ 会定时回查订单服务的本地事务状态，确保半消息最终要么提交要么回滚；&lt;/li&gt;
&lt;li&gt;库存服务消费提交后的消息，完成扣减库存。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;三、如何保证消息可靠投递&lt;a href=&quot;#三如何保证消息可靠投递&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;消息丢失可能发生在 “生产端→MQ”“MQ 内部存储”“消费端→业务处理” 三个阶段，需针对性防护：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;1. 生产端：保证消息能成功发送到 MQ&lt;a href=&quot;#1-生产端保证消息能成功发送到-mq&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;本地消息表 + 定时重试&lt;/strong&gt;（通用方案）：如上文所述，生产端先将消息写入本地数据库（与业务事务原子），再异步发送，失败则定时重试；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MQ 生产者确认机制&lt;/strong&gt;：开启 MQ 的生产者确认（Producer Ack），只有收到 MQ 的 “消息已接收” 确认，才标记消息为 “已发送”；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;幂等发送&lt;/strong&gt;：生产端重试时，给消息设置唯一 ID，避免重复发送（MQ 端可去重）。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. MQ 端：保证消息不丢失&lt;a href=&quot;#2-mq-端保证消息不丢失&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;持久化&lt;/strong&gt;：开启 MQ 消息持久化（如 RocketMQ 持久化到磁盘、Kafka 持久化到日志），即使 MQ 宕机，重启后消息不丢失；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;集群部署&lt;/strong&gt;：采用主从 / 副本集群，避免单节点故障导致消息丢失；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;消息刷盘策略&lt;/strong&gt;：设置 “同步刷盘”（牺牲一点性能，保证消息写入磁盘后再返回确认），而非 “异步刷盘”。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 消费端：保证消息能被成功处理&lt;a href=&quot;#3-消费端保证消息能被成功处理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;消费确认机制&lt;/strong&gt;：关闭 MQ 的 “自动确认”，改为 “手动确认”—— 只有业务处理成功（如库存扣减完成），才向 MQ 发送 “消费成功” 确认；若处理失败，不确认，MQ 会重新投递消息；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;死信队列&lt;/strong&gt;：设置最大重试次数（如 3 次），重试失败的消息进入死信队列，避免无限重试，同时人工排查死信队列的消息；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;消费状态记录&lt;/strong&gt;：消费端将已处理的消息 ID 写入本地数据库 / Redis，处理前先检查是否已处理，避免重复消费（幂等）。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;四、如何保证消费幂等&lt;a href=&quot;#四如何保证消费幂等&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;幂等消费是指 “同一消息被消费多次，结果仍一致”（如库存扣减不会因重复消费扣减多次），核心实现思路：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;1. 核心原则：业务操作必须是幂等的&lt;a href=&quot;#1-核心原则业务操作必须是幂等的&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;设计业务接口时，确保重复执行的结果与单次执行一致，常用方案：&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;幂等实现方式&lt;/th&gt;&lt;th&gt;适用场景&lt;/th&gt;&lt;th&gt;实现思路&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;唯一 ID + 防重表&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;所有场景（推荐）&lt;/td&gt;&lt;td&gt;1. 生产端给每条消息生成唯一 ID（如订单 ID + 操作类型）；2. 消费端处理前，先将消息 ID 插入防重表（数据库唯一索引）；3. 插入成功则处理业务，插入失败（已存在）则直接返回成功；&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;乐观锁&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;数据更新场景（如扣库存）&lt;/td&gt;&lt;td&gt;库存表添加版本号字段，扣减时：&lt;code&gt;UPDATE stock SET num=num-1, version=version+1 WHERE id=? AND version=?&lt;/code&gt;；重复执行时版本号不匹配，更新失败；&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;状态机控制&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;有状态流转的场景（如订单）&lt;/td&gt;&lt;td&gt;订单状态从 “待扣库存”→“已扣库存”，重复消费时检查状态，若已为 “已扣库存”，则跳过处理；&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Redis 分布式锁&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;高并发场景&lt;/td&gt;&lt;td&gt;消费时先获取消息 ID 的 Redis 锁，处理完成释放锁；重复消费时获取不到锁，直接返回成功；&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 实战示例：防重表实现幂等消费（扣库存）&lt;a href=&quot;#2-实战示例防重表实现幂等消费扣库存&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;ja&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 1. 防重表结构（MySQL）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;CREATE TABLE `mq_message_confirm` (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;`id` bigint NOT NULL AUTO_INCREMENT,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;`message_id` varchar(64) NOT NULL COMMENT &apos;消息唯一ID&apos;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;`business_type` varchar(32) NOT NULL COMMENT &apos;业务类型（如扣库存）&apos;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;`status` tinyint NOT NULL COMMENT &apos;0-处理中 1-处理成功 2-处理失败&apos;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;`create_time` datetime DEFAULT CURRENT_TIMESTAMP,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;PRIMARY KEY (`id`),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UNIQUE KEY `uk_message_id` (`message_id`) COMMENT &apos;唯一索引，防止重复插入&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 2. 消费端处理逻辑&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class StockConsumer {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Resource&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private StockMapper stockMapper;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Resource&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private MqMessageConfirmMapper confirmMapper;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Transactional&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public void consume(String messageId, Long productId, Integer deductNum) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 步骤1：插入防重表（唯一索引保证幂等）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;MqMessageConfirm confirm = new MqMessageConfirm();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confirm.setMessageId(messageId);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confirm.setBusinessType(&quot;DEDUCT_STOCK&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confirm.setStatus(0);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;try {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confirmMapper.insert(confirm);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} catch (DuplicateKeyException e) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 重复消费，直接返回成功&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 步骤2：执行扣库存业务（乐观锁保证幂等）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;int updateCount = stockMapper.deductStock(productId, deductNum);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if (updateCount == 0) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 库存不足/版本号不匹配，更新失败&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confirm.setStatus(2);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confirmMapper.updateById(confirm);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;throw new RuntimeException(&quot;库存扣减失败&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 步骤3：更新防重表状态为成功&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confirm.setStatus(1);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;47&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;confirmMapper.updateById(confirm);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;48&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;49&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;五、总结&lt;a href=&quot;#五总结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;最终一致性原理&lt;/strong&gt;：基于 “本地事务 + 消息队列”，将跨服务操作拆分为 “本地操作 + 异步消息通知”，通过重试 / 补偿机制，最终让所有服务数据一致；核心是 “本地事务原子性” 和 “消息可靠投递”；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可靠投递保障&lt;/strong&gt;：生产端（本地消息表 + 重试）+ MQ 端（持久化 + 集群）+ 消费端（手动确认 + 死信队列），覆盖消息流转全链路；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;消费幂等保障&lt;/strong&gt;：核心是 “业务操作幂等”，常用方案为 “唯一 ID + 防重表”（通用）、乐观锁（更新场景），确保重复消费结果一致。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;关键点：最终一致性不是 “忽略不一致”，而是通过异步重试、补偿、监控，让数据最终达成一致；可靠投递和幂等消费是该方案的两大基石，缺一不可&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;一、先理解：传统 Session 分布式会话的痛点&lt;a href=&quot;#一先理解传统-session-分布式会话的痛点&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;在单体应用中，Session 存储在服务器内存，客户端通过 Cookie 携带 &lt;code&gt;JSESSIONID&lt;/code&gt; 即可完成会话认证，但分布式 / 微服务场景下会出现核心问题：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;会话共享问题&lt;/strong&gt;：用户请求分发到不同服务器时，其他服务器无该用户的 Session，导致认证失效；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;存储压力&lt;/strong&gt;：Session 存储在服务端内存 / 数据库，高并发下占用大量资源，且需考虑 Session 过期、同步问题；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跨域问题&lt;/strong&gt;：Cookie 受同源策略限制，跨域服务间无法携带 &lt;code&gt;JSESSIONID&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;JWT 的核心解决思路&lt;/strong&gt;：将会话信息（用户 ID、角色、权限等）加密后存储在&lt;strong&gt;客户端令牌&lt;/strong&gt;中，服务端无需存储任何会话数据，仅通过校验令牌的合法性完成认证，天然适配分布式场景。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、JWT 实现分布式会话的核心原理&lt;a href=&quot;#二jwt-实现分布式会话的核心原理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;JWT（JSON Web Token）本质是&lt;strong&gt;基于 JSON 的轻量级令牌&lt;/strong&gt;，由服务端生成并签名，客户端存储（Cookie/Header/ 本地存储），每次请求携带令牌，服务端通过 “验签 + 解析” 完成身份认证，核心流程如下：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;1. JWT 令牌的结构（三段式，Base64 编码）&lt;a href=&quot;#1-jwt-令牌的结构三段式base64-编码&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;JWT 令牌由 &lt;code&gt;Header.Payload.Signature&lt;/code&gt; 三部分组成，用 &lt;code&gt;.&lt;/code&gt; 分隔，比如：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjMiLCJyb2xlIjoiYWRtaW4iLCJleHAiOjE3MzU2ODM2MDB9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;section&gt;&lt;h5&gt;（1）Header（头部）：声明算法和令牌类型&lt;a href=&quot;#1header头部声明算法和令牌类型&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ul&gt;
&lt;li&gt;内容：JSON 格式，指定签名算法（如 HS256）和令牌类型（JWT）；&lt;/li&gt;
&lt;li&gt;示例：&lt;code&gt;{&quot;alg&quot;: &quot;HS256&quot;, &quot;typ&quot;: &quot;JWT&quot;}&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;作用：服务端验签时，根据 Header 中的算法验证 Signature 是否合法。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;（2）Payload（载荷）：存储会话核心信息&lt;a href=&quot;#2payload载荷存储会话核心信息&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ul&gt;
&lt;li&gt;内容：JSON 格式，包含&lt;strong&gt;标准声明&lt;/strong&gt;（如过期时间 &lt;code&gt;exp&lt;/code&gt;、签发时间 &lt;code&gt;iat&lt;/code&gt;）和&lt;strong&gt;自定义声明&lt;/strong&gt;（如用户 ID、角色、权限）；&lt;/li&gt;
&lt;li&gt;示例：&lt;code&gt;{&quot;userId&quot;: &quot;123&quot;, &quot;role&quot;: &quot;admin&quot;, &quot;exp&quot;: 1735683600}&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;关键：Payload 仅 Base64 编码（可解码，&lt;strong&gt;不加密&lt;/strong&gt;），因此不能存储敏感信息（如密码）。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;（3）Signature（签名）：令牌合法性校验核心&lt;a href=&quot;#3signature签名令牌合法性校验核心&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ul&gt;
&lt;li&gt;生成规则：&lt;code&gt;HMACSHA256(base64UrlEncode(Header) + &quot;.&quot; + base64UrlEncode(Payload), 密钥)&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;作用：服务端通过相同的密钥和算法重新计算签名，与令牌中的 Signature 对比 —— 若一致，说明令牌未被篡改；若不一致，令牌无效。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. JWT 分布式会话的完整流程&lt;a href=&quot;#2-jwt-分布式会话的完整流程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;用户登录&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;认证服务校验账号密码&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;校验通过？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回登录失败&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;认证服务生成JWT令牌：Header+Payload+Signature&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回JWT令牌给客户端（存储在LocalStorage/Header）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;客户端发起请求（如/user/info），在Header中携带JWT令牌&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;网关/任意微服务节点接收请求&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;服务端解析Header，获取签名算法&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;服务端用相同密钥重新计算Signature&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Signature一致？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回401：令牌被篡改/无效&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Payload中exp未过期？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回401：令牌过期&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;解析Payload获取用户信息（如userId）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;完成身份/权限校验，处理业务请求&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回响应给客户端&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;graph TD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A[用户登录] --&amp;gt; B[认证服务校验账号密码]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;B --&amp;gt; C{校验通过？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C -- 否 --&amp;gt; D[返回登录失败]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C -- 是 --&amp;gt; E[认证服务生成JWT令牌：Header+Payload+Signature]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E --&amp;gt; F[返回JWT令牌给客户端（存储在LocalStorage/Header）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;F --&amp;gt; G[客户端发起请求（如/user/info），在Header中携带JWT令牌]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;G --&amp;gt; H[网关/任意微服务节点接收请求]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;H --&amp;gt; I[服务端解析Header，获取签名算法]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;I --&amp;gt; J[服务端用相同密钥重新计算Signature]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;J --&amp;gt; K{Signature一致？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;K -- 否 --&amp;gt; L[返回401：令牌被篡改/无效]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;K -- 是 --&amp;gt; M{Payload中exp未过期？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;M -- 否 --&amp;gt; N[返回401：令牌过期]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;M -- 是 --&amp;gt; O[解析Payload获取用户信息（如userId）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;O --&amp;gt; P[完成身份/权限校验，处理业务请求]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;P --&amp;gt; Q[返回响应给客户端]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;用户登录&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;认证服务校验账号密码&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;校验通过？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回登录失败&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;认证服务生成JWT令牌：Header+Payload+Signature&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回JWT令牌给客户端（存储在LocalStorage/Header）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;客户端发起请求（如/user/info），在Header中携带JWT令牌&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;网关/任意微服务节点接收请求&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;服务端解析Header，获取签名算法&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;服务端用相同密钥重新计算Signature&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Signature一致？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回401：令牌被篡改/无效&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Payload中exp未过期？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回401：令牌过期&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;解析Payload获取用户信息（如userId）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;完成身份/权限校验，处理业务请求&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回响应给客户端&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 分布式场景的核心优势（为什么适配分布式）&lt;a href=&quot;#3-分布式场景的核心优势为什么适配分布式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;无状态化&lt;/strong&gt;：服务端无需存储任何会话数据，所有会话信息都在 JWT 令牌中，任意微服务节点拿到令牌都能独立完成认证，解决了 Session 共享问题；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跨服务 / 跨域&lt;/strong&gt;：令牌可通过 HTTP Header（如 &lt;code&gt;Authorization: Bearer {token}&lt;/code&gt;）携带，不受 Cookie 同源策略限制，适配跨域 / 跨服务调用；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;轻量高效&lt;/strong&gt;：令牌仅为字符串，传输开销小，服务端验签 + 解析的计算成本低，适配高并发分布式场景。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;三、关键细节：JWT 分布式会话的注意事项&lt;a href=&quot;#三关键细节jwt-分布式会话的注意事项&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;密钥安全&lt;/strong&gt;：签名密钥（Secret）必须保密且足够复杂（建议 32 位以上），若密钥泄露，攻击者可伪造任意令牌；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;过期时间&lt;/strong&gt;：必须设置合理的 &lt;code&gt;exp&lt;/code&gt;（过期时间），避免令牌长期有效（建议 1-2 小时），过期后需重新登录或刷新令牌；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;刷新令牌机制&lt;/strong&gt;：为提升用户体验，可生成 “访问令牌（短期）+ 刷新令牌（长期）”—— 访问令牌过期后，用刷新令牌申请新的访问令牌，无需重新登录；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免存储敏感信息&lt;/strong&gt;：Payload 仅 Base64 编码（可通过 &lt;a href=&quot;https://jwt.io/&quot; target=&quot;_blank&quot;&gt;jwt.io&lt;/a&gt; 解码），不能存储密码、手机号等敏感信息；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;令牌吊销&lt;/strong&gt;：JWT 本身不支持主动吊销（服务端无存储），若需实现 “登出 / 令牌作废”，需结合 Redis 维护令牌黑名单（验签时先查黑名单）。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;四、JWT 分布式会话 vs 传统 Session 共享&lt;a href=&quot;#四jwt-分布式会话-vs-传统-session-共享&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;


































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;特性&lt;/th&gt;&lt;th&gt;JWT 分布式会话&lt;/th&gt;&lt;th&gt;传统 Session 共享（Redis）&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;服务端存储&lt;/td&gt;&lt;td&gt;无状态，不存储任何会话数据&lt;/td&gt;&lt;td&gt;需存储 Session 到 Redis，有状态&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;跨域 / 跨服务&lt;/td&gt;&lt;td&gt;天然支持（Header 携带）&lt;/td&gt;&lt;td&gt;依赖 Cookie，跨域需特殊配置&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;性能&lt;/td&gt;&lt;td&gt;验签 / 解析本地计算，性能高&lt;/td&gt;&lt;td&gt;需访问 Redis，有网络开销&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;令牌吊销&lt;/td&gt;&lt;td&gt;需结合 Redis 黑名单实现&lt;/td&gt;&lt;td&gt;直接删除 Redis 中的 Session 即可&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;过期管理&lt;/td&gt;&lt;td&gt;令牌内置 exp，客户端 / 服务端校验&lt;/td&gt;&lt;td&gt;依赖 Redis 过期策略&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;总结&lt;a href=&quot;#总结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;JWT 实现分布式会话的核心是&lt;strong&gt;无状态化&lt;/strong&gt;：将会话信息编码到客户端令牌中，服务端通过验签（Signature）保证令牌未被篡改，通过解析 Payload 获取用户信息完成认证，无需存储会话数据；&lt;/li&gt;
&lt;li&gt;三段式结构（Header+Payload+Signature）是核心：Header 声明算法，Payload 存储会话信息，Signature 保证令牌合法性；&lt;/li&gt;
&lt;li&gt;适配分布式的关键：任意微服务节点都能独立完成令牌校验，解决了传统 Session 共享、跨域、存储压力等问题。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;关键点：JWT 并非 “银弹”，需结合刷新令牌、黑名单、合理过期时间等机制，才能在分布式场景下安全、高效地实现会话管理。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>Redis面试常问的相关问题</title><link>https://www.liuguang.top/posts/redis/article-20260501-redis%E7%9B%B8%E5%85%B3%E9%9D%A2%E8%AF%95/</link><guid isPermaLink="true">https://www.liuguang.top/posts/redis/article-20260501-redis%E7%9B%B8%E5%85%B3%E9%9D%A2%E8%AF%95/</guid><description>问的比较多的一些问题整理。</description><pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;ol&gt;
&lt;li&gt;Redis 有哪些核心数据结构？各自的使用场景是什么？&lt;/li&gt;
&lt;li&gt;Redis 的 String 类型底层是如何实现的？int、embstr、raw 三种编码的区别？&lt;/li&gt;
&lt;li&gt;HashMap 和 Redis 的 Hash 结构底层实现有什么不同？Redis Hash 的扩容机制？&lt;/li&gt;
&lt;li&gt;Redis 的 List 类型底层是 ziplist 还是 linkedlist？转换条件是什么？&lt;/li&gt;
&lt;li&gt;Redis 的 Set 和 Sorted Set 底层实现分别是什么？Sorted Set 如何实现排序？&lt;/li&gt;
&lt;li&gt;Redis 有哪几种持久化方式？RDB 和 AOF 的原理、优缺点及适用场景？&lt;/li&gt;
&lt;li&gt;Redis 混合持久化的原理是什么？相比单独的 RDB/AOF 有什么优势？&lt;/li&gt;
&lt;li&gt;
&lt;section&gt;&lt;h5&gt;[Redis 的过期键删除策略有哪些？为什么不采用定时删除？](#####一、Redis 过期键的三种核心删除策略)&lt;a href=&quot;#redis-的过期键删除策略有哪些为什么不采用定时删除一redis-过期键的三种核心删除策略&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;/li&gt;
&lt;li&gt;Redis 的内存淘汰策略有哪些？volatile-lru 和 allkeys-lru 的区别？&lt;/li&gt;
&lt;li&gt;什么是缓存穿透？如何解决？布隆过滤器的原理及优缺点？&lt;/li&gt;
&lt;li&gt;什么是缓存击穿？如何解决？热点 key 永不过期的注意事项？&lt;/li&gt;
&lt;li&gt;什么是缓存雪崩？如何解决？过期时间随机化的实现思路？&lt;/li&gt;
&lt;li&gt;
&lt;section&gt;&lt;h5&gt;[Redis 如何实现分布式锁？需要注意哪些问题（原子性、死锁、误删）？](#####Redis 如何实现分布式锁？需要注意哪些问题（原子性、死锁、误删）？)&lt;a href=&quot;#redis-如何实现分布式锁需要注意哪些问题原子性死锁误删redis-如何实现分布式锁需要注意哪些问题原子性死锁误删&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;/li&gt;
&lt;li&gt;Redis 分布式锁和 ZooKeeper 分布式锁的区别及适用场景？&lt;/li&gt;
&lt;li&gt;
&lt;section&gt;&lt;h5&gt;[Redis 主从复制的原理是什么？数据同步的流程（全量同步 + 增量同步）？](#####Redis 主从复制的核心原理)&lt;a href=&quot;#redis-主从复制的原理是什么数据同步的流程全量同步--增量同步redis-主从复制的核心原理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;/li&gt;
&lt;li&gt;Redis 哨兵模式的作用？哨兵如何实现主节点故障转移？&lt;/li&gt;
&lt;li&gt;Redis Cluster 集群的哈希槽机制？16384 个哈希槽的设计原因？&lt;/li&gt;
&lt;li&gt;Redis Cluster 如何实现高可用？主节点宕机后从节点如何接管？&lt;/li&gt;
&lt;li&gt;Redis 为什么快？（从内存、IO 模型、数据结构、单线程等角度）&lt;/li&gt;
&lt;li&gt;
&lt;section&gt;&lt;h5&gt;Redis 的单线程模型为什么能支撑高并发？单线程的瓶颈是什么？&lt;a href=&quot;#redis-的单线程模型为什么能支撑高并发单线程的瓶颈是什么&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;/li&gt;
&lt;li&gt;Redis 的事务机制？为什么说 Redis 事务是 “弱事务”？&lt;/li&gt;
&lt;li&gt;Redis 的 Pipeline 原理？相比单条命令执行有什么优势？&lt;/li&gt;
&lt;li&gt;Redis 的 Bitmap 如何实现？适用场景（如统计活跃用户）？&lt;/li&gt;
&lt;li&gt;Redis 的 HyperLogLog 原理？为什么能以极小内存统计基数？&lt;/li&gt;
&lt;li&gt;Redis 的 Geo 数据结构原理？如何实现 “附近的人” 功能？&lt;/li&gt;
&lt;li&gt;Redis 主从同步时的数据延迟问题如何解决？&lt;/li&gt;
&lt;li&gt;Redis 中的 BigKey 会带来哪些问题？如何发现和处理 BigKey？&lt;/li&gt;
&lt;li&gt;Redis 的键过期后为什么还会占用内存？&lt;/li&gt;
&lt;li&gt;Redis 如何做内存优化？（数据结构、编码、过期策略等）&lt;/li&gt;
&lt;li&gt;Redis 与 Memcached 的区别？各自的适用场景？&lt;/li&gt;
&lt;li&gt;Redis 的持久化文件损坏如何恢复？&lt;/li&gt;
&lt;li&gt;Redis 集群脑裂问题如何产生？如何解决？&lt;/li&gt;
&lt;li&gt;Redis 的发布订阅模式原理？有什么缺点？&lt;/li&gt;
&lt;li&gt;Redis 的 Lua 脚本作用？为什么能保证原子性？&lt;/li&gt;
&lt;li&gt;
&lt;section&gt;&lt;h5&gt;&lt;a href=&quot;#####%E4%B8%80%E3%80%81%E5%85%88%E7%90%86%E8%A7%A3%EF%BC%9A%E7%BC%93%E5%AD%98%E4%B8%8E%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8F%8C%E5%86%99%E4%B8%8D%E4%B8%80%E8%87%B4%E7%9A%84%E6%A0%B8%E5%BF%83%E6%88%90%E5%9B%A0&quot;&gt;Redis 缓存与数据库双写不一致如何解决？（延时双删、分布式事务等）&lt;/a&gt;&lt;a href=&quot;#redis-缓存与数据库双写不一致如何解决延时双删分布式事务等&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;/section&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;section&gt;&lt;h5&gt;Redis 主从复制的核心原理&lt;a href=&quot;#redis-主从复制的核心原理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;Redis 主从复制（Master-Slave Replication）的核心目标是：&lt;strong&gt;让从节点（Slave）的数据完全镜像主节点（Master）的数据&lt;/strong&gt;，主节点负责处理写请求，从节点负责处理读请求，实现读写分离和故障备份。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h4&gt;核心设计原则&lt;a href=&quot;#核心设计原则&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;异步复制&lt;/strong&gt;：主节点处理完写请求后，异步将数据同步给从节点（不阻塞主节点的写操作）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;从节点只读&lt;/strong&gt;：从节点默认禁止写操作（可通过 &lt;code&gt;slave-read-only no&lt;/code&gt; 关闭，但不推荐）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多从架构&lt;/strong&gt;：一个主节点可挂载多个从节点，从节点也可作为其他从节点的主节点（级联复制）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据一致性&lt;/strong&gt;：主从之间通过 “复制偏移量”“运行 ID”“复制积压缓冲区” 保证同步的准确性。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
&lt;section&gt;&lt;h4&gt;核心概念（理解流程的关键）&lt;a href=&quot;#核心概念理解流程的关键&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;




























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;概念&lt;/th&gt;&lt;th&gt;作用&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;运行 ID（runid）&lt;/td&gt;&lt;td&gt;每个 Redis 节点启动时生成的唯一 ID，主节点的 runid 用于从节点识别主节点身份&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;复制偏移量（offset）&lt;/td&gt;&lt;td&gt;主节点每次向从节点同步数据，都会记录已发送的字节数（主节点：master_repl_offset；从节点：slave_repl_offset），用于判断数据是否同步一致&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;复制积压缓冲区（backlog）&lt;/td&gt;&lt;td&gt;主节点维护的一个固定长度的环形缓冲区，存储最近同步的写命令，用于增量同步&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PSYNC 命令&lt;/td&gt;&lt;td&gt;Redis 2.8+ 新增的同步命令，支持全量同步（FULLRESYNC）和增量同步（CONTINUE）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;RDB 文件&lt;/td&gt;&lt;td&gt;主节点生成的内存快照文件，全量同步时用于向从节点传输完整数据&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、Redis 主从复制的完整数据同步流程&lt;a href=&quot;#二redis-主从复制的完整数据同步流程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Redis 主从同步分为两个核心场景：&lt;strong&gt;全量同步（初次同步 / 异常恢复）&lt;/strong&gt; 和 &lt;strong&gt;增量同步（正常运行时）&lt;/strong&gt;，PSYNC 命令是连接这两个场景的核心。&lt;/p&gt;&lt;section&gt;&lt;h4&gt;前置条件：从节点连接主节点&lt;a href=&quot;#前置条件从节点连接主节点&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;从节点通过配置 / 命令指定主节点地址，建立连接：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 方式1：配置文件（redis.conf）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;replicaof 192.168.1.100 6379  # Redis 5.0+ 用replicaof，旧版本用slaveof&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;masterauth 123456  # 主节点有密码时配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 方式2：运行时命令&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;127.0.0.1:6379&amp;gt; replicaof 192.168.1.100 6379&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;127.0.0.1:6379&amp;gt; config set masterauth 123456&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;场景 1：全量同步（Full Resync）&lt;a href=&quot;#场景-1全量同步full-resync&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;触发时机&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;从节点首次连接主节点；&lt;/li&gt;
&lt;li&gt;从节点失联后重新连接，主节点的复制积压缓冲区中没有对应的偏移量数据；&lt;/li&gt;
&lt;li&gt;从节点发送的 PSYNC 命令中，主节点的 runid 不匹配（如主节点重启后 runid 变化）。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;全量同步完整流程&lt;/strong&gt;（10 个步骤，附流程图）：&lt;/p&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;从节点发送PSYNC ? -1 命令&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;主节点接收命令，判断需全量同步&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;主节点执行BGSAVE生成RDB文件&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;主节点将新写命令写入复制积压缓冲区&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;主节点发送FULLRESYNC + runid + offset给从节点&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;从节点保存runid和offset，清空旧数据&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;主节点发送RDB文件给从节点&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;从节点接收并加载RDB文件（阻塞读请求）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;主节点发送复制积压缓冲区中的写命令给从节点&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;从节点执行这些命令，同步offset到主节点水平&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;graph TD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A[从节点发送PSYNC ? -1 命令] --&amp;gt; B[主节点接收命令，判断需全量同步]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;B --&amp;gt; C[主节点执行BGSAVE生成RDB文件]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C --&amp;gt; D[主节点将新写命令写入复制积压缓冲区]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;D --&amp;gt; E[主节点发送FULLRESYNC + runid + offset给从节点]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E --&amp;gt; F[从节点保存runid和offset，清空旧数据]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;F --&amp;gt; G[主节点发送RDB文件给从节点]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;G --&amp;gt; H[从节点接收并加载RDB文件（阻塞读请求）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;H --&amp;gt; I[主节点发送复制积压缓冲区中的写命令给从节点]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;I --&amp;gt; J[从节点执行这些命令，同步offset到主节点水平]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;从节点发送PSYNC ? -1 命令&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;主节点接收命令，判断需全量同步&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;主节点执行BGSAVE生成RDB文件&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;主节点将新写命令写入复制积压缓冲区&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;主节点发送FULLRESYNC + runid + offset给从节点&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;从节点保存runid和offset，清空旧数据&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;主节点发送RDB文件给从节点&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;从节点接收并加载RDB文件（阻塞读请求）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;主节点发送复制积压缓冲区中的写命令给从节点&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;从节点执行这些命令，同步offset到主节点水平&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;步骤拆解（通俗易懂版）&lt;/strong&gt;：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;从节点发起同步请求&lt;/strong&gt;：从节点连接主节点后，发送 &lt;code&gt;PSYNC ? -1&lt;/code&gt; 命令（&lt;code&gt;?&lt;/code&gt; 表示未知主节点 runid，&lt;code&gt;-1&lt;/code&gt; 表示偏移量为 - 1，请求全量同步）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主节点准备全量数据&lt;/strong&gt;：主节点收到请求后，执行 &lt;code&gt;BGSAVE&lt;/code&gt; 命令（后台生成 RDB 文件，不阻塞主节点写操作）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主节点缓存新写命令&lt;/strong&gt;：生成 RDB 期间，主节点将新收到的写命令写入 “复制积压缓冲区”（避免这部分数据丢失）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主节点发送同步指令&lt;/strong&gt;：主节点向从节点发送 &lt;code&gt;FULLRESYNC {runid} {offset}&lt;/code&gt; 指令，告知从节点 “需要全量同步，我的 runid 是 XXX，当前偏移量是 XXX”；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;从节点准备接收数据&lt;/strong&gt;：从节点保存主节点的 runid 和 offset，清空自己的旧数据（避免数据冲突）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主节点传输 RDB 文件&lt;/strong&gt;：主节点将生成的 RDB 文件发送给从节点；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;从节点加载 RDB 文件&lt;/strong&gt;：从节点接收 RDB 文件后，加载到内存（此阶段从节点阻塞，无法处理读请求）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主节点同步增量命令&lt;/strong&gt;：RDB 传输完成后，主节点将 “复制积压缓冲区” 中的写命令发送给从节点；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;从节点执行增量命令&lt;/strong&gt;：从节点执行这些命令，将自己的 offset 同步到主节点的水平，完成全量同步。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;场景 2：增量同步（Partial Resync）&lt;a href=&quot;#场景-2增量同步partial-resync&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;触发时机&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;从节点短暂失联（如网络抖动）后重新连接主节点；&lt;/li&gt;
&lt;li&gt;主节点的复制积压缓冲区中，仍保存着从节点失联期间的写命令；&lt;/li&gt;
&lt;li&gt;从节点发送的 PSYNC 命令中，主节点的 runid 匹配，且偏移量在复制积压缓冲区范围内。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;增量同步完整流程&lt;/strong&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;graph TD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A[从节点发送PSYNC {runid} {offset}命令] --&amp;gt; B[主节点校验runid和offset]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;B --&amp;gt; C{校验通过？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C -- 是 --&amp;gt; D[主节点从复制积压缓冲区中，读取offset之后的写命令]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;D --&amp;gt; E[主节点发送CONTINUE指令+这些写命令给从节点]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E --&amp;gt; F[从节点执行命令，更新自己的offset]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C -- 否 --&amp;gt; G[发全量同步]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;步骤拆解&lt;/strong&gt;：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;从节点发起增量请求&lt;/strong&gt;：从节点重新连接主节点后，发送 &lt;code&gt;PSYNC {主节点runid} {自己当前的offset}&lt;/code&gt; 命令，告知主节点 “我之前同步到了这个偏移量，请求继续同步”；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主节点校验信息&lt;/strong&gt;：主节点检查 runid 是否匹配（确认是同一个主节点），并检查从节点的 offset 是否在 “复制积压缓冲区” 的范围内；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主节点发送增量命令&lt;/strong&gt;：校验通过后，主节点从复制积压缓冲区中，读取从节点 offset 之后的所有写命令，发送给从节点；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;从节点执行命令&lt;/strong&gt;：从节点执行这些写命令，将自己的 offset 更新到主节点的水平，完成增量同步；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;校验失败兜底&lt;/strong&gt;：若 runid 不匹配（如主节点重启）或 offset 超出缓冲区范围，主节点会触发全量同步。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;三、关键细节与实战注意事项&lt;a href=&quot;#三关键细节与实战注意事项&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;1. 复制积压缓冲区的核心作用&lt;a href=&quot;#1-复制积压缓冲区的核心作用&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;是主节点维护的&lt;strong&gt;固定长度环形缓冲区&lt;/strong&gt;（默认 1MB，可通过 &lt;code&gt;repl-backlog-size&lt;/code&gt; 配置）；&lt;/li&gt;
&lt;li&gt;仅存储最近的写命令，用于增量同步，缓冲区越大，支持的从节点失联时间越长；&lt;/li&gt;
&lt;li&gt;实战建议：根据业务写 QPS 调整大小（如写 QPS=10 万 /s，需支持 10 秒失联，则配置 &lt;code&gt;repl-backlog-size 1GB&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 异步复制的 “数据延迟” 问题&lt;a href=&quot;#2-异步复制的-数据延迟-问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;主节点写操作完成后，异步将命令发送给从节点，因此从节点数据会比主节点 “慢一拍”（延迟通常在毫秒级）；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;实战优化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;减少主从节点的网络延迟（同机房部署）；&lt;/li&gt;
&lt;li&gt;避免主节点执行大命令（如 &lt;code&gt;KEYS *&lt;/code&gt;），导致同步阻塞；&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;info replication&lt;/code&gt; 查看 &lt;code&gt;slave_repl_offset&lt;/code&gt; 和 &lt;code&gt;master_repl_offset&lt;/code&gt;，监控同步延迟。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 主节点 BGSAVE 阻塞问题&lt;a href=&quot;#3-主节点-bgsave-阻塞问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;全量同步时，主节点执行 &lt;code&gt;BGSAVE&lt;/code&gt; 生成 RDB 文件，若数据量过大（如 10GB），&lt;code&gt;BGSAVE&lt;/code&gt; 会消耗大量 CPU / 磁盘 IO，可能导致主节点短暂卡顿；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;实战优化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主节点配置 &lt;code&gt;rdbcompression yes&lt;/code&gt;（压缩 RDB 文件，减少传输时间）；&lt;/li&gt;
&lt;li&gt;避开业务高峰期进行首次全量同步；&lt;/li&gt;
&lt;li&gt;级联复制（从节点挂载到从节点上，减少主节点的同步压力）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;4. 从节点加载 RDB 的阻塞问题&lt;a href=&quot;#4-从节点加载-rdb-的阻塞问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;从节点加载 RDB 文件时会阻塞所有读请求，若 RDB 文件过大，阻塞时间会很长；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;实战优化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从节点配置 &lt;code&gt;repl-disable-tcp-nodelay no&lt;/code&gt;（开启 TCP_NODELAY，减少同步延迟）；&lt;/li&gt;
&lt;li&gt;从节点使用固态硬盘（SSD），提升 RDB 加载速度；&lt;/li&gt;
&lt;li&gt;分批启动从节点，避免多个从节点同时请求全量同步，压垮主节点。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;5. 主从切换的注意事项&lt;a href=&quot;#5-主从切换的注意事项&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;主节点宕机后，从节点可通过 &lt;code&gt;replicaof no one&lt;/code&gt; 升级为主节点；&lt;/li&gt;
&lt;li&gt;新主节点的 runid 会变化，其他从节点重新连接时会触发全量同步，需提前配置足够大的复制积压缓冲区。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;四、Redis 主从复制 vs 哨兵模式 vs Cluster 模式&lt;a href=&quot;#四redis-主从复制-vs-哨兵模式-vs-cluster-模式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;




























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;模式&lt;/th&gt;&lt;th&gt;核心能力&lt;/th&gt;&lt;th&gt;同步方式&lt;/th&gt;&lt;th&gt;适用场景&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;主从复制&lt;/td&gt;&lt;td&gt;读写分离、数据备份&lt;/td&gt;&lt;td&gt;全量 + 增量同步&lt;/td&gt;&lt;td&gt;低并发、简单高可用需求&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;哨兵模式&lt;/td&gt;&lt;td&gt;主从复制 + 自动故障转移&lt;/td&gt;&lt;td&gt;基于主从复制的同步&lt;/td&gt;&lt;td&gt;中并发、自动容灾需求&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 模式&lt;/td&gt;&lt;td&gt;分片 + 主从 + 自动故障转移&lt;/td&gt;&lt;td&gt;分片内主从同步&lt;/td&gt;&lt;td&gt;高并发、大数据量、水平扩展&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;总结&lt;a href=&quot;#总结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;主从复制核心原理&lt;/strong&gt;：主节点异步同步数据到从节点，通过 runid 识别主节点身份，通过 offset 保证数据同步一致性，复制积压缓冲区优化增量同步效率；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;数据同步流程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;全量同步：初次连接 / 异常恢复时，主节点生成 RDB 文件 + 传输缓冲区命令，从节点加载 RDB 并执行命令；&lt;/li&gt;
&lt;li&gt;增量同步：短暂失联后，主节点从复制积压缓冲区发送失联期间的命令，从节点执行完成同步；&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;关键注意事项&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;复制积压缓冲区大小需适配业务场景，避免增量同步降级为全量同步；&lt;/li&gt;
&lt;li&gt;关注主节点 BGSAVE 和从节点加载 RDB 的阻塞问题；&lt;/li&gt;
&lt;li&gt;监控同步延迟，保证读写分离场景下的数据一致性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;关键点：Redis 主从复制的核心是 “异步、高效、容错”—— 全量同步解决 “初始数据一致” 问题，增量同步解决 “运行时数据一致” 问题，而复制积压缓冲区则是连接两者的关键，实战中需重点优化缓冲区大小、网络延迟、磁盘 IO 等环节。&lt;/p&gt;&lt;section&gt;&lt;h5&gt;一、Redis 过期键的三种核心删除策略&lt;a href=&quot;#一redis-过期键的三种核心删除策略&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;Redis 中设置过期时间的键（如 &lt;code&gt;SET key val EX 60&lt;/code&gt;），核心有三种理论删除策略，每种策略的设计思路和优缺点截然不同：&lt;/p&gt;




























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;策略类型&lt;/th&gt;&lt;th&gt;核心原理&lt;/th&gt;&lt;th&gt;优点&lt;/th&gt;&lt;th&gt;缺点&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;定时删除&lt;/td&gt;&lt;td&gt;在设置键过期时间的同时，创建定时器（timer），到期后立即删除该键&lt;/td&gt;&lt;td&gt;过期键立即清理，内存干净&lt;/td&gt;&lt;td&gt;1. 大量定时器占用 CPU；2. 高并发下性能差&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;惰性删除&lt;/td&gt;&lt;td&gt;不主动删除过期键，仅在&lt;strong&gt;访问该键时&lt;/strong&gt;检查是否过期，过期则删除并返回 null&lt;/td&gt;&lt;td&gt;节省 CPU，仅在必要时删除&lt;/td&gt;&lt;td&gt;过期键长期占用内存，可能导致内存泄漏&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;定期删除&lt;/td&gt;&lt;td&gt;每隔一段时间（如 100ms），主动扫描部分过期键并删除，控制扫描频率和时长&lt;/td&gt;&lt;td&gt;平衡 CPU 和内存消耗&lt;/td&gt;&lt;td&gt;过期键可能延迟删除，仍有少量内存浪费&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;1. 定时删除（Timed Deletion）&lt;a href=&quot;#1-定时删除timed-deletion&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;执行逻辑&lt;/p&gt;
&lt;p&gt;当你给键设置&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;EX 60&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;时，Redis 会创建一个定时器，60 秒后定时器触发，Redis 立即删除该键。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;核心问题&lt;/p&gt;
&lt;p&gt;若 Redis 中有 10 万个过期键，就需要维护 10 万个定时器，定时器的创建、管理会占用大量 CPU 资源；高并发场景下，CPU 会被定时器调度占满，导致 Redis 处理业务请求的能力大幅下降。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 惰性删除（Lazy Deletion）&lt;a href=&quot;#2-惰性删除lazy-deletion&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;执行逻辑&lt;/p&gt;
&lt;p&gt;过期键不会被主动删除，只有当你执行&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;GET key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;时，Redis 才会检查该键是否过期：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;未过期：正常返回值；&lt;/li&gt;
&lt;li&gt;已过期：删除该键，返回 &lt;code&gt;(nil)&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;核心优势&lt;/p&gt;
&lt;p&gt;完全不消耗 CPU 资源在 “提前删除” 上，仅在用户访问时做检查，是 “按需处理” 的思路。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;核心问题&lt;/p&gt;
&lt;p&gt;若一个过期键长期不被访问，会一直占用内存（比如设置了&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;EX 60&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;的键，60 天后才被访问），极端情况下会导致 Redis 内存耗尽。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 定期删除（Periodic Deletion）&lt;a href=&quot;#3-定期删除periodic-deletion&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;执行逻辑&lt;/p&gt;
&lt;p&gt;Redis 会启动一个后台线程，每隔固定时间（默认 100ms，可配置）执行一次 “过期键扫描”：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;随机选取一定数量（如 20 个）的带过期时间的键；&lt;/li&gt;
&lt;li&gt;检查这些键是否过期，删除已过期的；&lt;/li&gt;
&lt;li&gt;若过期键占比超过 25%，则重复步骤 1-2（避免单次扫描耗时过久）；&lt;/li&gt;
&lt;li&gt;单次扫描有时间上限（如 25ms），避免阻塞主线程。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;核心优势&lt;/p&gt;
&lt;p&gt;既不会像定时删除那样消耗大量 CPU，也不会像惰性删除那样导致内存泄漏，是 “折中方案”。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;核心问题&lt;/p&gt;
&lt;p&gt;键的删除存在延迟（比如 100ms 后才被扫描到），但延迟可控，且不会长期占用内存。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、为什么 Redis 不单独采用定时删除？&lt;a href=&quot;#二为什么-redis-不单独采用定时删除&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Redis 作为高性能的内存数据库，&lt;strong&gt;CPU 资源是核心瓶颈&lt;/strong&gt;，定时删除的设计完全违背了 Redis 的性能优先原则，具体原因有三：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;1. 定时器的 CPU 开销不可接受&lt;a href=&quot;#1-定时器的-cpu-开销不可接受&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;Redis 是单线程（主线程处理业务请求）+ 多后台线程（处理慢操作）架构，定时删除的定时器若由主线程管理，会阻塞业务请求；若由后台线程管理，大量定时器的调度、触发会占用后台线程的全部资源。&lt;/li&gt;
&lt;li&gt;举例：若有 10 万个过期键，每个定时器的创建、销毁、触发都需要 CPU 计算，Redis 的 QPS 会从 10 万 + 骤降，失去高性能优势。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 高并发场景下的性能抖动&lt;a href=&quot;#2-高并发场景下的性能抖动&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;定时删除的定时器触发是 “突发式” 的：比如某一时刻有 1 万个定时器同时触发，Redis 会集中删除这 1 万个键，导致该时间段内业务请求响应超时，出现性能抖动。&lt;/li&gt;
&lt;li&gt;而定期删除是 “匀速扫描”，单次扫描有时间上限，能保证 Redis 的响应延迟稳定。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 无实际必要（混合策略已足够）&lt;a href=&quot;#3-无实际必要混合策略已足够&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;Redis 的核心诉求是 “高性能”，而非 “绝对实时的过期键删除”—— 即使过期键延迟几秒删除，只要内存不泄漏，对大部分业务无影响。&lt;/li&gt;
&lt;li&gt;定时删除的 “实时性” 收益，远低于其 CPU 开销的成本，属于 “过度设计”。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;三、Redis 实际的过期键删除策略：混合策略（惰性删除 + 定期删除）&lt;a href=&quot;#三redis-实际的过期键删除策略混合策略惰性删除--定期删除&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Redis 没有单独采用某一种策略，而是结合了&lt;strong&gt;惰性删除 + 定期删除&lt;/strong&gt;，既保证性能，又避免内存泄漏：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;混合策略的执行流程&lt;a href=&quot;#混合策略的执行流程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Redis 运行中&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;客户端访问键&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;键是否过期？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;惰性删除：删除该键，返回nil&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回键值&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;后台线程定期扫描（每100ms）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;随机选取20个带过期时间的键&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;键是否过期？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;删除过期键&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;跳过&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;过期键占比&amp;gt;25%？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;结束本次扫描&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;内存达到maxmemory阈值&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;触发内存淘汰策略&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;淘汰部分键（含过期键）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;graph TD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A[Redis 运行中] --&amp;gt; B[客户端访问键]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;B --&amp;gt; C{键是否过期？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C -- 是 --&amp;gt; D[惰性删除：删除该键，返回nil]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C -- 否 --&amp;gt; E[返回键值]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A --&amp;gt; F[后台线程定期扫描（每100ms）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;F --&amp;gt; G[随机选取20个带过期时间的键]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;G --&amp;gt; H{键是否过期？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;H -- 是 --&amp;gt; I[删除过期键]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;H -- 否 --&amp;gt; J[跳过]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;G --&amp;gt; K{过期键占比&amp;gt;25%？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;K -- 是 --&amp;gt; G&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;K -- 否 --&amp;gt; L[结束本次扫描]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A --&amp;gt; M[内存达到maxmemory阈值]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;M --&amp;gt; N[触发内存淘汰策略]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;N --&amp;gt; O[淘汰部分键（含过期键）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;是&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;否&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Redis 运行中&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;客户端访问键&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;键是否过期？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;惰性删除：删除该键，返回nil&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;返回键值&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;后台线程定期扫描（每100ms）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;随机选取20个带过期时间的键&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;键是否过期？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;删除过期键&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;跳过&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;过期键占比&amp;gt;25%？&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;结束本次扫描&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;内存达到maxmemory阈值&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;触发内存淘汰策略&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;淘汰部分键（含过期键）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;补充：内存淘汰策略（最终兜底）&lt;a href=&quot;#补充内存淘汰策略最终兜底&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;即使有了惰性 + 定期删除，若过期键仍未被清理且 Redis 内存达到 &lt;code&gt;maxmemory&lt;/code&gt; 阈值，Redis 会触发&lt;strong&gt;内存淘汰策略&lt;/strong&gt;（如 &lt;code&gt;volatile-lru&lt;/code&gt;：淘汰最近最少使用的过期键），进一步保证内存不溢出。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;四、过期键删除的额外注意事项&lt;a href=&quot;#四过期键删除的额外注意事项&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;过期键的持久化影响&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RDB：生成 RDB 文件时，会跳过过期键；加载 RDB 文件时，主库会跳过过期键，从库会保留（主从同步后再删除）。&lt;/li&gt;
&lt;li&gt;AOF：过期键被删除时，会往 AOF 文件写入 &lt;code&gt;DEL key&lt;/code&gt; 命令；AOF 重写时，会跳过过期键。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;主从同步的过期键处理&lt;/p&gt;
&lt;p&gt;过期键的删除操作由主库执行，主库删除后会向从库发送&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;DEL key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;命令，从库被动删除（从库不主动执行过期扫描），保证主从数据一致。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;过期时间的精度&lt;/p&gt;
&lt;p&gt;Redis 的过期时间精度是毫秒级，但定期删除的扫描间隔是 100ms，因此过期键的实际删除时间可能有 ±100ms 的误差，属于正常现象。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;总结&lt;a href=&quot;#总结-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;核心删除策略&lt;/strong&gt;：Redis 有定时删除、惰性删除、定期删除三种理论策略，实际采用 “惰性删除 + 定期删除” 的混合策略；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不采用定时删除的原因&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大量定时器占用 CPU 资源，导致性能下降；&lt;/li&gt;
&lt;li&gt;高并发下易出现性能抖动；&lt;/li&gt;
&lt;li&gt;实时性收益远低于成本，无实际必要；&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;混合策略的优势&lt;/p&gt;
&lt;p&gt;惰性删除保证 CPU 不被浪费，定期删除避免内存泄漏，内存淘汰策略作为最终兜底，平衡了性能和内存管理。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;关键点：Redis 设计过期键删除策略的核心原则是 “性能优先，兼顾内存”—— 宁可让过期键延迟删除，也不允许 CPU 资源被过度消耗，这是 Redis 高性能的核心设计思路之一。&lt;/p&gt;&lt;section&gt;&lt;h5&gt;Redis 如何实现分布式锁？需要注意哪些问题（原子性、死锁、误删）？&lt;a href=&quot;#redis-如何实现分布式锁需要注意哪些问题原子性死锁误删&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;你想知道 Redis 实现分布式锁的核心方法，以及在实现过程中需要解决的原子性、死锁、误删等关键问题 —— 分布式锁是解决分布式系统中资源竞争的核心方案，Redis 因其高性能和易用性成为最主流的实现方式，我会从&lt;strong&gt;核心实现原理、关键问题解决方案、完整代码示例、注意事项&lt;/strong&gt;四个维度，讲透 Redis 分布式锁的落地细节。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;一、Redis 分布式锁的核心实现原理&lt;a href=&quot;#一redis-分布式锁的核心实现原理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;分布式锁的核心要求是：&lt;strong&gt;在分布式集群中，同一时刻只有一个客户端能获取到锁&lt;/strong&gt;。Redis 实现分布式锁的核心是利用 &lt;code&gt;SETNX&lt;/code&gt;（SET if Not Exists）命令的原子性 —— 只有当锁 Key 不存在时，才能设置成功（获取锁），否则设置失败（锁已被占用）。&lt;/p&gt;&lt;section&gt;&lt;h4&gt;基础命令解析&lt;a href=&quot;#基础命令解析&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;命令&lt;/th&gt;&lt;th&gt;作用&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;SETNX lock_key val&lt;/code&gt;&lt;/td&gt;&lt;td&gt;原子操作：若 &lt;code&gt;lock_key&lt;/code&gt; 不存在，设置值并返回 1（获取锁成功）；否则返回 0（失败）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;DEL lock_key&lt;/code&gt;&lt;/td&gt;&lt;td&gt;删除锁 Key（释放锁）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;EXPIRE lock_key ttl&lt;/code&gt;&lt;/td&gt;&lt;td&gt;为锁 Key 设置过期时间（避免死锁）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、Redis 分布式锁的实现步骤（从基础到完善）&lt;a href=&quot;#二redis-分布式锁的实现步骤从基础到完善&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;步骤 1：基础版实现（存在死锁 / 误删问题）&lt;a href=&quot;#步骤-1基础版实现存在死锁--误删问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 客户端A获取锁（成功）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;127.0.0.1:6379&amp;gt; SETNX order_lock 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;(integer) 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 客户端B获取锁（失败，锁已被占用）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;127.0.0.1:6379&amp;gt; SETNX order_lock 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;(integer) 0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 客户端A释放锁&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;127.0.0.1:6379&amp;gt; DEL order_lock&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;(integer) 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;死锁：若客户端 A 获取锁后宕机，未执行 &lt;code&gt;DEL&lt;/code&gt;，锁 Key 会永久存在，其他客户端无法获取；&lt;/li&gt;
&lt;li&gt;无过期时间：即使未宕机，业务执行超时也会导致锁无法释放。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 2：优化版（添加过期时间，解决死锁）&lt;a href=&quot;#步骤-2优化版添加过期时间解决死锁&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Redis 2.6.12+ 支持 &lt;code&gt;SET&lt;/code&gt; 命令整合 &lt;code&gt;NX&lt;/code&gt;（等价 SETNX）和 &lt;code&gt;EX&lt;/code&gt;（过期时间），&lt;strong&gt;原子性设置锁 + 过期时间&lt;/strong&gt;（解决基础版 “SETNX+EXPIRE” 非原子的问题）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 原子操作：设置锁Key，值为1，过期时间30秒，仅当Key不存在时设置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;127.0.0.1:6379&amp;gt; SET order_lock 1 NX EX 30&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;OK&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 释放锁&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;127.0.0.1:6379&amp;gt; DEL order_lock&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;(integer) 1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;核心改进&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;原子性：&lt;code&gt;SET key val NX EX ttl&lt;/code&gt; 是单条命令，避免 “SETNX 成功但 EXPIRE 失败” 导致的死锁；&lt;/li&gt;
&lt;li&gt;过期时间：30 秒后锁自动释放，即使客户端宕机也不会永久占用锁。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;仍存在问题&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;误删锁：客户端 A 的锁因超时自动释放，客户端 B 获取锁，此时 A 业务执行完成，执行 &lt;code&gt;DEL&lt;/code&gt; 会误删客户端 B 的锁；&lt;/li&gt;
&lt;li&gt;过期时间难设置：过期时间太短，业务未执行完锁就释放；太长，锁释放延迟。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;步骤 3：最终版（解决误删 + 自动续期）&lt;a href=&quot;#步骤-3最终版解决误删--自动续期&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;section&gt;&lt;h5&gt;核心优化点：&lt;a href=&quot;#核心优化点&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;锁值唯一化&lt;/strong&gt;：用 UUID + 客户端 ID 作为锁值，释放锁时先校验值是否为自己的，再删除（避免误删）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lua 脚本释放锁&lt;/strong&gt;：校验值 + 删除锁是原子操作（避免校验后锁过期，删除别人的锁）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自动续期（看门狗）&lt;/strong&gt;：业务执行超时前，自动延长锁的过期时间（避免锁提前释放）。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;完整 Lua 释放锁脚本（原子校验 + 删除）&lt;a href=&quot;#完整-lua-释放锁脚本原子校验--删除&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;-- 入参：KEYS[1]=锁Key，ARGV[1]=客户端持有的锁值&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if redis.call(&apos;get&apos;, KEYS[1]) == ARGV[1] then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return redis.call(&apos;del&apos;, KEYS[1])  -- 校验通过，删除锁&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return 0  -- 校验失败，不删除&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;三、Java 代码实现（完整可落地）&lt;a href=&quot;#三java-代码实现完整可落地&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;1. 核心工具类（解决原子性、死锁、误删）&lt;a href=&quot;#1-核心工具类解决原子性死锁误删&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;org.springframework.data.redis.core.RedisTemplate&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;org.springframework.data.redis.core.script.DefaultRedisScript&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;org.springframework.stereotype.Component&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;javax.annotation.Resource&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;java.util.Collections&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;java.util.UUID&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;java.util.concurrent.TimeUnit&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;java.util.concurrent.locks.Condition&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;java.util.concurrent.locks.Lock&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Component&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RedisDistributedLock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;implements&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Lock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Resource&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RedisTemplate&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; redisTemplate&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 锁Key前缀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; LOCK_PREFIX &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;dist_lock:&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 锁默认过期时间（秒）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt; DEFAULT_TTL &lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 客户端唯一标识（避免误删）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;final&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; lockValue&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 当前锁Key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; lockKey&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RedisDistributedLock&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// 生成唯一值（UUID+线程ID）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lockValue&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;UUID&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;randomUUID&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;-&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Thread&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;currentThread&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;getId&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 获取锁（阻塞式，直到获取成功）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lock&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;/span&gt;&lt;span&gt;tryLock&lt;/span&gt;&lt;span&gt;()) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;// 自旋等待，可优化为阻塞（如Thread.sleep(100)）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;Thread&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 尝试获取锁（非阻塞）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@return&lt;/span&gt;&lt;span&gt; true-成功，false-失败&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;47&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tryLock&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;48&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tryLock&lt;/span&gt;&lt;span&gt;(DEFAULT_TTL, &lt;/span&gt;&lt;span&gt;TimeUnit&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;SECONDS&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;49&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;50&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;51&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;52&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 尝试获取锁（带超时时间）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;53&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;54&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;55&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tryLock&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;time&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TimeUnit&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unit&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;56&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ttl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;unit&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;toSeconds&lt;/span&gt;&lt;span&gt;(time);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;57&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// 原子操作：设置锁Key + 唯一值 + 过期时间&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;58&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;Boolean&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;success&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redisTemplate&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;opsForValue&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;setIfAbsent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;59&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lockKey, lockValue, ttl, &lt;/span&gt;&lt;span&gt;TimeUnit&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;SECONDS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;60&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;61&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// 开启看门狗（自动续期）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;62&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;Boolean&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;TRUE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;equals&lt;/span&gt;&lt;span&gt;(success)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;63&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;startWatchDog&lt;/span&gt;&lt;span&gt;(ttl);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;64&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;65&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Boolean&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;TRUE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;equals&lt;/span&gt;&lt;span&gt;(success);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;66&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;67&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;68&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;69&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 释放锁（原子校验+删除）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;70&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;71&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;72&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unlock&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;73&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// Lua脚本：校验锁值是否为当前客户端的，是则删除&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;74&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;if redis.call(&apos;get&apos;, KEYS[1]) == ARGV[1] then return redis.call(&apos;del&apos;, KEYS[1]) else return 0 end&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;75&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;DefaultRedisScript&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Long&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;redisScript&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DefaultRedisScript&lt;/span&gt;&lt;span&gt;&amp;lt;&amp;gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;76&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;redisScript&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setScriptText&lt;/span&gt;&lt;span&gt;(script);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;77&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;redisScript&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setResultType&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Long&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;78&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;79&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// 执行脚本&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;80&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;Long&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redisTemplate&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;81&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redisScript,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;82&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;Collections&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;singletonList&lt;/span&gt;&lt;span&gt;(lockKey),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;83&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lockValue&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;84&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;85&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;86&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// 释放看门狗&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;87&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;stopWatchDog&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;88&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;89&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;90&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;91&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 看门狗：每隔10秒续期一次，延长锁过期时间&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;92&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;93&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;startWatchDog&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;long&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ttl&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;94&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// 用线程池执行续期任务（避免创建过多线程）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;95&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Thread&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;96&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;97&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;98&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;// 续期间隔：过期时间的1/3（如30秒→10秒）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;99&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;Thread&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sleep&lt;/span&gt;&lt;span&gt;&lt;span&gt;(ttl &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;100&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;// 原子续期：仅当锁存在且值匹配时，重置过期时间&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;101&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;renewScript&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;if redis.call(&apos;get&apos;, KEYS[1]) == ARGV[1] then return redis.call(&apos;expire&apos;, KEYS[1], ARGV[2]) else return 0 end&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;102&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;DefaultRedisScript&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Long&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DefaultRedisScript&lt;/span&gt;&lt;span&gt;&amp;lt;&amp;gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;103&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setScriptText&lt;/span&gt;&lt;span&gt;(renewScript);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;104&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setResultType&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Long&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;105&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;106&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;Long&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redisTemplate&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;107&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;script,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;108&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                            &lt;/span&gt;&lt;span&gt;Collections&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;singletonList&lt;/span&gt;&lt;span&gt;(lockKey),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;109&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lockValue,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;110&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                            &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;valueOf&lt;/span&gt;&lt;span&gt;(ttl)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;111&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;112&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;113&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;// 续期失败（锁已释放/过期），退出循环&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;114&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (result &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;115&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                        &lt;/span&gt;&lt;span&gt;break&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;116&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;117&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;InterruptedException&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;118&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;Thread&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;currentThread&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;interrupt&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;119&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;break&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;120&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;121&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;122&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}).&lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;123&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;124&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;125&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stopWatchDog&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;126&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// 实际开发中需用线程池管理续期线程，此处简化为标记退出&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;127&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;128&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;129&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 以下为Lock接口默认实现，无需关注&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;130&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;131&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lockInterruptibly&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;throws&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;InterruptedException&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;132&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Override&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;133&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Condition&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;newCondition&lt;/span&gt;&lt;span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;134&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 使用示例（扣库存场景）&lt;a href=&quot;#2-使用示例扣库存场景&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StockService&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Resource&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StockMapper&lt;/span&gt;&lt;span&gt; stockMapper&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;Resource&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RedisDistributedLock&lt;/span&gt;&lt;span&gt; redisLock&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;deductStock&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Long&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;productId&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// 设置锁Key（按商品ID粒度，避免全局锁）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;redisLock&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setLockKey&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;dist_lock:stock:&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; productId);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;// 获取锁&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;redisLock&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lock&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;// 执行业务：扣库存&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;Stock&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stockMapper&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;selectById&lt;/span&gt;&lt;span&gt;(productId);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;stock&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getNum&lt;/span&gt;&lt;span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;stockMapper&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;deductNum&lt;/span&gt;&lt;span&gt;(productId);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;库存扣减成功，剩余：&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;&lt;span&gt;stock&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getNum&lt;/span&gt;&lt;span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;库存不足&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;finally&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;// 释放锁（必须在finally中，避免业务异常导致锁未释放）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;redisLock&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;unlock&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;四、实现分布式锁需注意的核心问题&lt;a href=&quot;#四实现分布式锁需注意的核心问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;1. 原子性问题（最核心）&lt;a href=&quot;#1-原子性问题最核心&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题场景&lt;/strong&gt;：基础版 “SETNX + EXPIRE” 是两条命令，若 SETNX 成功后，EXPIRE 执行前客户端宕机，锁 Key 无过期时间，导致死锁；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解决方案&lt;/strong&gt;：使用 &lt;code&gt;SET key val NX EX ttl&lt;/code&gt; 原子命令，或 Lua 脚本保证多操作原子性（如释放锁时 “校验值 + 删除”）。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 死锁问题&lt;a href=&quot;#2-死锁问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;问题场景&lt;/strong&gt;：客户端获取锁后宕机 / 网络中断，未释放锁，其他客户端无法获取；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;解决方案&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;为锁 Key 设置合理的过期时间（如 30 秒），即使未手动释放，也会自动过期；&lt;/li&gt;
&lt;li&gt;看门狗机制：业务执行超时前，自动续期锁的过期时间（避免锁提前释放）。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 误删锁问题&lt;a href=&quot;#3-误删锁问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;问题场景&lt;/strong&gt;：客户端 A 的锁因超时释放，客户端 B 获取锁，A 业务执行完成后执行 DEL，误删 B 的锁；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;解决方案&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;锁值唯一化：用 UUID + 客户端 ID 作为锁值，而非固定值；&lt;/li&gt;
&lt;li&gt;原子释放锁：通过 Lua 脚本先校验锁值是否为自己的，再删除（避免 “校验 + 删除” 非原子）。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;4. 锁粒度问题&lt;a href=&quot;#4-锁粒度问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题场景&lt;/strong&gt;：使用全局锁（如 &lt;code&gt;lock:all&lt;/code&gt;），所有业务竞争同一把锁，导致并发性能极低；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解决方案&lt;/strong&gt;：按业务维度细化锁粒度（如商品 ID、订单 ID），如 &lt;code&gt;lock:stock:1001&lt;/code&gt;（仅锁定 1001 号商品的库存）。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;5. Redis 集群脑裂问题&lt;a href=&quot;#5-redis-集群脑裂问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;问题场景&lt;/strong&gt;：Redis 主从集群脑裂，主节点宕机后，从节点升级为主节点，但原主节点仍在处理请求，导致多个客户端获取到锁；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;解决方案&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用 Redis Redlock 算法（多实例部署，需过半节点获取锁才算成功）；&lt;/li&gt;
&lt;li&gt;配置 Redis 主从的 &lt;code&gt;min-replicas-to-write&lt;/code&gt; 参数，减少脑裂概率。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;6. 重入性问题&lt;a href=&quot;#6-重入性问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;问题场景&lt;/strong&gt;：同一客户端获取锁后，再次请求获取锁时失败（非重入锁）；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;解决方案&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;锁值存储 “客户端 ID + 重入次数”，获取锁时若为自己的锁，重入次数 + 1；&lt;/li&gt;
&lt;li&gt;释放锁时重入次数 - 1，仅当次数为 0 时删除锁 Key。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;五、Redis 分布式锁 vs Zookeeper 分布式锁&lt;a href=&quot;#五redis-分布式锁-vs-zookeeper-分布式锁&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;







































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;特性&lt;/th&gt;&lt;th&gt;Redis 分布式锁&lt;/th&gt;&lt;th&gt;Zookeeper 分布式锁&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;性能&lt;/td&gt;&lt;td&gt;高（内存操作，无磁盘 IO）&lt;/td&gt;&lt;td&gt;中（需创建临时节点，有网络开销）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;可靠性&lt;/td&gt;&lt;td&gt;中（集群脑裂可能导致多锁）&lt;/td&gt;&lt;td&gt;高（临时节点 + Watcher 机制）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;实现复杂度&lt;/td&gt;&lt;td&gt;低（命令 / 脚本简单）&lt;/td&gt;&lt;td&gt;中（需处理 Watcher 事件）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;重入性&lt;/td&gt;&lt;td&gt;需自定义实现&lt;/td&gt;&lt;td&gt;原生支持&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;阻塞获取锁&lt;/td&gt;&lt;td&gt;需自旋实现&lt;/td&gt;&lt;td&gt;原生支持（Watcher 通知）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;适用场景&lt;/td&gt;&lt;td&gt;高并发、短持有时间（如库存扣减）&lt;/td&gt;&lt;td&gt;低并发、长持有时间（如分布式任务调度）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;总结&lt;a href=&quot;#总结-2&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Redis 分布式锁核心实现&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原子获取锁：&lt;code&gt;SET lock_key 唯一值 NX EX ttl&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;原子释放锁：Lua 脚本校验锁值 + 删除；&lt;/li&gt;
&lt;li&gt;防死锁：过期时间 + 看门狗续期；&lt;/li&gt;
&lt;li&gt;防误删：锁值唯一化。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;核心注意事项&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;必须保证操作原子性（避免多命令拆分）；&lt;/li&gt;
&lt;li&gt;锁 Key 需设置过期时间，避免死锁；&lt;/li&gt;
&lt;li&gt;释放锁前校验锁值，避免误删；&lt;/li&gt;
&lt;li&gt;细化锁粒度，提升并发性能。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;选型建议&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;高并发短锁场景选 Redis（性能优先）；&lt;/li&gt;
&lt;li&gt;高可靠长锁场景选 Zookeeper（一致性优先）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;关键点：Redis 分布式锁的核心是 “原子性” 和 “容错性”—— 所有操作必须保证原子，同时通过过期时间、看门狗、唯一锁值等机制，应对宕机、超时、并发等异常场景。&lt;/p&gt;&lt;section&gt;&lt;h5&gt;一、先理解：缓存与数据库双写不一致的核心成因&lt;a href=&quot;#一先理解缓存与数据库双写不一致的核心成因&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;缓存和数据库双写的核心矛盾是：&lt;strong&gt;两个独立存储的写操作无法原子化&lt;/strong&gt;，常见不一致场景有 2 类：&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;场景 1：更新操作顺序错误（并发更新）&lt;a href=&quot;#场景-1更新操作顺序错误并发更新&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;假设存在两个请求同时更新同一数据：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;请求 A：更新数据库 → 删除缓存（正常流程）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求 B：查询缓存（未命中）→ 查询数据库（旧值）→ 写入缓存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;时序异常：A 更新数据库 → B 查询数据库（旧值）→ A 删除缓存 → B 写入缓存（旧值）&lt;/p&gt;
&lt;p&gt;→ 最终缓存是旧值，数据库是新值，不一致。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;场景 2：删除 / 更新缓存失败（网络 / 宕机）&lt;a href=&quot;#场景-2删除--更新缓存失败网络--宕机&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;请求 A：更新数据库成功 → 删除缓存时网络超时 / Redis 宕机 → 缓存未删除&lt;/p&gt;
&lt;p&gt;→ 后续查询会读取缓存中的旧值，不一致。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;场景 3：并发读写&lt;a href=&quot;#场景-3并发读写&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;请求 A：读取缓存（未命中）→ 查询数据库（旧值）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求 B：更新数据库（新值）→ 删除缓存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;时序异常：A 查询数据库（旧值）→ B 更新数据库 + 删除缓存 → A 写入缓存（旧值）&lt;/p&gt;
&lt;p&gt;→ 缓存存旧值，数据库存新值。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;二、核心解决方案（按落地难度 / 适用场景分类）&lt;a href=&quot;#二核心解决方案按落地难度--适用场景分类&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;方案 1：延时双删（最简单，适配大部分场景）&lt;a href=&quot;#方案-1延时双删最简单适配大部分场景&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;section&gt;&lt;h5&gt;原理&lt;a href=&quot;#原理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;更新数据库后，先删除一次缓存，等待一段时间（如 500ms），再删除一次缓存 —— 目的是覆盖 “并发读写导致的旧值写入缓存” 场景。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;完整流程&lt;a href=&quot;#完整流程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;收到更新请求&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;更新数据库&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;第一次删除缓存&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;延时N毫秒（如500ms）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;第二次删除缓存&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;操作完成&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;graph TD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A[收到更新请求] --&amp;gt; B[更新数据库]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;B --&amp;gt; C[第一次删除缓存]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C --&amp;gt; D[延时N毫秒（如500ms）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;D --&amp;gt; E[第二次删除缓存]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E --&amp;gt; F[操作完成]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;收到更新请求&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;更新数据库&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;第一次删除缓存&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;延时N毫秒（如500ms）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;第二次删除缓存&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;操作完成&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;实操代码（Java 示例）&lt;a href=&quot;#实操代码java-示例&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@Service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public class UserService {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Resource&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private UserMapper userMapper;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@Resource&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private StringRedisTemplate redisTemplate;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 延时时间（根据业务QPS调整，建议500ms~1s）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;private static final long DELAY_TIME = 500;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;public void updateUser(User user) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 1. 更新数据库&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;userMapper.updateById(user);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String cacheKey = &quot;user:&quot; + user.getId();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 2. 第一次删除缓存&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redisTemplate.delete(cacheKey);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 3. 延时后第二次删除（异步执行，避免阻塞主线程）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CompletableFuture.runAsync(() -&amp;gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;try {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Thread.sleep(DELAY_TIME);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redisTemplate.delete(cacheKey);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} catch (InterruptedException e) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Thread.currentThread().interrupt();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;关键细节&lt;a href=&quot;#关键细节&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ul&gt;
&lt;li&gt;延时时间：需大于 “一次查询缓存 + 查询数据库 + 写入缓存” 的耗时（一般 500ms 足够）；&lt;/li&gt;
&lt;li&gt;异步执行：第二次删除需异步（如线程池 / 消息队列），避免阻塞业务流程；&lt;/li&gt;
&lt;li&gt;适用场景：读多写少、一致性要求不极致（允许短暂不一致）的场景（如商品详情、用户信息）。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;方案 2：缓存更新 + 分布式锁（强一致性，适配高并发写）&lt;a href=&quot;#方案-2缓存更新--分布式锁强一致性适配高并发写&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;section&gt;&lt;h5&gt;原理&lt;a href=&quot;#原理-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;通过分布式锁（Redis/Zookeeper）保证 “更新数据库 + 更新缓存” 的原子性，同时避免并发读写导致的不一致。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;完整流程&lt;a href=&quot;#完整流程-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;graph TD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A[收到更新请求] --&amp;gt; B[获取分布式锁（key=user:{id}）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;B --&amp;gt; C{获取锁成功？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C -- 否 --&amp;gt; D[重试/返回失败]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C -- 是 --&amp;gt; E[更新数据库]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E --&amp;gt; F[更新缓存（设置过期时间）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;F --&amp;gt; G[释放分布式锁]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;G --&amp;gt; H[操作完成]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;I[收到查询请求] --&amp;gt; J[查询缓存（命中则返回）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;J --&amp;gt; K{缓存命中？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;K -- 是 --&amp;gt; L[返回数据]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;K -- 否 --&amp;gt; M[获取分布式锁]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;M --&amp;gt; N{获取锁成功？}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;N -- 否 --&amp;gt; O[重试查询缓存/返回降级数据]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;N -- 是 --&amp;gt; P[查询数据库]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;P --&amp;gt; Q[写入缓存（设置过期时间）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Q --&amp;gt; R[释放锁]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;R --&amp;gt; L&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;收到更新请求&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;B&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;实操代码（分布式锁 + 更新缓存）&lt;a href=&quot;#实操代码分布式锁--更新缓存&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public void updateUserWithLock(User user) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String lockKey = &quot;lock:user:&quot; + user.getId();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;String cacheKey = &quot;user:&quot; + user.getId();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 1. 获取分布式锁（超时时间30s，避免死锁）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, &quot;1&quot;, 30, TimeUnit.SECONDS);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if (Boolean.TRUE.equals(locked)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;try {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 2. 更新数据库&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;userMapper.updateById(user);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 3. 更新缓存（设置过期时间，兜底）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(user), 1, TimeUnit.HOURS);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} finally {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 4. 释放锁&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redisTemplate.delete(lockKey);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} else {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 获取锁失败，重试（或抛出异常）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;throw new RuntimeException(&quot;更新频繁，请稍后再试&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;显示更多&lt;/span&gt;&lt;span&gt;显示更少&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;关键细节&lt;a href=&quot;#关键细节-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ul&gt;
&lt;li&gt;锁粒度：按数据 ID 加锁（如 user:1001），避免全局锁影响性能；&lt;/li&gt;
&lt;li&gt;锁超时：必须设置锁超时，防止服务宕机导致死锁；&lt;/li&gt;
&lt;li&gt;适用场景：写并发高、一致性要求高的场景（如订单状态、库存）。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;方案 3：基于 binlog 的缓存更新（最终一致性，无侵入）&lt;a href=&quot;#方案-3基于-binlog-的缓存更新最终一致性无侵入&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;section&gt;&lt;h5&gt;原理&lt;a href=&quot;#原理-2&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;通过监听数据库 binlog（如 Canal 监听 MySQL binlog），异步更新 / 删除缓存，完全解耦业务代码和缓存操作。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;完整流程&lt;a href=&quot;#完整流程-2&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;查看代码&lt;/p&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;改/删&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;增&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;业务服务更新数据库&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;MySQL写入binlog&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Canal 监听binlog并解析&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;判断操作类型（增/删/改）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;操作类型&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;删除对应缓存&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;写入缓存&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;操作完成&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;graph TD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A[业务服务更新数据库] --&amp;gt; B[MySQL写入binlog]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;B --&amp;gt; C[Canal 监听binlog并解析]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C --&amp;gt; D[判断操作类型（增/删/改）]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;D --&amp;gt; E{操作类型}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E -- 改/删 --&amp;gt; F[删除对应缓存]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;E -- 增 --&amp;gt; G[写入缓存]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;F --&amp;gt; H[操作完成]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;G --&amp;gt; H&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;改/删&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;增&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;业务服务更新数据库&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;MySQL写入binlog&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Canal 监听binlog并解析&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;判断操作类型（增/删/改）&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;操作类型&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;删除对应缓存&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;写入缓存&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;操作完成&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;关键细节&lt;a href=&quot;#关键细节-2&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ul&gt;
&lt;li&gt;无业务侵入：无需修改业务代码，仅通过中间件监听 binlog；&lt;/li&gt;
&lt;li&gt;最终一致性：binlog 解析和缓存更新是异步的，存在短暂不一致，但最终会一致；&lt;/li&gt;
&lt;li&gt;适用场景：大规模微服务、业务代码不想耦合缓存逻辑的场景。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;方案 4：分布式事务（极致一致性，适配金融级场景）&lt;a href=&quot;#方案-4分布式事务极致一致性适配金融级场景&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;section&gt;&lt;h5&gt;原理&lt;a href=&quot;#原理-3&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;p&gt;通过分布式事务（如 Seata TCC/2PC）保证 “数据库更新” 和 “缓存更新 / 删除” 的原子性 —— 要么都成功，要么都回滚。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;流程（以 TCC 为例）&lt;a href=&quot;#流程以-tcc-为例&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Try 阶段&lt;/strong&gt;：锁定数据库数据 + 标记缓存待更新；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Confirm 阶段&lt;/strong&gt;：提交数据库更新 + 删除 / 更新缓存；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cancel 阶段&lt;/strong&gt;：回滚数据库更新 + 恢复缓存旧值。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h5&gt;关键细节&lt;a href=&quot;#关键细节-3&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;&lt;ul&gt;
&lt;li&gt;实现复杂：需改造业务代码，开发成本高；&lt;/li&gt;
&lt;li&gt;性能损耗：分布式事务会增加网络开销，降低吞吐量；&lt;/li&gt;
&lt;li&gt;适用场景：金融级场景（如转账、支付），一致性要求极高。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;三、各方案对比与选型建议&lt;a href=&quot;#三各方案对比与选型建议&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;







































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;方案&lt;/th&gt;&lt;th&gt;一致性级别&lt;/th&gt;&lt;th&gt;实现难度&lt;/th&gt;&lt;th&gt;性能&lt;/th&gt;&lt;th&gt;适用场景&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;延时双删&lt;/td&gt;&lt;td&gt;最终一致（短暂不一致）&lt;/td&gt;&lt;td&gt;极低&lt;/td&gt;&lt;td&gt;高&lt;/td&gt;&lt;td&gt;读多写少、非核心数据（商品详情）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;缓存更新 + 分布式锁&lt;/td&gt;&lt;td&gt;强一致&lt;/td&gt;&lt;td&gt;中&lt;/td&gt;&lt;td&gt;中&lt;/td&gt;&lt;td&gt;高并发写、核心数据（库存、订单）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;binlog 监听&lt;/td&gt;&lt;td&gt;最终一致&lt;/td&gt;&lt;td&gt;中&lt;/td&gt;&lt;td&gt;高&lt;/td&gt;&lt;td&gt;大规模微服务、低侵入需求&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;分布式事务&lt;/td&gt;&lt;td&gt;强一致&lt;/td&gt;&lt;td&gt;高&lt;/td&gt;&lt;td&gt;低&lt;/td&gt;&lt;td&gt;金融级、极致一致性（支付、转账）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;四、通用保障措施（无论选哪种方案都要加）&lt;a href=&quot;#四通用保障措施无论选哪种方案都要加&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;1. 缓存设置过期时间（兜底）&lt;a href=&quot;#1-缓存设置过期时间兜底&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;所有缓存都必须设置过期时间（如 1 小时），即使出现不一致，过期后也会自动失效，重新从数据库加载最新值。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2. 读写分离场景的特殊处理&lt;a href=&quot;#2-读写分离场景的特殊处理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;若数据库是读写分离架构，需注意：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;更新操作走主库，查询操作若缓存未命中，需查主库（避免从库同步延迟导致的旧值）；&lt;/li&gt;
&lt;li&gt;或等待从库同步完成后再写入缓存。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;3. 避免缓存更新，优先删除缓存&lt;a href=&quot;#3-避免缓存更新优先删除缓存&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;“更新缓存” 易出现并发问题，建议优先选择 “删除缓存”—— 查询时缓存未命中，再从数据库加载最新值写入，减少不一致概率。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h3&gt;总结&lt;a href=&quot;#总结-3&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;核心矛盾&lt;/strong&gt;：缓存与数据库双写不一致的本质是 “两个独立存储的写操作无法原子化”，并发读写 / 操作失败是主要诱因；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;方案选型&lt;/p&gt;
&lt;p&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;简单场景选「延时双删」，成本最低；&lt;/li&gt;
&lt;li&gt;高并发写选「分布式锁 + 缓存更新」，兼顾一致性和性能；&lt;/li&gt;
&lt;li&gt;大规模微服务选「binlog 监听」，低侵入；&lt;/li&gt;
&lt;li&gt;金融级场景选「分布式事务」，极致一致；&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;通用兜底&lt;/strong&gt;：缓存设置过期时间、优先删除缓存而非更新，是避免长期不一致的关键。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;关键点：没有 “银弹” 方案，需根据业务的一致性要求、并发量、开发成本选择合适的方案，大部分业务场景下，延时双删 + 过期时间已足够。&lt;/p&gt;&lt;/section&gt;</content:encoded></item></channel></rss>