Explore

too young, too simple...

理解Callable 和 Spring DeferredResult

英文原文地址:http://xpadro.blogspot.com/2015/07/understanding-callable-and-spring.html <div>原作者:Xavier Padró</div><div>
</div><div>1.简介</div><div>Servlet 3.0引入了异步支持,使程序在其它线程处理HTTP请求成为了可能。当你需要执行长时间任务时,这就十分有趣,当其它线程在处理任务时,容器线程将被释放出来为其它的请求服务。</div><div>这个话题已经解释过多次,但是 Spring框架提供的那些用来实现这一特性的类确实看起来有点令人费解。我将要讨论的是在控制器中返回 Callable和DeferredResult。</div><div>在本文中我将实现两个例子,用来展示两者之间的区别。</div><div>这里的所有例子都是为了实现一个执行长时间任务的控制器,任务执行完之后返回结果给客户端。长时间任务是由 TaskService 来处理的。</div><div><div><pre class="prettyprint linenums prettyprinted"><ol class="linenums"><li class="L0"><span class="lit">@Service</span></li><li class="L1"><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">TaskServiceImpl</span><span class="pln"> </span><span class="kwd">implements</span><span class="pln"> </span><span class="typ">TaskService</span><span class="pln"> </span><span class="pun">{</span></li><li class="L2"><span class="pln"> </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">final</span><span class="pln"> </span><span class="typ">Logger</span><span class="pln"> logger </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LoggerFactory</span><span class="pun">.</span><span class="pln">getLogger</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">getClass</span><span class="pun">());</span></li><li class="L3"><span class="pln"> </span></li><li class="L4"><span class="pln"> </span><span class="lit">@Override</span></li><li class="L5"><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> execute</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span></li><li class="L6"><span class="pln"> </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span></li><li class="L7"><span class="pln"> </span><span class="typ">Thread</span><span class="pun">.</span><span class="pln">sleep</span><span class="pun">(</span><span class="lit">5000</span><span class="pun">);</span></li><li class="L8"><span class="pln"> logger</span><span class="pun">.</span><span class="pln">info</span><span class="pun">(</span><span class="str">"Slow task executed"</span><span class="pun">);</span></li><li class="L9"><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Task finished"</span><span class="pun">;</span></li><li class="L0"><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="typ">InterruptedException</span><span class="pln"> e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span></li><li class="L1"><span class="pln"> </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RuntimeException</span><span class="pun">();</span></li><li class="L2"><span class="pln"> </span><span class="pun">}</span></li><li class="L3"><span class="pln"> </span><span class="pun">}</span></li><li class="L4"><span class="pun">}</span></li></ol></pre></div><div>这个web应用是基于 Spring Boot 搭建的。我们将使用如下代码来执行它:</div></div><div><div><pre class="prettyprint linenums prettyprinted"><ol class="linenums"><li class="L0"><span class="lit">@SpringBootApplication</span></li><li class="L1"><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">MainApp</span><span class="pln"> </span><span class="pun">{</span></li><li class="L2"><span class="pln"> </span></li><li class="L3"><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pun">[]</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span></li><li class="L4"><span class="pln"> </span><span class="typ">SpringApplication</span><span class="pun">.</span><span class="pln">run</span><span class="pun">(</span><span class="typ">MainApp</span><span class="pun">.</span><span class="kwd">class</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">);</span></li><li class="L5"><span class="pln"> </span><span class="pun">}</span></li><li class="L6"><span class="pun">}</span></li></ol></pre></div><div><span style="line-height: 1.6;">这些示例的所有源代码都可以在 Github 仓库 Spring-Rest 中找到。 </span>
</div></div><div><span style="line-height: 1.6;">
</span></div><div>2.开启一个阻塞的控制器</div><div><span style="line-height: 1.6;">在这个示例中,一个请求到达了控制器。在长时间任务执行结束且退出 </span><span style="line-height: 1.6;">@RequestMapping 注解的方法之前,</span><span style="line-height: 1.6;">Servlet 线程不会被释放出来。</span>
</div><div><div><pre class="prettyprint linenums prettyprinted"><ol class="linenums"><li class="L0"><span class="lit">@RestController</span></li><li class="L1"><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">BlockingController</span><span class="pln"> </span><span class="pun">{</span></li><li class="L2"><span class="pln"> </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">final</span><span class="pln"> </span><span class="typ">Logger</span><span class="pln"> logger </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LoggerFactory</span><span class="pun">.</span><span class="pln">getLogger</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">getClass</span><span class="pun">());</span></li><li class="L3"><span class="pln"> </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">final</span><span class="pln"> </span><span class="typ">TaskService</span><span class="pln"> taskService</span><span class="pun">;</span></li><li class="L4"><span class="pln"> </span></li><li class="L5"><span class="pln"> </span><span class="lit">@Autowired</span></li><li class="L6"><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">BlockingController</span><span class="pun">(</span><span class="typ">TaskService</span><span class="pln"> taskService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span></li><li class="L7"><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">taskService </span><span class="pun">=</span><span class="pln"> taskService</span><span class="pun">;</span></li><li class="L8"><span class="pln"> </span><span class="pun">}</span></li><li class="L9"><span class="pln"> </span></li><li class="L0"><span class="pln"> </span><span class="lit">@RequestMapping</span><span class="pun">(</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="str">"/block"</span><span class="pun">,</span><span class="pln"> method </span><span class="pun">=</span><span class="pln"> </span><span class="typ">RequestMethod</span><span class="pun">.</span><span class="pln">GET</span><span class="pun">,</span><span class="pln"> produces </span><span class="pun">=</span><span class="pln"> </span><span class="str">"text/html"</span><span class="pun">)</span></li><li class="L1"><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> executeSlowTask</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span></li><li class="L2"><span class="pln"> logger</span><span class="pun">.</span><span class="pln">info</span><span class="pun">(</span><span class="str">"Request received"</span><span class="pun">);</span></li><li class="L3"><span class="pln"> </span><span class="typ">String</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> taskService</span><span class="pun">.</span><span class="pln">execute</span><span class="pun">();</span></li><li class="L4"><span class="pln"> logger</span><span class="pun">.</span><span class="pln">info</span><span class="pun">(</span><span class="str">"Servlet thread released"</span><span class="pun">);</span></li><li class="L5"><span class="pln"> </span></li><li class="L6"><span class="pln"> </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span></li><li class="L7"><span class="pln"> </span><span class="pun">}</span></li><li class="L8"><span class="pun">}</span></li></ol></pre></div><div><span style="line-height: 1.6;">如果我们访问 http://localhost:8080/block,查看日志,可以发现servlet线程直到长时间任务执行结束后(5秒后)才释放:</span>
</div></div><div><div><pre class="prettyprint linenums prettyprinted"><ol class="linenums"><li class="L0"><span class="lit">2015</span><span class="pun">-</span><span class="lit">07</span><span class="pun">-</span><span class="lit">12</span><span class="pln"> </span><span class="lit">12</span><span class="pun">:</span><span class="lit">41</span><span class="pun">:</span><span class="lit">11.849</span><span class="pln"> </span><span class="pun">[</span><span class="pln">nio</span><span class="pun">-</span><span class="lit">8080</span><span class="pun">-</span><span class="pln">exec</span><span class="pun">-</span><span class="lit">6</span><span class="pun">]</span><span class="pln"> x</span><span class="pun">.</span><span class="pln">s</span><span class="pun">.</span><span class="pln">web</span><span class="pun">.</span><span class="pln">controller</span><span class="pun">.</span><span class="typ">BlockingController</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">Request</span><span class="pln"> received</span></li><li class="L1"><span class="lit">2015</span><span class="pun">-</span><span class="lit">07</span><span class="pun">-</span><span class="lit">12</span><span class="pln"> </span><span class="lit">12</span><span class="pun">:</span><span class="lit">41</span><span class="pun">:</span><span class="lit">16.851</span><span class="pln"> </span><span class="pun">[</span><span class="pln">nio</span><span class="pun">-</span><span class="lit">8080</span><span class="pun">-</span><span class="pln">exec</span><span class="pun">-</span><span class="lit">6</span><span class="pun">]</span><span class="pln"> x</span><span class="pun">.</span><span class="pln">spring</span><span class="pun">.</span><span class="pln">web</span><span class="pun">.</span><span class="pln">service</span><span class="pun">.</span><span class="typ">TaskServiceImpl</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">Slow</span><span class="pln"> task executed</span></li><li class="L2"><span class="lit">2015</span><span class="pun">-</span><span class="lit">07</span><span class="pun">-</span><span class="lit">12</span><span class="pln"> </span><span class="lit">12</span><span class="pun">:</span><span class="lit">41</span><span class="pun">:</span><span class="lit">16.851</span><span class="pln"> </span><span class="pun">[</span><span class="pln">nio</span><span class="pun">-</span><span class="lit">8080</span><span class="pun">-</span><span class="pln">exec</span><span class="pun">-</span><span class="lit">6</span><span class="pun">]</span><span class="pln"> x</span><span class="pun">.</span><span class="pln">s</span><span class="pun">.</span><span class="pln">web</span><span class="pun">.</span><span class="pln">controller</span><span class="pun">.</span><span class="typ">BlockingController</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">Servlet</span><span class="pln"> thread released</span></li></ol></pre></div><div>
</div></div><div><span style="line-height: 1.6;">3.返回Callable</span></div><div>In this example, instead of returning directly the result, we will return a Callable: <span style="line-height: 1.6;">
</span></div><div>在这个范例中,我们不再直接返回结果,而是返回一个 Callable 对象:</div><div><div><pre class="prettyprint linenums prettyprinted"><ol class="linenums"><li class="L0"><span class="lit">@RestController</span></li><li class="L1"><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AsyncCallableController</span><span class="pln"> </span><span class="pun">{</span></li><li class="L2"><span class="pln"> </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">final</span><span class="pln"> </span><span class="typ">Logger</span><span class="pln"> logger </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LoggerFactory</span><span class="pun">.</span><span class="pln">getLogger</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">getClass</span><span class="pun">());</span></li><li class="L3"><span class="pln"> </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">final</span><span class="pln"> </span><span class="typ">TaskService</span><span class="pln"> taskService</span><span class="pun">;</span></li><li class="L4"><span class="pln"> </span></li><li class="L5"><span class="pln"> </span><span class="lit">@Autowired</span></li><li class="L6"><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">AsyncCallableController</span><span class="pun">(</span><span class="typ">TaskService</span><span class="pln"> taskService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span></li><li class="L7"><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">taskService </span><span class="pun">=</span><span class="pln"> taskService</span><span class="pun">;</span></li><li class="L8"><span class="pln"> </span><span class="pun">}</span></li><li class="L9"><span class="pln"> </span></li><li class="L0"><span class="pln"> </span><span class="lit">@RequestMapping</span><span class="pun">(</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="str">"/callable"</span><span class="pun">,</span><span class="pln"> method </span><span class="pun">=</span><span class="pln"> </span><span class="typ">RequestMethod</span><span class="pun">.</span><span class="pln">GET</span><span class="pun">,</span><span class="pln"> produces </span><span class="pun">=</span><span class="pln"> </span><span class="str">"text/html"</span><span class="pun">)</span></li><li class="L1"><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Callable</span><span class="pun"><</span><span class="typ">String</span><span class="pun">></span><span class="pln"> executeSlowTask</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span></li><li class="L2"><span class="pln"> logger</span><span class="pun">.</span><span class="pln">info</span><span class="pun">(</span><span class="str">"Request received"</span><span class="pun">);</span></li><li class="L3"><span class="pln"> </span><span class="typ">Callable</span><span class="pun"><</span><span class="typ">String</span><span class="pun">></span><span class="pln"> callable </span><span class="pun">=</span><span class="pln"> taskService</span><span class="pun">::</span><span class="pln">execute</span><span class="pun">;</span></li><li class="L4"><span class="pln"> logger</span><span class="pun">.</span><span class="pln">info</span><span class="pun">(</span><span class="str">"Servlet thread released"</span><span class="pun">);</span></li><li class="L5"><span class="pln"> </span></li><li class="L6"><span class="pln"> </span><span class="kwd">return</span><span class="pln"> callable</span><span class="pun">;</span></li><li class="L7"><span class="pln"> </span><span class="pun">}</span></li><li class="L8"><span class="pun">}</span></li></ol></pre></div><div><div><span style="line-height: 1.6;">返回 Callable 意味着Spring MVC 将会在其它线程中执行我们在Callable对象中所定义的任务。Spring将会使用 TaskExecutor 来管理这个线程。在等待长时间任务执行完成之前,servlet线程将会被释放。</span>
</div><div>请看日志:</div><div><div><pre class="prettyprint linenums prettyprinted"><ol class="linenums"><li class="L0"><span class="lit">2015</span><span class="pun">-</span><span class="lit">07</span><span class="pun">-</span><span class="lit">12</span><span class="pln"> </span><span class="lit">13</span><span class="pun">:</span><span class="lit">07</span><span class="pun">:</span><span class="lit">07.012</span><span class="pln"> </span><span class="pun">[</span><span class="pln">nio</span><span class="pun">-</span><span class="lit">8080</span><span class="pun">-</span><span class="pln">exec</span><span class="pun">-</span><span class="lit">5</span><span class="pun">]</span><span class="pln"> x</span><span class="pun">.</span><span class="pln">s</span><span class="pun">.</span><span class="pln">w</span><span class="pun">.</span><span class="pln">c</span><span class="pun">.</span><span class="typ">AsyncCallableController</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">Request</span><span class="pln"> received</span></li><li class="L1"><span class="lit">2015</span><span class="pun">-</span><span class="lit">07</span><span class="pun">-</span><span class="lit">12</span><span class="pln"> </span><span class="lit">13</span><span class="pun">:</span><span class="lit">07</span><span class="pun">:</span><span class="lit">07.013</span><span class="pln"> </span><span class="pun">[</span><span class="pln">nio</span><span class="pun">-</span><span class="lit">8080</span><span class="pun">-</span><span class="pln">exec</span><span class="pun">-</span><span class="lit">5</span><span class="pun">]</span><span class="pln"> x</span><span class="pun">.</span><span class="pln">s</span><span class="pun">.</span><span class="pln">w</span><span class="pun">.</span><span class="pln">c</span><span class="pun">.</span><span class="typ">AsyncCallableController</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">Servlet</span><span class="pln"> thread released</span></li><li class="L2"><span class="lit">2015</span><span class="pun">-</span><span class="lit">07</span><span class="pun">-</span><span class="lit">12</span><span class="pln"> </span><span class="lit">13</span><span class="pun">:</span><span class="lit">07</span><span class="pun">:</span><span class="lit">12.014</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> </span><span class="typ">MvcAsync2</span><span class="pun">]</span><span class="pln"> x</span><span class="pun">.</span><span class="pln">spring</span><span class="pun">.</span><span class="pln">web</span><span class="pun">.</span><span class="pln">service</span><span class="pun">.</span><span class="typ">TaskServiceImpl</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">Slow</span><span class="pln"> task executed</span></li></ol></pre></div><div><span style="line-height: 1.6;">可以看见,在长时间任务完成之前,servlet就把线程释放出来了。这并不表示客户端就收不到响应。与客户端之间的通信一直开启,等待执行结果;但是接收请求的线程就已经被释放出来用于处理其它客户端的请求。</span></div></div></div></div><div><span style="line-height: 1.6;">
</span></div><div><span style="line-height: 1.6;">4.返回</span><span style="line-height: 1.6;">DeferredResult</span></div><div><span style="line-height: 1.6;">首先需要创建一个 DeferredResult 对象。它由控制器返回。要达到的目的和 Callable 一样,就是为了及时释放servlet线程,用新的线程去执行长时间任务。</span>
</div><div><div><pre class="prettyprint linenums prettyprinted"><ol class="linenums"><li class="L0"><span class="lit">@RestController</span></li><li class="L1"><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AsyncDeferredController</span><span class="pln"> </span><span class="pun">{</span></li><li class="L2"><span class="pln"> </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">final</span><span class="pln"> </span><span class="typ">Logger</span><span class="pln"> logger </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LoggerFactory</span><span class="pun">.</span><span class="pln">getLogger</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">getClass</span><span class="pun">());</span></li><li class="L3"><span class="pln"> </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">final</span><span class="pln"> </span><span class="typ">TaskService</span><span class="pln"> taskService</span><span class="pun">;</span></li><li class="L4"><span class="pln"> </span></li><li class="L5"><span class="pln"> </span><span class="lit">@Autowired</span></li><li class="L6"><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">AsyncDeferredController</span><span class="pun">(</span><span class="typ">TaskService</span><span class="pln"> taskService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span></li><li class="L7"><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">taskService </span><span class="pun">=</span><span class="pln"> taskService</span><span class="pun">;</span></li><li class="L8"><span class="pln"> </span><span class="pun">}</span></li><li class="L9"><span class="pln"> </span></li><li class="L0"><span class="pln"> </span><span class="lit">@RequestMapping</span><span class="pun">(</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="str">"/deferred"</span><span class="pun">,</span><span class="pln"> method </span><span class="pun">=</span><span class="pln"> </span><span class="typ">RequestMethod</span><span class="pun">.</span><span class="pln">GET</span><span class="pun">,</span><span class="pln"> produces </span><span class="pun">=</span><span class="pln"> </span><span class="str">"text/html"</span><span class="pun">)</span></li><li class="L1"><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">DeferredResult</span><span class="pun"><</span><span class="typ">String</span><span class="pun">></span><span class="pln"> executeSlowTask</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span></li><li class="L2"><span class="pln"> logger</span><span class="pun">.</span><span class="pln">info</span><span class="pun">(</span><span class="str">"Request received"</span><span class="pun">);</span></li><li class="L3"><span class="pln"> </span><span class="typ">DeferredResult</span><span class="pun"><</span><span class="typ">String</span><span class="pun">></span><span class="pln"> deferredResult </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">DeferredResult</span><span class="pun"><>();</span></li><li class="L4"><span class="pln"> </span><span class="typ">CompletableFuture</span><span class="pun">.</span><span class="pln">supplyAsync</span><span class="pun">(</span><span class="pln">taskService</span><span class="pun">::</span><span class="pln">execute</span><span class="pun">)</span></li><li class="L5"><span class="pln"> </span><span class="pun">.</span><span class="pln">whenCompleteAsync</span><span class="pun">((</span><span class="pln">result</span><span class="pun">,</span><span class="pln"> throwable</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-></span><span class="pln"> deferredResult</span><span class="pun">.</span><span class="pln">setResult</span><span class="pun">(</span><span class="pln">result</span><span class="pun">));</span></li><li class="L6"><span class="pln"> logger</span><span class="pun">.</span><span class="pln">info</span><span class="pun">(</span><span class="str">"Servlet thread released"</span><span class="pun">);</span></li><li class="L7"><span class="pln"> </span></li><li class="L8"><span class="pln"> </span><span class="kwd">return</span><span class="pln"> deferredResult</span><span class="pun">;</span></li><li class="L9"><span class="pln"> </span><span class="pun">}</span></li><li class="L0"><span class="pun">}</span></li></ol></pre></div><div><div><span style="line-height: 1.6;">所以,和Callable的区别在哪儿?这一次,线程是由我们自己管理的。我们必须在另一个线程中手动设置结果。</span>
</div><div>在这个范例中我们通过CompletableFuture创建了一个异步任务。长时间任务会在这个新线程中执行。也就是在这个新线程中设置执行结果。</div><div>将会从哪个线程池中获取线程呢?默认情况下, CompletableFuture 的 <span style="line-height: 1.6;">supplyAsync 方法会使用 </span><span style="line-height: 1.6;">ForkJoin</span><span style="line-height: 1.6;"> 池。如果希望使用别的线程池,可以传递一个 executor 对象给 </span><span style="line-height: 1.6;">supplyAsync 方法:</span></div><div><div><pre class="prettyprint linenums prettyprinted"><ol class="linenums"><li class="L0"><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="pun"><</span><span class="pln">U</span><span class="pun">></span><span class="pln"> </span><span class="typ">CompletableFuture</span><span class="pun"><</span><span class="pln">U</span><span class="pun">></span><span class="pln"> supplyAsync</span><span class="pun">(</span><span class="typ">Supplier</span><span class="pun"><</span><span class="pln">U</span><span class="pun">></span><span class="pln"> supplier</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Executor</span><span class="pln"> executor</span><span class="pun">)</span></li></ol></pre></div><div><span style="line-height: 1.6;">如果运行这个范例,将会得到与Callable相同的结果:</span>
</div></div><div><div><pre class="prettyprint linenums prettyprinted"><ol class="linenums"><li class="L0"><span class="lit">2015</span><span class="pun">-</span><span class="lit">07</span><span class="pun">-</span><span class="lit">12</span><span class="pln"> </span><span class="lit">13</span><span class="pun">:</span><span class="lit">28</span><span class="pun">:</span><span class="lit">08.433</span><span class="pln"> </span><span class="pun">[</span><span class="pln">io</span><span class="pun">-</span><span class="lit">8080</span><span class="pun">-</span><span class="pln">exec</span><span class="pun">-</span><span class="lit">10</span><span class="pun">]</span><span class="pln"> x</span><span class="pun">.</span><span class="pln">s</span><span class="pun">.</span><span class="pln">w</span><span class="pun">.</span><span class="pln">c</span><span class="pun">.</span><span class="typ">AsyncDeferredController</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">Request</span><span class="pln"> received</span></li><li class="L1"><span class="lit">2015</span><span class="pun">-</span><span class="lit">07</span><span class="pun">-</span><span class="lit">12</span><span class="pln"> </span><span class="lit">13</span><span class="pun">:</span><span class="lit">28</span><span class="pun">:</span><span class="lit">08.475</span><span class="pln"> </span><span class="pun">[</span><span class="pln">io</span><span class="pun">-</span><span class="lit">8080</span><span class="pun">-</span><span class="pln">exec</span><span class="pun">-</span><span class="lit">10</span><span class="pun">]</span><span class="pln"> x</span><span class="pun">.</span><span class="pln">s</span><span class="pun">.</span><span class="pln">w</span><span class="pun">.</span><span class="pln">c</span><span class="pun">.</span><span class="typ">AsyncDeferredController</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">Servlet</span><span class="pln"> thread released</span></li><li class="L2"><span class="lit">2015</span><span class="pun">-</span><span class="lit">07</span><span class="pun">-</span><span class="lit">12</span><span class="pln"> </span><span class="lit">13</span><span class="pun">:</span><span class="lit">28</span><span class="pun">:</span><span class="lit">13.469</span><span class="pln"> </span><span class="pun">[</span><span class="pln">onPool</span><span class="pun">-</span><span class="pln">worker</span><span class="pun">-</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> x</span><span class="pun">.</span><span class="pln">spring</span><span class="pun">.</span><span class="pln">web</span><span class="pun">.</span><span class="pln">service</span><span class="pun">.</span><span class="typ">TaskServiceImpl</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">Slow</span><span class="pln"> task executed</span></li></ol></pre></div><div>
</div></div></div></div><div><span style="line-height: 1.6;">5.结论</span></div><div><span style="line-height: 1.6;">从高级层面来看,Callable 和 </span><span style="line-height: 1.6;">DeferredResult</span><span style="line-height: 1.6;"> 做了同样的事情,也就是释放容器线程并且在另一个线程中异步处理长时间任务。区别就在于谁来负责管理那个执行长时间任务的线程。</span>
</div><div><span style="line-height: 1.6;">
</span></div><div>
</div>

阅读更多

从零开始

很多年前曾经有习惯写博客。事隔多年,原先的域名没有续费然后丢失,原先的文章数据也没有备份。想找回过去很难。所以就从零开始。

阅读更多