﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-JAVA</title><link>http://www.blogjava.net/zzzlyr/</link><description /><language>zh-cn</language><lastBuildDate>Sun, 03 May 2026 09:59:10 GMT</lastBuildDate><pubDate>Sun, 03 May 2026 09:59:10 GMT</pubDate><ttl>60</ttl><item><title>JDK21 CompletableFuture 链式多线程处理任务方式</title><link>http://www.blogjava.net/zzzlyr/articles/451709.html</link><dc:creator>为自己代言</dc:creator><author>为自己代言</author><pubDate>Mon, 08 Dec 2025 08:41:00 GMT</pubDate><guid>http://www.blogjava.net/zzzlyr/articles/451709.html</guid><wfw:comment>http://www.blogjava.net/zzzlyr/comments/451709.html</wfw:comment><comments>http://www.blogjava.net/zzzlyr/articles/451709.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zzzlyr/comments/commentRss/451709.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zzzlyr/services/trackbacks/451709.html</trackback:ping><description><![CDATA[springboot 3 web工程 多线程查询任务处理方式：<br /><br /><div style="background-color:#2b2b2b;color:#a9b7c6"><pre style="font-family:'JetBrains Mono',monospace;font-size:13.5pt;"><span style="color:#bbb529;">@Configuration<br /></span><span style="color:#cc7832;">public class </span>AsyncThreadPoolConfig {<br />    <span style="color:#808080;">// </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">获取</span><span style="color:#808080;"> CPU </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">核心数<br /></span> <span style="color:#cc7832;">private static final int </span><span style="color:#9876aa;font-style:italic;">CPU_CORES </span>= Runtime.<span style="font-style:italic;">getRuntime</span>().availableProcessors()<span style="color:#cc7832;">;<br /></span><span style="color:#cc7832;"><br /></span> <span style="color:#629755;font-style:italic;">/**<br /></span><span style="color:#629755;font-style:italic;">     * </span><span style="color:#629755;font-style:italic;font-family:'Menlo-Regular',monospace;">自定义异步线程池 springboot 推荐返回 </span>ThreadPoolTaskExecutor ，也可以返回JDK原生：Executor，ExcecutorService<br /><span style="color:#629755;font-style:italic;font-family:'Menlo-Regular',monospace;"><br /></span><span style="color:#629755;font-style:italic;font-family:'Menlo-Regular',monospace;"><br /></span> <span style="color:#629755;font-style:italic;">*/<br /></span> <span style="color:#bbb529;">@Bean</span>(name = <span style="color:#6a8759;">"asyncExecutor"</span>) <span style="color:#808080;">// </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">线程池名称，用于</span><span style="color:#808080;"> @Async </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">注解指定<br /></span> <span style="color:#cc7832;">public </span>Executor <span style="color:#ffc66d;">asyncExecutor</span>() {<br />        ThreadPoolTaskExecutor executor = <span style="color:#cc7832;">new </span>ThreadPoolTaskExecutor()<span style="color:#cc7832;">;<br /></span><span style="color:#cc7832;"><br /></span> <span style="color:#808080;">// </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">核心线程数（默认线程数）：线程池创建时初始化的线程数量<br /></span> executor.setCorePoolSize(<span style="color:#9876aa;font-style:italic;">CPU_CORES</span>)<span style="color:#cc7832;">;<br /></span> <span style="color:#808080;">// </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">最大线程数：线程池最大可创建的线程数量<br /></span> executor.setMaxPoolSize(<span style="color:#9876aa;font-style:italic;">CPU_CORES </span>* <span style="color:#6897bb;">2</span>)<span style="color:#cc7832;">;<br /></span> <span style="color:#808080;">// </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">队列容量：任务队列的最大长度，超过核心线程数的任务会先进入队列等待<br /></span> executor.setQueueCapacity(<span style="color:#6897bb;">10000</span>)<span style="color:#cc7832;">;<br /></span> <span style="color:#808080;">// </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">线程空闲时间：当线程数超过核心线程数时，多余线程的最大空闲时间（单位：秒）<br /></span> executor.setKeepAliveSeconds(<span style="color:#6897bb;">60</span>)<span style="color:#cc7832;">;<br /></span> <span style="color:#808080;">// </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">线程名称前缀：方便日志排查<br /></span> executor.setThreadNamePrefix(<span style="color:#6a8759;">"async-task-"</span>)<span style="color:#cc7832;">;<br /></span> <span style="color:#808080;">// </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">拒绝策略：当任务队列满且线程数达到最大线程数时，如何处理新任务<br /></span> <span style="color:#808080;">// ThreadPoolExecutor.CALLER_RUNS_POLICY</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">：由提交任务的线程执行（避免任务丢失，适合非核心任务）<br /></span> executor.setRejectedExecutionHandler(<span style="color:#cc7832;">new </span>ThreadPoolExecutor.CallerRunsPolicy())<span style="color:#cc7832;">;<br /></span> <span style="color:#808080;">// </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">初始化线程池<br /></span> executor.initialize()<span style="color:#cc7832;">;<br /></span><span style="color:#cc7832;"> return </span>executor<span style="color:#cc7832;">;<br /></span> }<br /><br /><br /><div><pre style="font-family:'JetBrains Mono',monospace;font-size:13.5pt;">List&lt;CompletableFuture&lt;List&lt;GameUserView&gt;&gt;&gt; futureList = <span style="color:#cc7832;">new </span>ArrayList&lt;&gt;()<span style="color:#cc7832;">;<br /></span><span style="color:#cc7832;">for </span>(<span style="color:#cc7832;">int </span>i = <span style="color:#6897bb;">0</span><span style="color:#cc7832;">; </span>i &lt; userIdList.size()<span style="color:#cc7832;">; </span>i += <span style="color:#6897bb;">500</span>) {<br />    <span style="color:#cc7832;">int </span>end = Math.<span style="font-style:italic;">min</span>(i + <span style="color:#6897bb;">500</span><span style="color:#cc7832;">, </span>userIdList.size())<span style="color:#cc7832;">;<br /></span> List&lt;Long&gt; batchIds = <span style="color:#cc7832;">new </span>ArrayList&lt;&gt;(userIdList.subList(i<span style="color:#cc7832;">, </span>end))<span style="color:#cc7832;">;<br /></span> <span style="color:#808080;">// </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">指定自定义线程池<br /></span> CompletableFuture&lt;List&lt;GameUserView&gt;&gt; future = CompletableFuture.<span style="font-style:italic;">supplyAsync</span>(<br />            () -&gt; <span style="color:#9876aa;">gameUserTelegramInfoMapper</span>.getBotNameByUserIds(<span style="color:#b389c5;">batchIds</span><span style="color:#cc7832;">,</span>YesOrNoEnum.<span style="color:#9876aa;font-style:italic;">YES</span>.getCode())<span style="color:#cc7832;">, </span><span style="color:#9876aa;">asyncExecutor</span>)<span style="color:#808080;">//</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">使用自定义线程池<br /></span> .orTimeout(<span style="color:#6897bb;">600</span><span style="color:#cc7832;">, </span><span style="color:#9876aa;font-style:italic;">MILLISECONDS</span>)<span style="color:#808080;">//</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">单次超时</span><span style="color:#808080;">600ms<br /></span> .exceptionally(e -&gt; {<br />                <span style="color:#9876aa;font-style:italic;">log</span>.error(<span style="color:#6a8759;">"</span><span style="color:#6a8759;font-family:'Menlo-Regular',monospace;">多线程批量查询用户信息完成</span><span style="color:#6a8759;">!"</span><span style="color:#cc7832;">, </span>e)<span style="color:#cc7832;">;<br /></span><span style="color:#cc7832;">                return new </span>ArrayList&lt;&gt;()<span style="color:#cc7832;">;<br /></span> })<span style="color:#cc7832;">;<br /></span> futureList.add(future)<span style="color:#cc7832;">;<br /></span>}<br /><span style="color:#808080;">//2: </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">等待所有任务完成并汇总结果<br /></span>CompletableFuture&lt;Void&gt; allFuture = CompletableFuture.<span style="font-style:italic;">allOf</span>(futureList.toArray(<span style="color:#cc7832;">new </span>CompletableFuture[<span style="color:#6897bb;">0</span>]))<span style="color:#cc7832;">;<br /></span><span style="color:#808080;">//3: </span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">汇总结果<br /></span>List&lt;GameUserView&gt; userViews =allFuture.thenApply(v -&gt;<br />                <span style="color:#b389c5;">futureList</span>.stream()<br />                        <span style="color:#808080;">//</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">单个任务异常兜底（</span><span style="color:#808080;">exceptionally</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">）<br /></span> .map(f -&gt; f.exceptionally(e -&gt; <span style="color:#cc7832;">new </span>ArrayList&lt;&gt;()).join())<br />                        .filter(result -&gt; !result.isEmpty())<br />                        .flatMap(List::stream)<br />                        .collect(Collectors.<span style="font-style:italic;">toList</span>())<br />        ).orTimeout(<span style="color:#6897bb;">5</span><span style="color:#cc7832;">, </span><span style="color:#9876aa;font-style:italic;">SECONDS</span>)<span style="color:#808080;">//4:</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">总超时兜底</span><span style="color:#808080;">,</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">任务层面超时，替代</span><span style="color:#808080;">try-catch</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">捕获</span><span style="color:#808080;">TimeoutException<br /></span> .exceptionally(e -&gt;{<br />            <span style="color:#808080;">//5:</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">超时</span><span style="color:#808080;">/</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">异常时自动取消所有未完成任务</span><span style="color:#808080;">,</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">防止资源耗尽。<br /></span> <span style="color:#b389c5;">futureList</span>.forEach(f -&gt; {<br />                <span style="color:#cc7832;">if</span>(!f.isDone()){<br />                    <span style="color:#cc7832;">boolean </span>cancel = f.cancel(<span style="color:#cc7832;">true</span>)<span style="color:#cc7832;">;<br /></span> <span style="color:#9876aa;font-style:italic;">log</span>.info(<span style="color:#6a8759;">"</span><span style="color:#6a8759;font-family:'Menlo-Regular',monospace;">取消子任务：</span><span style="color:#cc7832;">{}</span><span style="color:#6a8759;">"</span><span style="color:#cc7832;">, </span>cancel)<span style="color:#cc7832;">;<br /></span> }<br />            })<span style="color:#cc7832;">;<br /></span> <span style="color:#9876aa;font-style:italic;">log</span>.error(<span style="color:#6a8759;">"</span><span style="color:#6a8759;font-family:'Menlo-Regular',monospace;">多线程批量查询用户信息完成</span><span style="color:#6a8759;">!"</span><span style="color:#cc7832;">,</span>e)<span style="color:#cc7832;">;<br /></span><span style="color:#cc7832;">            return new </span>ArrayList&lt;&gt;()<span style="color:#cc7832;">;<br /></span> }).join()<span style="color:#cc7832;">;</span><span style="color:#808080;">//6:</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">最终无参</span><span style="color:#808080;">join</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">（已通过</span><span style="color:#808080;">orTimeout+exceptionally</span><span style="color:#808080;font-family:'Menlo-Regular',monospace;">兜底，无阻塞风险）<br /></span><span style="color:#9876aa;font-style:italic;">log</span>.info(<span style="color:#6a8759;">"</span><span style="color:#6a8759;font-family:'Menlo-Regular',monospace;">多线程批量查询用户信息完成</span><span style="color:#6a8759;">!</span><span style="color:#6a8759;font-family:'Menlo-Regular',monospace;">结果数</span><span style="color:#6a8759;">:</span><span style="color:#cc7832;">{}</span><span style="color:#6a8759;">"</span><span style="color:#cc7832;">,</span>userViews.size())<span style="color:#cc7832;">;<br /></span><span style="color:#cc7832;">return </span>userViews<span style="color:#cc7832;">;</span></pre></div><br /></pre></div><img src ="http://www.blogjava.net/zzzlyr/aggbug/451709.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zzzlyr/" target="_blank">为自己代言</a> 2025-12-08 16:41 <a href="http://www.blogjava.net/zzzlyr/articles/451709.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>springboot3+springcloudstream4.x配置集成</title><link>http://www.blogjava.net/zzzlyr/articles/451511.html</link><dc:creator>为自己代言</dc:creator><author>为自己代言</author><pubDate>Thu, 31 Oct 2024 03:27:00 GMT</pubDate><guid>http://www.blogjava.net/zzzlyr/articles/451511.html</guid><wfw:comment>http://www.blogjava.net/zzzlyr/comments/451511.html</wfw:comment><comments>http://www.blogjava.net/zzzlyr/articles/451511.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zzzlyr/comments/commentRss/451511.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zzzlyr/services/trackbacks/451511.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: ringboot3+springcloudstream4.x配置集成springcloudstream3.X 后逐渐淘汰了 @input @output&nbsp;@EnableBinding 这些注解 到4.X后这个注解都没有了，全部转向function 方式(关于function可以了解下)组件版本：springboot 3、 springcloud&nbsp;2022.0.0、springc...&nbsp;&nbsp;<a href='http://www.blogjava.net/zzzlyr/articles/451511.html'>阅读全文</a><img src ="http://www.blogjava.net/zzzlyr/aggbug/451511.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zzzlyr/" target="_blank">为自己代言</a> 2024-10-31 11:27 <a href="http://www.blogjava.net/zzzlyr/articles/451511.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>springboot3.X + nacos2.3 集成使用备忘</title><link>http://www.blogjava.net/zzzlyr/articles/451507.html</link><dc:creator>为自己代言</dc:creator><author>为自己代言</author><pubDate>Sun, 27 Oct 2024 14:16:00 GMT</pubDate><guid>http://www.blogjava.net/zzzlyr/articles/451507.html</guid><wfw:comment>http://www.blogjava.net/zzzlyr/comments/451507.html</wfw:comment><comments>http://www.blogjava.net/zzzlyr/articles/451507.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zzzlyr/comments/commentRss/451507.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zzzlyr/services/trackbacks/451507.html</trackback:ping><description><![CDATA[1：nacos2.X 配置中心三个主要的参数 dataId、group、namespace<br /><br />&nbsp; &nbsp;其中dataid 是指配置文件名称；group:相当于给配置文件分类；namespace:用来隔离环境的(例如：dev,test,product)<br /><br />&nbsp; &nbsp;比较关注的点是 bootstrap.yml； application.yml 这两文件用途和加载顺序<br /><br /><ol><li>bootstrap.yml 用于配置nacos地址，用户名，密码，命名空间(相当要先连接上nacos),然后才能拉去nacos上的配置文件信息;&nbsp;<br /></li><li>application.yml 文件用于配置在本地的配置文件(其实这个也可以放到nacos上)<br /><p style="margin: 24px 0px; padding: 0px; box-sizing: border-box; line-height: 30px; font-size: 16px; color: #2d3037; word-break: break-all; font-family: &quot;Helvetica Neue&quot;, Helvetica, Arial, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, &quot;Heiti SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif; background-color: #ffffff;">bootstrap配置文件是spring cloud新增的启动配置文件，需要引入spring-cloud-context依赖后，才会进行加载(看spring cloud 2022版本以以前)。</p><ul list-paddingleft-2"="" style="margin: 0px; padding: 0px; box-sizing: border-box; list-style: inside disc; color: #2d3037; font-family: &quot;Helvetica Neue&quot;, Helvetica, Arial, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, &quot;Heiti SC&quot;, &quot;WenQuanYi Micro Hei&quot;, sans-serif; font-size: 16px; background-color: #ffffff;"><li style="margin: 6px 0px; padding: 0px; box-sizing: border-box; line-height: 26px; font-size: 14px; word-break: break-all;"><p style="margin: 24px 0px; padding: 0px; box-sizing: border-box; line-height: 30px; font-size: 16px; word-break: break-all; display: inline;">bootstrap由父ApplicationContext加载，所以比application优先加载</p></li><li style="margin: 6px 0px; padding: 0px; box-sizing: border-box; line-height: 26px; font-size: 14px; word-break: break-all;"><p style="margin: 24px 0px; padding: 0px; box-sizing: border-box; line-height: 30px; font-size: 16px; word-break: break-all; display: inline;">因为bootstrap优先于application加载，所以不会被覆盖</p></li><li style="margin: 6px 0px; padding: 0px; box-sizing: border-box; line-height: 26px; font-size: 14px; word-break: break-all;"><p style="margin: 24px 0px; padding: 0px; box-sizing: border-box; line-height: 30px; font-size: 16px; word-break: break-all; display: inline;">使用配置中心spring cloud config时，需要在bootstarp中配置配置中心的地址，从而实现父ApplicationContext加载时，从配置中心拉去相应的配置到应用中。</p></li></ul>它们加载顺序：bootstrap.yaml文件生效后，去nacos拉去完配置信息后，与本地的application.yaml配置信息进行合并，然后加载到spring容器中<br /><br />在springboot工程中使用基础上使用两个注释：@RefreshScope 放在类上和@value 配置使用&nbsp;<br /><div>要实现取值和动态刷新通过实验证明使用@NacosValue是不行了，因为使用了Spring Cloud的依赖包，所以根据官方文档显示，要通过 Spring Cloud 原生注解 @RefreshScope + @Value 来实现配置自动更新，我们可以用下面的方法达到同样的效。<br />nacos client 从服务端拉取配置信息会放到client 本地缓存起来 默认目录：${user}\nacos\config 下(拉到调试时候有用)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div><div>详细文章 ：https://developer.aliyun.com/article/859891</div><br /><div></div></li></ol>&nbsp; &nbsp;&nbsp;<img src ="http://www.blogjava.net/zzzlyr/aggbug/451507.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zzzlyr/" target="_blank">为自己代言</a> 2024-10-27 22:16 <a href="http://www.blogjava.net/zzzlyr/articles/451507.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>软件微服务体系中的分层设计和领域划分</title><link>http://www.blogjava.net/zzzlyr/articles/435939.html</link><dc:creator>为自己代言</dc:creator><author>为自己代言</author><pubDate>Sun, 26 Sep 2021 10:09:00 GMT</pubDate><guid>http://www.blogjava.net/zzzlyr/articles/435939.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1：软件分层图：2：详细分层架构图：3：适用业务快速发展的分层图COLA 是 Clean Object-Oriented and Layered Architecture的缩写，代表&#8220;整洁面向对象分层架构&#8221;。 目前COLA已经发展到COLA 4.0。COLA分为两个部分，COLA架构和COLA组件。https://github.com/alibaba/COLA?spm=at...&nbsp;&nbsp;<a href='http://www.blogjava.net/zzzlyr/articles/435939.html'>阅读全文</a><img src ="http://www.blogjava.net/zzzlyr/aggbug/435939.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zzzlyr/" target="_blank">为自己代言</a> 2021-09-26 18:09 <a href="http://www.blogjava.net/zzzlyr/articles/435939.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>InnoDB的锁的机制探究</title><link>http://www.blogjava.net/zzzlyr/articles/435921.html</link><dc:creator>为自己代言</dc:creator><author>为自己代言</author><pubDate>Mon, 12 Jul 2021 07:38:00 GMT</pubDate><guid>http://www.blogjava.net/zzzlyr/articles/435921.html</guid><wfw:comment>http://www.blogjava.net/zzzlyr/comments/435921.html</wfw:comment><comments>http://www.blogjava.net/zzzlyr/articles/435921.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zzzlyr/comments/commentRss/435921.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zzzlyr/services/trackbacks/435921.html</trackback:ping><description><![CDATA[<h1>InnoDB的锁</h1>
<h2>InnoDB的行锁：共享锁、排他锁、MDL锁</h2>
<h5>共享锁：又称读锁、S锁。一个事务获取一个数据行的共享锁，其他事务能获取该行对应的共享锁，但不能获得排他锁；即一个事务在读取一个数据行时，其他事务也可以读，但不能对数据进行增删改查。</h5>
<p>应用：</p>
<p>1.自动提交模式下的select查询，不加任何锁，直接返回查询结果</p>
<p>2.通过select&#8230;&#8230;lock in share mode在被读取的行记录或范围上加一个读锁，其他事务可以读，但是申请加写锁会被阻塞</p>
<h5>排他锁：又称写锁、X锁。一个事务获取了一个数据行的写锁，其他事务就不能再获取该行的其他锁，写锁优先级最高。</h5>
<p>应用：</p>
<p>1.一些DML操作会对行记录加写锁</p>
<p>2.select for update会对读取的行记录上加一个写锁，其他任何事务都不能对锁定的行加任何锁，否则会被阻塞</p>
<h5>MDL锁：MySQL5.5引入，用于保证表中元数据的信息。在会话A中，表开启了查询事务后，会自动获得一个MDL锁，会话B就不能执行任何DDL语句的操作</h5>
<h3>行锁实现方式</h3>
<p>InnoDB 行锁是通过给索引上的索引项加锁来实现的，这一点 MySQL 与 Oracle 不同，后者是 
通过在数据块中对相应数据行加锁来实现的。InnoDB 这种行锁实现特点意味着:只有通过 索引条件检索数据，InnoDB 
才使用行级锁，否则，InnoDB 将使用表锁! 在实际应用中，要特别注意 InnoDB 行锁的这一特性，不然的话，可能导致大量的锁冲突， 
从而影响并发性能。</p>
<h3>行锁的三种算法</h3>
<p>InnoDB 存储引擎有三种行锁的算法，其分别是：</p>
<ul><li>Record Lock: 单个行记录上的锁</li><li>Gap Lock: 间隙锁，锁定一个范围，但不包含记录本身</li><li>Next-Key 锁: Gap Lock + Record Lock，锁定一个范围，并且会锁定记录本身</li></ul>
<p>RC模式下只采用Record Lock，RR模式下采用了Next-Key</p>
<h3>加锁场景分析</h3>
<ul><li>主键索引</li></ul>
<p>如果我们加锁的行上存在主键索引，那么就会在这个主键索引上添加一个 Record Lock。</p>
<ul><li>辅助索引</li></ul>
<p>如果我们加锁的行上存在辅助索引，那么我们就会在这行的辅助索引上添加 Next-Key Lock，并在这行之后的辅助索引上添加一个 Gap Lock</p>
<p>辅助索引上的 Next-Key Lock 和 Gap Lock 都是针对 Repeatable Read 隔离模式存在的，这两种锁都是为了防止幻读现象的发生。</p>
<ul><li>唯一的辅助索引</li></ul>
<p>这里有一个特殊情况，如果辅助索引是唯一索引的话，MySQL 会将 Next-Key Lock 降级为 Record Lock，只会锁定当前记录的辅助索引。</p>
<p>如果唯一索引由多个列组成的，而我们只锁定其中一个列的话，那么此时并不会进行锁降级，还会添加 Next-Key Lock 和 Gap Lock。</p>
<ul><li>Insert 语句</li></ul>
<p>在 InnoDB 存储引擎中，对于 Insert 的操作，其会检查插入记录的下一条记录是否被锁定，若已经被锁定，则不允许查询。</p>
<h2>意向锁</h2>
<p>意向锁可以分为意向共享锁(Intention Shared Lock, IS)和意向排他锁(Intention eXclusive 
Lock, 
IX)。但它的锁定方式和共享锁和排他锁并不相同，意向锁上锁只是表示一种&#8220;意向&#8221;,并不会真的将对象锁住，让其他事物无法修改或访问。例如事物T1想要修改表<code>test</code>中的行<code>r1</code>，它会上两个锁:</p>
<ol><li>在表<code>test</code>上意向排他锁</li><li>在行<code>r1</code>上排他锁</li></ol>
<p>事物T1在<code>test</code>表上上了意向排他锁，并不代表其他事物无法访问<code>test</code>了，它上的锁只是表明一种意向，它将会在<code>db</code>中的<code>test</code>表中的某几行记录上上一个排他锁。</p>
<table>
<thead>
<tr>
<th><br /></th>
<th>意向共享锁</th>
<th>意向排他锁</th>
<th>共享锁</th>
<th>排他锁</th>
</tr>
</thead>
<tbody>
<tr>
<td>意向共享锁</td>
<td>兼容</td>
<td>兼容</td>
<td>兼容</td>
<td>不兼容</td>
</tr>
<tr>
<td>意向排他锁</td>
<td>兼容</td>
<td>兼容</td>
<td>不兼容</td>
<td>不兼容</td>
</tr>
<tr>
<td>共享锁</td>
<td>兼容</td>
<td>不兼容</td>
<td>兼容</td>
<td>不兼容</td>
</tr>
<tr>
<td>排他锁</td>
<td>不兼容</td>
<td>不兼容</td>
<td>不兼容</td>
<td>不兼容</td>
</tr>
</tbody>
</table>
<h2>一致性非锁定读</h2>
<p>一致性非锁定读是指 InnoDB 存储引擎通过行多版本控制(multi 
version)的方式来读取当前执行时间数据库中行的数据。具体来说就是如果一个事务读取的行正在被锁定，那么它就会去读取这行数据之前的快照数据，而不会等待这行数据上的锁释放。这个读取流程如图1所示:</p>
<p><img src="https://passage-1253400711.cos.ap-beijing.myqcloud.com/2018-03-06-052802.png" alt="图1" /></p>
<p>行的快照数据是通过undo段来实现的，而undo段用来回滚事务，所以快照数据本身没有额外的开销。此外，读取快照数据时不需要上锁的，因为没有事务会对快照数据进行更改。</p>
<p>MySQL 中并不是每种隔离级别都采用非一致性非锁定读的读取模式，而且就算是采用了一致性非锁定读，不同隔离级别的表现也不相同。在 READ
 COMMITTED 和 REPEATABLE READ 这两种隔离级别下，InnoDB存储引擎都使用一致性非锁定读。但是对于快照数据，READ
 COMMITTED 隔离模式中的事务读取的是当前行最新的快照数据，而 REPEATABLE READ 
隔离模式中的事务读取的是事务开始时的行数据版本。</p>
<h2>一致性锁定读</h2>
<p>在 InnoDB 存储引擎中，<code>select</code>语句默认采取的是一致性非锁定读的情况，但是有时候我们也有需求需要对某一行记录进行锁定再来读取，这就是一致性锁定读。</p>
<p>InnoDB 对于<code>select</code>语句支持以下两种锁定读:</p>
<ul><li><code>select ... for update</code></li><li><code>select ... lock in share mode</code></li></ul>
<p><code>select ... for update</code>会对读取的记录加一个X锁，其他事务不能够再来为这些记录加锁。<code>select ... lock in share mode</code>会对读取的记录加一个S锁，其它事务能够再为这些记录加一个S锁，但不能加X锁。</p>
<p>对于一致性非锁定读，即使行记录上加了X锁，它也是能够读取的，因为它读取的是行记录的快照数据，并没有读取行记录本身。</p>
<p><code>select ... for update</code>和<code>select ... lock in share mode</code>这两个语句必须在一个事务中，当事务提交了，锁也就释放了。因此在使用这两条语句之前必须先执行<code>begin</code>, <code>start transaction</code>，或者执行<code>set autocommit = 0</code>。</p>
<h2>InnoDB 在不同隔离级别下的一致性读及锁的差异</h2>
<pre><code class="lang-">consisten read //一致性读
share locks //共享锁
Exclusive locks //排他锁
</code></pre>
<table>
<thead>
<tr>
<th><br /></th>
<th><br /></th>
<th>读未提交</th>
<th>读已提交</th>
<th>可重复读</th>
<th>串行化</th>
</tr>
</thead>
<tbody>
<tr>
<td>SQL</td>
<td>条件</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td>select</td>
<td>相等</td>
<td>None locks</td>
<td>Consisten read/None lock</td>
<td>Consisten read/None lock</td>
<td>Share locks</td>
</tr>
<tr>
<td><br /></td>
<td>范围</td>
<td>None locks</td>
<td>Consisten read/None lock</td>
<td>Consisten read/None lock</td>
<td>Share Next-Key</td>
</tr>
<tr>
<td>update</td>
<td>相等</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
</tr>
<tr>
<td><br /></td>
<td>范围</td>
<td>Exclusive next-key</td>
<td>Exclusive next-key</td>
<td>Exclusive next-key</td>
<td>Exclusive next-key</td>
</tr>
<tr>
<td>Insert</td>
<td>N/A</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
</tr>
<tr>
<td>Replace</td>
<td>无键冲突</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
</tr>
<tr>
<td><br /></td>
<td>键冲突</td>
<td>Exclusive next-key</td>
<td>Exclusive next-key</td>
<td>Exclusive next-key</td>
<td>Exclusive next-key</td>
</tr>
<tr>
<td>delete</td>
<td>相等</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
</tr>
<tr>
<td><br /></td>
<td>范围</td>
<td>Exclusive next-key</td>
<td>Exclusive next-key</td>
<td>Exclusive next-key</td>
<td>Exclusive next-key</td>
</tr>
<tr>
<td><strong>Select &#8230; from &#8230; Lock in share mode</strong></td>
<td>相等</td>
<td>Share locks</td>
<td>Share locks</td>
<td>Share locks</td>
<td>Share locks</td>
</tr>
<tr>
<td><br /></td>
<td>范围</td>
<td>Share locks</td>
<td>Share locks</td>
<td>Exclusive next-key</td>
<td>Exclusive next-key</td>
</tr>
<tr>
<td><strong>Select * from &#8230; For update</strong></td>
<td>相等</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
</tr>
<tr>
<td><br /></td>
<td>范围</td>
<td>Exclusive locks</td>
<td>Exclusive locks</td>
<td>Exclusive next-key</td>
<td>Exclusive next-key</td>
</tr>
<tr>
<td><strong>Insert into &#8230; Select &#8230;</strong></td>
<td><strong>innodb_locks_ unsafe_for_bi nlog=off</strong></td>
<td>Share Next-Key</td>
<td>Share Next-Key</td>
<td>Share Next-Key</td>
<td>Share Next-Key</td>
</tr>
<tr>
<td>(指源表锁)</td>
<td><strong>innodb_locks_ unsafe_for_bi nlog=on</strong></td>
<td>None locks</td>
<td>Consisten read/None lock</td>
<td>Consisten read/None lock</td>
<td>Share Next-Key</td>
</tr>
<tr>
<td><strong>create table &#8230; Select &#8230;</strong></td>
<td><strong>innodb_locks_ unsafe_for_bi nlog=off</strong></td>
<td>Share Next-Key</td>
<td>Share Next-Key</td>
<td>Share Next-Key</td>
<td>Share Next-Key</td>
</tr>
<tr>
<td>(指源表锁)</td>
<td><strong>innodb_locks_ unsafe_for_bi nlog=on</strong></td>
<td>None locks</td>
<td>Consisten read/None lock</td>
<td>Consisten read/None lock</td>
<td>Share Next-Key</td>
</tr>
</tbody>
</table>
<h5>在了解 InnoDB 锁特性后，用户可以通过设计和 SQL 调整等措施减少锁冲突和死锁，包括:</h5>
<ul><li>尽量使用较低的隔离级别;</li><li>精心设计索引，并尽量使用索引访问数据，使加锁更精确，从而减少锁冲突的机会;</li><li>选择合理的事务大小， 小事务发生锁冲突的几率也更小;</li><li>给记录集显示加锁时，最好一次性请求足够级别的锁。比如要修改数据的话，最好直接申请排他锁，而不是先申请共享锁，修改时再请求排他锁，这样容易产生死锁;</li><li>不同的程序访问一组表时，应尽量约定以相同的顺序访问各表，对一个表而言，尽可能以固定的顺序存取表中的行。这样可以大大减少死锁的机会;</li><li>尽量用相等条件访问数据，这样可以避免间隙锁对并发插入的影响;</li><li>不要申请超过实际需要的锁级别;除非必须，查询时不要显示加锁;</li><li>对于一些特定的事务，可以使用表锁来提高处理速度或减少死锁的可能。</li></ul>
<h2>参考资料</h2>
<p>1.https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-intention-locks&nbsp; mysql官网开发手册<br /></p>
<p>2.《MySQL 技术内幕 &#8211; InnoDB 存储引擎》</p>
<p>3.《深入浅出MySQL》</p>
<p>4.https://www.modb.pro/db/33873</p><p><br /></p><img src ="http://www.blogjava.net/zzzlyr/aggbug/435921.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zzzlyr/" target="_blank">为自己代言</a> 2021-07-12 15:38 <a href="http://www.blogjava.net/zzzlyr/articles/435921.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>大厂链路追踪架构图(鹰眼)</title><link>http://www.blogjava.net/zzzlyr/articles/435886.html</link><dc:creator>为自己代言</dc:creator><author>为自己代言</author><pubDate>Fri, 04 Jun 2021 07:36:00 GMT</pubDate><guid>http://www.blogjava.net/zzzlyr/articles/435886.html</guid><wfw:comment>http://www.blogjava.net/zzzlyr/comments/435886.html</wfw:comment><comments>http://www.blogjava.net/zzzlyr/articles/435886.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zzzlyr/comments/commentRss/435886.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zzzlyr/services/trackbacks/435886.html</trackback:ping><description><![CDATA[<h1 id="整体架构" data-spm-anchor-id="a1zco.8292288.0.i5.749e105bdMZpj6" style="box-sizing: border-box; font-size: 1.3em; margin-top: 0px !important; margin-right: 0px; margin-bottom: 0.85em; margin-left: 0px; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-weight: 700; line-height: 1.1; color: #333333; break-after: avoid; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: 0.2px; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-bottom: 1px solid #e7e9ed; padding-bottom: 10px;">整体架构</h1><div><span style="color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; orphans: 3; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 3; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">从日志生成到抓取、存储、分析、展现的多个系统间交互过程。</span></div><div><span style="color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; orphans: 3; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 3; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;"><br /></span></div><div><br /><span style="color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; orphans: 3; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 3; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;"><img src="http://git.cn-hangzhou.oss.aliyun-inc.com/uploads/middleware/eagleeye-docs/e6b1f43e23bea8208c64b8fa44e1f795/image.png" alt="image" /><br /></span></div><div><h1 id="eagleeye-的核心" data-spm-anchor-id="a1zco.8292288.0.i7.749e105bdMZpj6" style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; font-size: 1.3em; margin: 1.275em 0px 0.85em; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-weight: 700; line-height: 1.1; color: #333333; break-after: avoid; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: 0.2px; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-bottom: 1px solid #e7e9ed; padding-bottom: 10px;">EagleEye 的核心</h1><div><ul style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; margin: 0px 0px 0.85em; font-size: 14px; padding: 0px 0px 0px 2em; color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><li data-spm-anchor-id="a1zco.8292288.0.i8.749e105bdMZpj6" style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; font-size: inherit;">TraceId</li></ul></div></div><div><span style="color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; orphans: 3; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 3; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">在复杂的分布式系统环境下，EagleEye是一个有广泛用途的调用分析和问题排查工具。与一般的调用信息埋点日志相比，EagleEye埋点的一个显著的不同点在于它的每条日志都有与每次请求关联的上下文ID，我们称为TraceId。通过TraceId，后期的日志处理时可以把一次前端请求在不同服务器记录的调用日志关联起来，重新组合成当时这个请求的调用链。因此，EagleEye不仅可以分析到应用之间的直接调用关系，还可以得到他们的间接调用关系、以及上下游的业务处理信息；对于调用链的底层系统，可以追溯到它的最上层请求来源以及中间经过的所有节点；对于调用链的上层入口，可以收集到它的整棵调用树，从而定位下游系统的处理瓶颈，当下游某个应用有异常发生时，能迅速定位到问题发生的位置。</span></div><div><br /><span style="color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; orphans: 3; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 3; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;"></span></div><div><img src="http://git.cn-hangzhou.oss.aliyun-inc.com/uploads/middleware/eagleeye-docs/df5b5886c61c9bc6fd53c054c9233df9/image.png" alt="image" /></div><div></div><div><p data-spm-anchor-id="a1zco.8292288.0.i11.749e105bdMZpj6" style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; margin: 0px 0px 0.85em; font-size: 14px; orphans: 3; widows: 3; color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">如上图所示，应用A是接受到来自用户浏览器的Web请求的前端服务器，它是一条调用链的开始端，在TBSession和EagleEyeFilter中都做了EagleEye上下文埋点。请求收到后它会先调用EagleEye StartTrace生成TraceId并放置在当前线程的ThreadLocal，日志埋点请求信息（如URL、SessionId、UserId等）。在请求处理完毕提交相应时，再调用EndTrace清理线程中的EagleEye信息。 在应用A调用应用B、C的HSF服务，或者发送Notify消息时，TraceId被包含在EagleEye上下文中，随网络请求到达应用B、C、D、E之中，并放置在线程ThreadLocal内，因此后续调用到的这些系统都会有EagleEye这次请求的上下文。这些系统再发起网络请求时，也类似的携带了上下文信息的。</p><ul style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; margin: 0px 0px 0.85em; font-size: 14px; padding: 0px 0px 0px 2em; color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><li style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; font-size: inherit;">RpcId</li></ul><p style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; margin: 0px 0px 0.85em; font-size: 14px; orphans: 3; widows: 3; color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">为了区别同一个调用链下多个网络调用的顺序和嵌套层次，EagleEye还需要传输和记录RpcId。 RpcId用0.X1.X2.X3.....Xi来表示，Xi都是非负整数，根节点的RpcId固定从0开始，第一层网络调用的RpcId是0.X1，第二层的则为0.X1.X2，依次类推。*例如，从根节点发出的调用的RpcId是0.1、0.2、0.3，RpcId是0.1的节点发出的RpcId则为0.1.1、0.1.2、0.1.3。如下图所示</p><p style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; margin: 0px 0px 0.85em; font-size: 14px; orphans: 3; widows: 3; color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><img src="http://git.cn-hangzhou.oss.aliyun-inc.com/uploads/middleware/eagleeye-docs/fa0814a9353ccd928c00eae5ec9e3c4e/image.png" alt="image" /></p><p style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; margin: 0px 0px 0.85em; font-size: 14px; orphans: 3; widows: 3; color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><br /></p><p style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; margin: 0px 0px 0.85em; font-size: 14px; orphans: 3; widows: 3; color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span style="color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; orphans: 3; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 3; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">通过RpcId，可以准确的还原出调用链上每次调用的层次关系和兄弟调用之间的先后顺序。 例如上图应用 G 的两次调用0.2.1.1和0.1.2.1，可以看出对 DB 的访问0.2.1.1源于 C 到 G 的调用0.2.1，对 Tair 的访问0.1.2.1源于B 到 G 的调用0.1.2。 很多调用场景会比上面说的完全同步的调用更为复杂，比如会遇到异步、单向、广播、并发、批处理等等，这时候需要妥善处理好ThreadLocal上的调用上下文，避免调用上下文混乱和无法正确释放。另外，采用多级序号的RpcId设计方案会比单级序号递增更容易准确还原当时的调用情况。</span></p><p style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; margin: 0px 0px 0.85em; font-size: 14px; orphans: 3; widows: 3; color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><br /></p><br /><span style="color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; orphans: 3; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 3; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;"></span><span style="color: #333333; font-family: 微软雅黑, &quot;Microsoft Yahei&quot;, &quot;Hiragino Sans GB&quot;, tahoma, arial, 宋体; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.2px; orphans: 3; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 3; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;"></span></div><img src ="http://www.blogjava.net/zzzlyr/aggbug/435886.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zzzlyr/" target="_blank">为自己代言</a> 2021-06-04 15:36 <a href="http://www.blogjava.net/zzzlyr/articles/435886.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>分布式锁实现-redis,zk </title><link>http://www.blogjava.net/zzzlyr/articles/435834.html</link><dc:creator>为自己代言</dc:creator><author>为自己代言</author><pubDate>Wed, 24 Mar 2021 12:11:00 GMT</pubDate><guid>http://www.blogjava.net/zzzlyr/articles/435834.html</guid><wfw:comment>http://www.blogjava.net/zzzlyr/comments/435834.html</wfw:comment><comments>http://www.blogjava.net/zzzlyr/articles/435834.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zzzlyr/comments/commentRss/435834.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zzzlyr/services/trackbacks/435834.html</trackback:ping><description><![CDATA[<div> 1:分布锁有好多实现方式</div><div><ul><li>&nbsp;基于数据库实现</li></ul><div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个实现方式比较复杂,考虑因素比较多，比如：超时,非公平锁,非重入等会有各种各样的问题，在解决问题的过程中会使整个方案变得越来越复杂。操作数据库需要一定的开销，性能问题需要考虑&nbsp; &nbsp; &nbsp;&nbsp; </div><ul><li>基于redis实现(这个对于不太敏感的场景可以使用，由于redis集群和单机，还有客户端，版本等多方面因素考虑情况比较多)</li></ul><div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 性能好。使用缓存实现分布式锁的缺点 其数据库一样</div><ul><li>基于zookeeper实现(这个是最终也是最好最可靠的)<br /></li></ul><div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 创建临时节点，可以解决单机，锁无法释放，非阻塞，不可冲入，非公平的问题<br />&nbsp;</div><div>&nbsp;&nbsp;&nbsp; 总结<br /><blockquote>从理解的难易程度角度（从低到高）<br /><br />数据库 &gt; 缓存 &gt; Zookeeper<br /><br />从实现的复杂性角度（从低到高）<br /><br />Zookeeper &gt; 缓存 &gt; 数据库<br /><br />从性能角度（从高到低）<br /><br />缓存 &gt; Zookeeper &gt;= 数据库<br /><br />从可靠性角度（从高到低）<br /><br />Zookeeper &gt; 缓存 &gt; 数据库 <br /></blockquote>下面讲基于redis实现分布锁代码：RedisTemplate 客户端 <span style="color:#000080;font-weight:bold;">lettuce</span></div><div><span style="color:#000080;font-weight:bold;"><br /></span></div><div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><br /></span><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">@Service<br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;RedisDistributedLockUtils&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Autowired<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;RedisTemplate&nbsp;redisTemplate;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;Long&nbsp;RELEASE_SUCCESS&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1L</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;DEFAULT_TIMEOUT&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1000</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">10</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">因为要使用lua&nbsp;脚本是因为&nbsp;redis&nbsp;执行lua脚本是原子操作</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">final</span><span style="color: #000000; ">&nbsp;String&nbsp;UNLOCK_LUA</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">if&nbsp;redis.call('get',&nbsp;KEYS[1])&nbsp;==&nbsp;ARGV[1]&nbsp;then&nbsp;return&nbsp;redis.call('del',&nbsp;KEYS[1])&nbsp;else&nbsp;return&nbsp;0&nbsp;end</span><span style="color: #000000; ">"</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;实时获取锁<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;尝试获取分布式锁&nbsp;将redis版本升级到2.1以上(spring-boot-starter-data-redis&nbsp;版本&nbsp;2.X以上)，然后使用setIfAbsent&nbsp;不存在<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;当setIfAbsent成功之后断开连接，下面设置过期时间的代码&nbsp;stringRedisTemplate.expire(key,timeout);是无法执行的，这时候就会有大量没有过期时间的数据存在数据库<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;lockKey&nbsp;&nbsp;&nbsp;&nbsp;锁<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;requestId&nbsp;&nbsp;请求标识<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;expireTime&nbsp;超期时间<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@return</span><span style="color: #008000; ">&nbsp;是否获取成功<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">boolean</span><span style="color: #000000; ">&nbsp;trySetDistributedLock(String&nbsp;lockKey,&nbsp;String&nbsp;requestId,&nbsp;</span><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;expireTime)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;redisTemplate.opsForValue().setIfAbsent(lockKey,&nbsp;requestId,</span><span style="color: #000000; ">0</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;expireTime&nbsp;</span><span style="color: #000000; ">?</span><span style="color: #000000; ">&nbsp;DEFAULT_TIMEOUT&nbsp;:&nbsp;expireTime,&nbsp;TimeUnit.MILLISECONDS);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;以阻塞方式的获取锁<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;key<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;value<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;timeout<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@return</span><span style="color: #008000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">boolean</span><span style="color: #000000; ">&nbsp;setDistributedLock(String&nbsp;key,&nbsp;String&nbsp;value,&nbsp;</span><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;timeout)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Boolean&nbsp;lock&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;start&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;System.currentTimeMillis();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">while</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">!</span><span style="color: #000000; ">lock&nbsp;</span><span style="color: #000000; ">&amp;&amp;</span><span style="color: #000000; ">&nbsp;(System.currentTimeMillis()&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;start&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">&nbsp;timeout))&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">执行set命令</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lock&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;redisTemplate.opsForValue().setIfAbsent(key,&nbsp;value,&nbsp;timeout,&nbsp;TimeUnit.MILLISECONDS);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">不频繁去获取锁</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">!</span><span style="color: #000000; ">lock)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(</span><span style="color: #000000; ">60</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(InterruptedException&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;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;lock;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">boolean</span><span style="color: #000000; ">&nbsp;releaseLock(String&nbsp;key,&nbsp;String&nbsp;value)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;使用Lua脚本：先判断是否是自己设置的锁，再执行删除<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;使用lua脚本删除redis中匹配value的key，可以避免由于方法执行时间过长而redis锁自动过期失效的时候误删其他线程的锁<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;spring自带的执行脚本方法中，集群模式直接抛出不支持执行脚本的异常EvalSha&nbsp;is&nbsp;not&nbsp;supported&nbsp;in&nbsp;cluster&nbsp;environment<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;所以只能拿到原redis的connection来执行脚本</span><span style="color: #008000; "><br /></span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;keys&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ArrayList</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; ">();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;keys.add(key);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;args&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ArrayList</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; ">();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;args.add(value);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Long&nbsp;result&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(Long)redisTemplate.execute(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;RedisCallback</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Long</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Long&nbsp;doInRedis(RedisConnection&nbsp;connection)&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;DataAccessException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;nativeConnection&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;connection.getNativeConnection();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;集群模式和单机模式虽然执行脚本的方法一样，但是没有共同的接口，所以只能分开执行<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;集群模式</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(nativeConnection&nbsp;</span><span style="color: #0000FF; ">instanceof</span><span style="color: #000000; ">&nbsp;JedisCluster)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;(Long)((JedisCluster)nativeConnection).eval(UNLOCK_LUA,&nbsp;keys,&nbsp;args);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">客户端是Jedis时候(单机模式)</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(nativeConnection&nbsp;</span><span style="color: #0000FF; ">instanceof</span><span style="color: #000000; ">&nbsp;Jedis)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;(Long)((Jedis)nativeConnection).eval(UNLOCK_LUA,&nbsp;keys,&nbsp;args);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">这里使用&nbsp;redisTemplate&nbsp;中lettuce&nbsp;客户端</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DefaultRedisScript</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Long</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;redisScript&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;DefaultRedisScript</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; ">();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redisScript.setScriptText(UNLOCK_LUA);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redisScript.setResultType(Long.</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;(Long)redisTemplate.execute(redisScript,&nbsp;keys,&nbsp;value);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<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;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">返回最终结果</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;RELEASE_SUCCESS.equals(result);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /></span></div></div><span style="color:#000080;font-weight:bold;"></span><pre style="background-color: #ffffff; color: #000000; font-family: &quot;Menlo&quot;; font-size: 11.3pt;">基于zookeeper实现下期补上：<br /><br /><br /></pre> <pre style="background-color: #ffffff; color: #000000; font-family: &quot;Menlo&quot;; font-size: 11.3pt;">介绍分布式锁文章写的比较详细：<br />https://blog.csdn.net/u010963948/article/details/79006572<br /></pre></div><div></div><div></div><div></div></div><img src ="http://www.blogjava.net/zzzlyr/aggbug/435834.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zzzlyr/" target="_blank">为自己代言</a> 2021-03-24 20:11 <a href="http://www.blogjava.net/zzzlyr/articles/435834.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDK 8 函数式接口  Supplier、Function、Consumer、Predicate</title><link>http://www.blogjava.net/zzzlyr/articles/435611.html</link><dc:creator>为自己代言</dc:creator><author>为自己代言</author><pubDate>Fri, 24 Jul 2020 07:46:00 GMT</pubDate><guid>http://www.blogjava.net/zzzlyr/articles/435611.html</guid><wfw:comment>http://www.blogjava.net/zzzlyr/comments/435611.html</wfw:comment><comments>http://www.blogjava.net/zzzlyr/articles/435611.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zzzlyr/comments/commentRss/435611.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zzzlyr/services/trackbacks/435611.html</trackback:ping><description><![CDATA[<div><strong style="font-size: 12pt;">函数式接口的特征</strong></div>
<h3><span style="font-size: 12pt;">1、三种方法</span></h3>
<div></div>
<div>
<ul>
     <li><span style="font-size: 12pt;">唯一的抽象方法</span></li>
     <li><span style="font-size: 12pt;">使用default定义普通方法（默认方法），通过对象调用。</span></li>
     <li><span style="font-size: 12pt;">使用static定义静态方法，通过接口名调用。</span></li>
</ul>
</div>
<h3><span style="font-size: 12pt;">2、一个新注解@FunctionInterface</span></h3>
<div style="font-variant: normal; font-stretch: normal; line-height: normal; font-family: Arial;">
<ul>
     <li><span style="font-size: 12pt;">注解@FunctionalInterface告诉编译器这是一个函数式接口，明确这个函数中只有一个抽象方法，当你尝试在接口中编写多个抽象方法的时候编译器将不允许，但是可以有多个非抽象方法。</span><br />
     <br />
     </li>
     <li><span style="font-size: 12pt;">不过Object类的方法可以定义为抽象方法，因为接口的实现类一定是Object的子类</span><br />
     <br />
     </li>
     <li><span style="font-size: 12pt;">如果接口被标注了@FunctionalInterface，这个类就必须符合函数式接口的规范</span><br />
     <br />
     </li>
     <li><span style="font-size: 12pt;">即使一个接口没有标注@FunctionalInterface，如果这个接口满足函数式接口规则，依旧被当作函数式接口。</span></li>
</ul>
<h3><span style="font-size: 12pt;">3</span><span style="font-family: verdana, &quot;courier new&quot;; font-size: 12pt;">、</span><span style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: break-word; color: #4d4d4d; font-family: &quot;Microsoft YaHei&quot;, &quot;SF Pro Display&quot;, Roboto, Noto, Arial, &quot;PingFang SC&quot;, sans-serif; font-size: 12pt; font-variant-ligatures: common-ligatures; background-color: #ffffff;">JDK 1.8 新增加的函数接口包</span><span style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; font-weight: 700; overflow-wrap: break-word; color: #4d4d4d; font-family: &quot;Microsoft YaHei&quot;, &quot;SF Pro Display&quot;, Roboto, Noto, Arial, &quot;PingFang SC&quot;, sans-serif; font-size: 12pt; font-variant-ligatures: common-ligatures; background-color: #ffffff;">：<br />&nbsp; &nbsp;<br /></span><p><span style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; font-weight: 700; overflow-wrap: break-word; color: #4d4d4d; font-family: &quot;Microsoft YaHei&quot;, &quot;SF Pro Display&quot;, Roboto, Noto, Arial, &quot;PingFang SC&quot;, sans-serif; font-size: 12pt; font-variant-ligatures: common-ligatures; background-color: #ffffff;"> &nbsp;&nbsp;</span><span style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: break-word; color: #4d4d4d; font-family: &quot;Microsoft YaHei&quot;, &quot;SF Pro Display&quot;, Roboto, Noto, Arial, &quot;PingFang SC&quot;, sans-serif; font-size: 12pt; font-variant-ligatures: common-ligatures; background-color: #ffffff;"><span style="font-weight: normal;">&nbsp;&nbsp;java.util.function.*</span><br style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: break-word; font-weight: 400;" />&nbsp; &nbsp;&nbsp;<span style="font-weight: 400;">java.util.function 它包含了很多接口，用来支持 Java的 函数式编程，它们大致分为五类：</span><br /><br />&nbsp; &nbsp;&nbsp;<img src="http://www.blogjava.net/images/blogjava_net/zzzlyr/fu.jpg" width="1449" height="364" alt="" /><br /></span><span style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: break-word; color: #4d4d4d; font-family: &quot;Microsoft YaHei&quot;, &quot;SF Pro Display&quot;, Roboto, Noto, Arial, &quot;PingFang SC&quot;, sans-serif; font-size: 12pt; font-variant-ligatures: common-ligatures; background-color: #ffffff;">&nbsp;&nbsp;</span><span style="background-color: #ffffff; color: #4d4d4d; font-family: &quot;Microsoft YaHei&quot;, &quot;SF Pro Display&quot;, Roboto, Noto, Arial, &quot;PingFang SC&quot;, sans-serif; font-size: 12pt; font-variant-ligatures: common-ligatures;">&nbsp;</span></p><span style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: break-word; color: #4d4d4d; font-family: &quot;Microsoft YaHei&quot;, &quot;SF Pro Display&quot;, Roboto, Noto, Arial, &quot;PingFang SC&quot;, sans-serif; font-size: 12pt; font-variant-ligatures: common-ligatures; background-color: #ffffff;">&nbsp;4</span>、代码样例<br /></h3><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #008000; font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;<br /><div style="display: inline-block;"><div><span style="font-size: 12pt;">&nbsp; &nbsp; /**</span><br /><span style="font-size: 12pt;">&nbsp; &nbsp; &nbsp;*JDK&nbsp;8&nbsp;函数式接口&nbsp;&nbsp;Supplier、Function、Consumer、Predicate</span></div></div></span><span style="color: #008000; font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*<br /></span><span style="color: #008000; font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; font-size: 12pt;">@param</span><span style="color: #008000; font-size: 12pt;">&nbsp;args<br /></span><span style="color: #008000; font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; font-size: 12pt;">@throws</span><span style="color: #008000; font-size: 12pt;">&nbsp;Exception<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; font-size: 12pt;">*/</span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff; font-size: 12pt;">public</span>&nbsp;<span style="color: #0000ff; font-size: 12pt;">static</span>&nbsp;<span style="color: #0000ff; font-size: 12pt;">void</span><span style="font-size: 12pt;">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span style="color: #0000ff; font-size: 12pt;">throws</span><span style="font-size: 12pt;">&nbsp;Exception&nbsp;{</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ThreadPoolExecutor&nbsp;executor&nbsp;</span><span style="font-size: 12pt;">=</span><span style="font-size: 12pt;">&nbsp;(ThreadPoolExecutor)newFixedThreadPool(</span><span style="font-size: 12pt;">10</span><span style="font-size: 12pt;">);</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; font-size: 12pt;">//</span><span style="color: #008000; font-size: 12pt;">1:JDK8以前，通过匿名内部类实现函数式接口</span><span style="color: #008000; "><br /></span><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;executor.submit(</span><span style="color: #0000ff; font-size: 12pt;">new</span><span style="font-size: 12pt;">&nbsp;Runnable()&nbsp;{</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff; font-size: 12pt;">public</span>&nbsp;<span style="color: #0000ff; font-size: 12pt;">void</span><span style="font-size: 12pt;">&nbsp;run()&nbsp;{</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">JDK8以前，通过匿名内部类实现函数式接口</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">);</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; font-size: 12pt;">//</span><span style="color: #008000; font-size: 12pt;">2:JDK8以后可以使用lambda&nbsp;表达式来实现，lambda表达式就是为了优化匿名内部类而生(分开写效果)</span><span style="color: #008000; "><br /></span><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread&nbsp;thread&nbsp;</span><span style="font-size: 12pt;">=</span>&nbsp;<span style="color: #0000ff; font-size: 12pt;">new</span><span style="font-size: 12pt;">&nbsp;Thread(()&nbsp;</span><span style="font-size: 12pt;">-&gt;</span><span style="font-size: 12pt;">&nbsp;System.out.println(</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">task&nbsp;running&nbsp;!</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">));</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Runnable&nbsp;r&nbsp;</span><span style="font-size: 12pt;">=</span><span style="font-size: 12pt;">&nbsp;()&nbsp;</span><span style="font-size: 12pt;">-&gt;</span><span style="font-size: 12pt;">&nbsp;System.out.println(</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">JDK8以后可以使用lambda&nbsp;表达式来实现，lambda表达式就是为了优化匿名内部类而生(分开写效果)!</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">);</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;executor.submit(r);</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; font-size: 12pt;">//</span><span style="color: #008000; font-size: 12pt;">3:合并起来效果</span><span style="color: #008000; "><br /></span><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;executor.submit(()&nbsp;</span><span style="font-size: 12pt;">-&gt;</span><span style="font-size: 12pt;">&nbsp;{</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">JDK8以后可以使用lambda&nbsp;表达式来实现，lambda表达式就是为了优化匿名内部类而生!</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">);</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});</span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; font-size: 12pt;">//</span><span style="color: #008000; font-size: 12pt;">4:其它&nbsp;Supplier、Function、Consumer、Predicate&nbsp;都可以用lambda&nbsp;表达式来实现</span><span style="color: #008000; "><br /></span><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Supplier</span><span style="font-size: 12pt;">&lt;</span><span style="font-size: 12pt;">String</span><span style="font-size: 12pt;">&gt;</span><span style="font-size: 12pt;">&nbsp;supplier&nbsp;</span><span style="font-size: 12pt;">=</span><span style="font-size: 12pt;">&nbsp;()&nbsp;</span><span style="font-size: 12pt;">-&gt;</span>&nbsp;<span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">我是SuSupplier</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">;</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Supplier</span><span style="font-size: 12pt;">&lt;</span><span style="font-size: 12pt;">Integer</span><span style="font-size: 12pt;">&gt;</span><span style="font-size: 12pt;">&nbsp;supplier2&nbsp;</span><span style="font-size: 12pt;">=</span><span style="font-size: 12pt;">&nbsp;()&nbsp;</span><span style="font-size: 12pt;">-&gt;</span>&nbsp;<span style="color: #0000ff; font-size: 12pt;">new</span><span style="font-size: 12pt;">&nbsp;Integer(</span><span style="font-size: 12pt;">1</span><span style="font-size: 12pt;">);</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">supplier=</span><span style="font-size: 12pt;">"</span>&nbsp;<span style="font-size: 12pt;">+</span><span style="font-size: 12pt;">&nbsp;supplier.get()&nbsp;</span><span style="font-size: 12pt;">+</span>&nbsp;<span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">;supplier2=</span><span style="font-size: 12pt;">"</span>&nbsp;<span style="font-size: 12pt;">+</span><span style="font-size: 12pt;">&nbsp;supplier2.get());</span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; font-size: 12pt;">//</span><span style="color: #008000; font-size: 12pt;">5:&nbsp;Function功能型函数式接口&nbsp;Function&lt;T,&nbsp;R&gt;&nbsp;接受一个输入参数T，返回一个结果R</span><span style="color: #008000; "><br /></span><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Function</span><span style="font-size: 12pt;">&lt;</span><span style="font-size: 12pt;">String,Integer</span><span style="font-size: 12pt;">&gt;</span><span style="font-size: 12pt;">&nbsp;function</span><span style="font-size: 12pt;">=</span><span style="font-size: 12pt;">str&nbsp;</span><span style="font-size: 12pt;">-&gt;</span><span style="font-size: 12pt;">&nbsp;Integer.parseInt(str);</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Function</span><span style="font-size: 12pt;">&lt;</span><span style="font-size: 12pt;">Integer,String</span><span style="font-size: 12pt;">&gt;</span><span style="font-size: 12pt;">&nbsp;fun2&nbsp;</span><span style="font-size: 12pt;">=</span><span style="font-size: 12pt;">&nbsp;item&nbsp;</span><span style="font-size: 12pt;">-&gt;</span><span style="font-size: 12pt;">&nbsp;item</span><span style="font-size: 12pt;">+</span><span style="font-size: 12pt;">""</span><span style="font-size: 12pt;">;</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">输入字符型&nbsp;1&nbsp;返回int型结果：</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">+</span><span style="font-size: 12pt;">function.apply(</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">1</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">));</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">输入整型&nbsp;1&nbsp;返回字符型结果：</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">+</span><span style="font-size: 12pt;">fun2.apply(</span><span style="font-size: 12pt;">2</span><span style="font-size: 12pt;">));</span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; font-size: 12pt;">//</span><span style="color: #008000; font-size: 12pt;">6:&nbsp;Consumer&nbsp;一个接受单个输入参数并且不返回结果的操作。&nbsp;与大多数其他函数接口不同，&nbsp;Consumer接口期望通过接受参数，改普通对象引用值(说明白点就是对原来的值进行加工，注意返回值&nbsp;void)</span><span style="color: #008000; "><br /></span><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Consumer</span><span style="font-size: 12pt;">&lt;</span><span style="font-size: 12pt;">StringBuffer</span><span style="font-size: 12pt;">&gt;</span><span style="font-size: 12pt;">&nbsp;consumer</span><span style="font-size: 12pt;">=</span><span style="font-size: 12pt;">&nbsp;sb</span><span style="font-size: 12pt;">-&gt;</span><span style="font-size: 12pt;">sb.append(</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">-yyy</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">);</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuffer&nbsp;sb1</span><span style="font-size: 12pt;">=</span><span style="color: #0000ff; font-size: 12pt;">new</span><span style="font-size: 12pt;">&nbsp;StringBuffer().append(</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">111</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">);</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;consumer.accept(sb1);</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; font-size: 12pt;">//</span><span style="color: #008000; font-size: 12pt;">改变sb的内部引用值</span><span style="color: #008000; "><br /></span><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">=========s=</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">+</span><span style="font-size: 12pt;">sb1.toString());</span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; font-size: 12pt;">//</span><span style="color: #008000; font-size: 12pt;">7:&nbsp;Predicate&lt;T&gt;&nbsp;断言型接口常用于集合的过滤，得到一个新的集合&nbsp;Stream&nbsp;filter(Predicate&lt;?&nbsp;super&nbsp;T&gt;&nbsp;predicate);</span><span style="color: #008000; "><br /></span><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Predicate</span><span style="font-size: 12pt;">&lt;</span><span style="font-size: 12pt;">Integer</span><span style="font-size: 12pt;">&gt;</span><span style="font-size: 12pt;">&nbsp;predicate&nbsp;</span><span style="font-size: 12pt;">=</span><span style="font-size: 12pt;">&nbsp;age&nbsp;</span><span style="font-size: 12pt;">-&gt;</span><span style="font-size: 12pt;">&nbsp;age&nbsp;</span><span style="font-size: 12pt;">&gt;</span>&nbsp;<span style="font-size: 12pt;">18</span><span style="font-size: 12pt;">;</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Predicate</span><span style="font-size: 12pt;">&lt;</span><span style="font-size: 12pt;">String</span><span style="font-size: 12pt;">&gt;</span><span style="font-size: 12pt;">&nbsp;predicate2&nbsp;</span><span style="font-size: 12pt;">=</span><span style="font-size: 12pt;">&nbsp;str&nbsp;</span><span style="font-size: 12pt;">-&gt;</span><span style="font-size: 12pt;">&nbsp;str&nbsp;</span><span style="font-size: 12pt;">!=</span>&nbsp;<span style="color: #0000ff; font-size: 12pt;">null</span><span style="font-size: 12pt;">;</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(predicate.test(</span><span style="font-size: 12pt;">19</span><span style="font-size: 12pt;">));</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(predicate2.test(</span><span style="color: #0000ff; font-size: 12pt;">null</span><span style="font-size: 12pt;">));</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; font-size: 12pt;">//</span><span style="color: #008000; font-size: 12pt;">我们常用集合过滤类就是对这个接口实现类&nbsp;其中&nbsp;filter(Predicate&lt;?&nbsp;super&nbsp;T&gt;&nbsp;predicate)&nbsp;用的就是这个接口</span><span style="color: #008000; "><br /></span><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List</span><span style="font-size: 12pt;">&lt;</span><span style="font-size: 12pt;">String</span><span style="font-size: 12pt;">&gt;</span><span style="font-size: 12pt;">&nbsp;list</span><span style="font-size: 12pt;">=</span><span style="font-size: 12pt;">&nbsp;Lists.newArrayList(</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">1</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">,</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">2</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">,</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">2</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">,</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">3</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">,</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">4</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">,</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">4</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">,</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">8</span><span style="font-size: 12pt;">"</span><span style="font-size: 12pt;">);</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.stream().map(s&nbsp;</span><span style="font-size: 12pt;">-&gt;</span><span style="font-size: 12pt;">&nbsp;Long.parseLong(s)).distinct().filter(s&nbsp;</span><span style="font-size: 12pt;">-&gt;</span><span style="font-size: 12pt;">&nbsp;s&nbsp;</span><span style="font-size: 12pt;">&lt;</span>&nbsp;<span style="font-size: 12pt;">10</span><span style="font-size: 12pt;">).collect(Collectors.toList()).forEach(</span><span style="font-size: 12pt;">u&nbsp;</span><span style="font-size: 12pt;">-&gt;</span><span style="font-size: 12pt;">&nbsp;System.out.println(u));</span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; font-size: 12pt;">//</span><span style="color: #008000; font-size: 12pt;">总结，以上的例子其实都是JDK8&nbsp;lambda&nbsp;表达式简洁的写法，而且全是合并写的，并没有分开步骤写(所有函数性接口，都可以用lambda&nbsp;表达式简洁写法)<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; font-size: 12pt;">//</span><span style="color: #008000; font-size: 12pt;">关闭线程池</span><span style="color: #008000; "><br /></span><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;executor.shutdownNow();</span><br /><span style="font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<ul>
</ul>
</div>
<div></div><img src ="http://www.blogjava.net/zzzlyr/aggbug/435611.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zzzlyr/" target="_blank">为自己代言</a> 2020-07-24 15:46 <a href="http://www.blogjava.net/zzzlyr/articles/435611.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDK 8 CompletableFuture 详细使用介绍</title><link>http://www.blogjava.net/zzzlyr/articles/435608.html</link><dc:creator>为自己代言</dc:creator><author>为自己代言</author><pubDate>Thu, 23 Jul 2020 11:29:00 GMT</pubDate><guid>http://www.blogjava.net/zzzlyr/articles/435608.html</guid><wfw:comment>http://www.blogjava.net/zzzlyr/comments/435608.html</wfw:comment><comments>http://www.blogjava.net/zzzlyr/articles/435608.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zzzlyr/comments/commentRss/435608.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zzzlyr/services/trackbacks/435608.html</trackback:ping><description><![CDATA[<pre><pre><br style="font-size: 14pt;" /></pre><h2><span style="font-size: 14pt;">JDK 8 中 CompletableFuture 是对 Future 的增强 大大简化了异步编程步骤,在Spring 框架中配合@EnableAsync @Async 更加事办功倍。</span></h2><span style="font-size: 14pt;">
1:在JDK 8 之前实现多线必需实现两个接口 Runnable 不带返回值，另一个Callable带返回值的接口,配合ThreadPoolTaskExecutor.submit(Callable callable) 返回一个Future对象。

使用Future获得异步执行结果时，要么调用阻塞方法get()，要么轮询看isDone()是否为true，这两种方法都不是很好，因为主线程也会被迫等待，而CompletableFuture出现改变了这个问题，而且提供更多并且强大的其它功能。<br /></span><span style="font-size: 14pt;">
2：CompletableFuture简介

CompletableFuture&lt;T&gt; implements Future&lt;T&gt;, CompletionStage&lt;T&gt;

其实CompletableFuture 除了实现原来的Future 接口外，其它大部分方法都是在CompletionStage中
<img src="https://image-static.segmentfault.com/409/022/4090228150-5d99fb9c65eb8_articlex" alt="CompletableFuture 类图" /><br /></span><span style="font-size: 14pt;">
大致介绍下completableFuture的命名规则</span><span style="font-size: 14pt;">：</span><br /><span>
</span><h3><span style="font-size: 14pt;">1:按功能分类的话：</span></h3><ul><li><code><span style="font-size: 14pt;">xxx()</span></code><span style="font-size: 14pt;">：表示该方法将继续在已有的线程中执行；</span></li><br /><li><code><span style="font-size: 14pt;">xxxAsync()</span></code><span style="font-size: 14pt;">：表示将异步在线程池中执行。</span></li><br /><li><span style="font-size: 14pt;">异步执行方法默认一个参数的话任务是在&nbsp;</span><code><span style="font-size: 14pt;">ForkJoinPool.commonPool()</span></code><span style="font-family: verdana, &quot;courier new&quot;; font-size: 14pt;">&nbsp;线程池中执行的，带executor&nbsp;参数的使用&nbsp;executor线程池异步执行。</span></li></ul><br /><h3><span style="font-size: 14pt;">2:按逻辑和组织方式来分话(completableFuture 中大约有50个来方法)</span></h3><ul><li><span style="font-size: 14pt;">一种是 then 的逻辑，即前一个计算完成的时候调度后一个计算</span></li><br /><li><span style="font-size: 14pt;">一种是 both 的逻辑，即等待两个计算都完成之后执行下一个计算，只要能组合一个和另一个，我们就可以无限复用这个 +1 的逻辑组合任意多的计算</span></li><br /><li><span style="font-size: 14pt;">另一种是 either 的逻辑，即等待两个计算的其中一个完成之后执行下一个计算。注意这样的计算可以说是非确定性的。因为被组合的两个计算中先触发下一个计算执行的那个会被作为前一个计算，而这两个前置的计算到底哪一个先完成是不可预知的</span></li></ul><h3><span style="font-size: 14pt;">3:从依赖关系和出入参数类型区别，基本分为三类：</span></h3><ul><li><span style="font-size: 14pt;">apply 字样的方式意味着组合方式是&nbsp;</span><code><span style="font-size: 14pt;">Function</span></code><span style="font-size: 14pt;">，即接受前一个计算的结果，应用函数之后返回一个新的结果</span></li><br /><li><span style="font-size: 14pt;">accept 字样的方式意味着组合方式是&nbsp;</span><code><span style="font-size: 14pt;">Consumer</span></code><span style="font-size: 14pt;">，即接受前一个计算的结果，执行消费后不返回有意义的值</span></li><br /><li><span style="font-size: 14pt;">run 字样的方式意味着组合方式是&nbsp;</span><code><span style="font-size: 14pt;">Runnable</span></code><span style="font-family: verdana, &quot;courier new&quot;; font-size: 14pt;">，即忽略前一个计算的结果，仅等待它完成后执行动作</span></li></ul><span style="font-size: 14pt;">其中出入参数主要有JDK8 Function，Consumer或Runnable三中函数型接口,每一种都决定了是怎么样一种依赖关系,我有一篇文章详细介绍了JDK8函数型接口用法，能有助理解completableFuture方法使用。</span><br /><a href="http://www.blogjava.net/zzzlyr/articles/435611.html"><span style="font-size: 14pt;">http://www.blogjava.net/zzzlyr/articles/435611.html</span></a><br /><pre><h3><span style="font-size: 14pt;">4:completableFuture 配合框架使用</span></h3><pre><span style="font-size: 14pt;">  因为自从JDK8以后增强了多线程的使用便捷程度：</span><br /><span style="font-size: 14pt;">  1：JDk8 的函数式接口和lambda表过式</span><br /><span style="font-size: 14pt;">  2：completableFuture 对 Future 类的增强。</span><br /><span style="font-size: 14pt;"> 这只是JDK 基础包中的功能，现在大部分开发都在使用框架 java 现在基本上都在使用spring框架，因为JDK基础包中的功能还是不如框架使用方便，下边文章详细介绍 springboot中对JDK基础包中多线程功能配置和使用。 </span><br /></pre><a href="http://www.blogjava.net/zzzlyr/articles/435305.html"><span style="font-size: 14pt;">http://www.blogjava.net/zzzlyr/articles/435305.html</span></a><br />
<pre><br /></pre>
</pre>
<pre><br />
</pre>
<pre><br />
</pre>
</pre><img src ="http://www.blogjava.net/zzzlyr/aggbug/435608.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zzzlyr/" target="_blank">为自己代言</a> 2020-07-23 19:29 <a href="http://www.blogjava.net/zzzlyr/articles/435608.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SpringBoot异步及线程池配置(jdk8新增加线程支持CompletableFuture)</title><link>http://www.blogjava.net/zzzlyr/articles/435305.html</link><dc:creator>为自己代言</dc:creator><author>为自己代言</author><pubDate>Wed, 25 Mar 2020 12:19:00 GMT</pubDate><guid>http://www.blogjava.net/zzzlyr/articles/435305.html</guid><wfw:comment>http://www.blogjava.net/zzzlyr/comments/435305.html</wfw:comment><comments>http://www.blogjava.net/zzzlyr/articles/435305.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zzzlyr/comments/commentRss/435305.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zzzlyr/services/trackbacks/435305.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 如何在 Spring 使用@Async,@EnableAsync注释进行异步处理:异步处理适用那些与业务逻辑（横切关注点）不直接相关或者不作为其他业务逻辑输入的部分，也可在分布式系统中解耦。*译注：横切关注点（cross-cutting concerns）指一些具有横越多个模块的行为，使用传统的软件开发方法不能够达到有效模块化的一类特殊关注点。*Spring 中，`@Async`注解可以标记异步操...&nbsp;&nbsp;<a href='http://www.blogjava.net/zzzlyr/articles/435305.html'>阅读全文</a><img src ="http://www.blogjava.net/zzzlyr/aggbug/435305.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zzzlyr/" target="_blank">为自己代言</a> 2020-03-25 20:19 <a href="http://www.blogjava.net/zzzlyr/articles/435305.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>