<div class="htmledit_views" id="content_views"> <p>原文出处:</p>html
<p>https://mp.weixin.qq.com/s/p3JZERyZXnF8jR_3KKIGJA</p>java
<p>写做本文时参考了《java并发编程实战》、《java并发编程的艺术》、java源码以及一些博客文章,力求把这个专题的知识讲的足够全面,最重要的是通俗易懂,如文中有错误请与我联系。为保证你把这些知识彻底掌握,最好使用电脑观看,投机取巧是学不到东西的,对本身诚实一点哈。</p>程序员
<h3><a name="t0"></a><a name="t0"></a>任务执行的几种方式</h3>编程
<p>咱们先来看一个现实生活中的例子,拿银行来讲,天天都会有不少的客户来办理业务,每一个人办理一次业务均可以称为一个<code>任务</code>,为了圆满的完成这些任务,银行能够按照下边的这几种方式来安排工做。</p>缓存
<p>串行执行</p>并发
<p>银行可让等待办理业务的客户排成一道长龙,只留一个业务员来办理业务,对应到java程序里,就至关于用1个线程来依次处理全部的任务,这种执行方式咱们称之为<code>串行执行</code>。</p>框架
<p><code>串行执行</code>的劣势很是好理解,假若有一个办理业务的客户十分墨迹,将影响到后边全部排队的客户,对应到线程里,若是有一个任务里调用了某些阻塞操做,好比<code>I/O</code>操做,那后边的任务将须要等待很长时间,用java代码表示出来就像这个样子:</p>ide
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">(List<Runnable> runnables)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">for</span> (Runnable r : runnables) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> r.run();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>工具
<p>为每一个任务建立一个线程</p>学习
<p>考虑到用一个业务员来处理业务太慢了,每天遭客户投诉,新来的行长拍大腿决定每一个客户都要有一个业务员单独服务,用java语言表述就是为每一个任务来建立一个线程单独处理。这样一开始办理业务的客户很少的时候这一招的确听有效,客户们不用再排队了,也不用由于前边的某个客户十分墨迹而影响到后边客户的处理效率了,因此银行一天能接待的客户也明显增多了。用java语言描述,就是程序的<code>响应性</code>和<code>吞吐量</code>明显提升了,接下来用java代码表示一下这个过程:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">(List<Runnable> runnables)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">for</span> (Runnable r : runnables) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">new</span> Thread(r).start();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>可是随着办理业务的人愈来愈多,这种模式的弊端慢慢的暴露出来了,好比须要不少的业务员,每一个业务员上岗前都要培训就浪费了不少时间,而后银行的柜台渐渐不够用了,慢慢的办理业务的人连大厅都放不下了,人越多越混乱了,致使最后的效率其实不升反降了。对于java的线程来讲也存在这种问题,咱们前边说过,线程的建立和销毁都是须要系统资源的,并且线程的上下文切换也要浪费不少时间,线程越多浪费的时间越多;在相同时间内分配在每一个线程头上的处理时间就越少;每一个线程在执行任务的时候都须要分配内存,若是无限制的建立线程,确定会致使内存不够用的,并且通常的操做系统对线程的建立数量是有限制的。</p>
<p>因此结论就是:在必定范围内增长线程数量的确能够提高系统的处理能力,可是过多的建立线程将会下降系统的处理速度,若是无限制的建立线程,将会使系统崩溃。</p>
<p>线程的重复利用</p>
<p>在经历了一次银行崩溃以后,行长决定只留下10个业务员,让办理业务的客户都排成一队,哪一个业务员空闲,就把队头的客户叫走,这样便可以提高处理效率,又能够避免业务员太多带来的烦恼,完美!咱们用java代码表示一下:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">(List<Runnable> runnables)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">final</span> Queue<Runnable> queue = <span class="hljs-keyword">new</span> ConcurrentLinkedQueue<>(runnables); <span class="hljs-comment">//同步队列</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">10</span>; i++) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">new</span> Thread(<span class="hljs-keyword">new</span> Runnable() {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-meta">@Override</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="8"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="9"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="10"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> Runnable r = queue.poll();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="11"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">if</span> (r == <span class="hljs-keyword">null</span>) { <span class="hljs-comment">//若是没任务了就退出</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="12"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">break</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="13"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="14"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> r.run(); <span class="hljs-comment">//执行任务</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="15"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="16"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="17"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }).start();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="18"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="19"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">} </div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>能够看到,咱们一共建立了10个线程,每一个线程都试图从任务队列中取任务来作,直到任务队列里没有任务了才退出。</p>
<h3><a name="t1"></a><a name="t1"></a>Executor框架</h3>
<p>咱们上边唠叨的3种任务的执行方式用一个高大上的词儿描述就是任务的<code>执行策略</code>,每一个<code>执行策略</code>都要规定不少任务的执行细节,好比用多少线程去执行任务;是在把线程都建立好以后再去处理任务,仍是遇到任务以后再建立线程;建立好的线程在空闲的时候能不能销毁等等的问题,若是咱们乐意,咱们能够定义不少的<code>执行策略</code>。</p>
<p>Executor接口的提出</p>
<p>其实对于客户端程序员来讲,每次在执行某些任务的时候都要设计一种新的执行策略太累人了,因此设计java的大叔们就帮助客户端程序员设计了这样的一个接口:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Executor</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">execute</span><span class="hljs-params">(Runnable command)</span></span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p><code>Executor</code>翻译过来的意思就是<code>执行器</code>,设计java的大叔们针对不一样的<code>执行策略</code>定义了不一样的<code>Executor</code>子类(咱们稍后介绍)。客户端程序员只须要把<code>Runnable</code>任务放到<code>执行器</code>的<code>execute</code>方法里就表示任务提交了,具体提交之后这些任务怎么分配线程怎么执行就无论了。这也就是:把任务的提交和执行解耦开来了。咱们先举个例子看一下<code>执行器</code>怎么用:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">(List<Runnable> runnables)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> Executor executor = Executors.newFixedThreadPool(<span class="hljs-number">10</span>); <span class="hljs-comment">//建立包含10个线程的执行器</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">for</span> (Runnable r : runnables) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> executor.execute(r); <span class="hljs-comment">//提交任务</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>其中的<code>Executors</code>类提供了一系列建立<code>Executor</code>子类的静态方法,上边<code>newFixedThreadPool(10)</code>方法表明建立了一个包含10个线程<code>Executor</code>,能够用这10个线程去执行任务。更多的使用方式咱们下节详细唠叨。</p>
<p>咱们也能够自定义本身的<code>执行策略</code>,好比对于<code>串行执行</code>的策略,能够定义一个这样的<code>Executor</code>子类:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SerialExecutor</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Executor</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-meta">@Override</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">execute</span><span class="hljs-params">(Runnbale r)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> r.run();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>对于<code>为每一个任务建立一个线程</code>的策略,能够定义一个这样的子类:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThreadPerRunnalbeExecutor</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Executor</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-meta">@Override</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">executor</span><span class="hljs-params">(Runnbale r)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">new</span> Thread(r).start();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>咱们下边深刻看一下java类库中都针对哪些<code>执行策略</code>提供了<code>执行器</code>。</p>
<p>类库中的线程池</p>
<p>因为<code>Executor</code>子类负责执行任务,因此里边确定要包含一些线程,才能使用这些线程去执行任务,因此<code>Executor</code>子类也叫作<code>线程池</code>,寓意盛放线程的池子。通常状况下,调用 Executor 的 execute方法会把该任务塞到一个队列中,而后线程池中的线程从这个队列中取任务执行。</p>
<p><code>Executors</code>类里提供了建立适用于各类场景<code>线程池</code>的工具方法(静态方法),咱们看一下经常使用的几个:</p>
<pre class="has" name="code"><code class="hljs go">注意,是<span class="hljs-string">`Executors`</span>类,不是<span class="hljs-string">`Executor`</span>接口,不要认错哦。就和<span class="hljs-string">`Collections`</span>和<span class="hljs-string">`Collection`</span>不是一个东西同样~ </code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p><img alt="" class="has" height="168" src="https://img2018.cnblogs.com/blog/1112483/201907/1112483-20190715215250397-1400413508.png" width="679"></p>
<p> </p>
<p> </p>
<p>你们能够看到有的方法返回类型是<code>ExecutorService</code>,有的是<code>ScheduledExecutorService</code>,其实这两个都是<code>Executor</code>的子接口,稍后咱们会详细说道的。下边咱们看一下这几个方法的详细用法。</p>
<p>newFixedThreadPool(int nThreads)</p>
<p>经过此方法能够建立一个拥有固定线程数量的线程池,具体的线程数量由<code>nThreads</code>参数指定。</p>
<p>这里所说的固定线程数量的线程池的意思是:最开始该线程池中的线程数为0,以后每提交一个任务就会建立一个线程,直到线程数等于指定的<code>nThreads</code>参数,此后线程数量将再也不变化。</p>
<p>newCachedThreadPool()</p>
<p>经过此方法能够建立一个可缓存的线程池。</p>
<p>可缓存的意思是:会为每一个任务都分配一个线程,可是若是一个线程执行完任务后长时间(60秒)没有新的任务可执行,该线程将被回收。</p>
<p>newSingleThreadExecutor()</p>
<p>经过此方法能够建立单线程的线程池。</p>
<p>其实只有一个线程也很差意思叫线程池了,不过为了统一名称,也就勉强叫一声线程池吧~ 被提交到该线程的任务将在一个线程中<code>串行</code>执行,而且能确保任务能够按照队列中的顺序串行执行。</p>
<p>newScheduledThreadPool(int corePoolSize)</p>
<p>经过此方法能够建立固定线程数量的线程池,并且以延迟或定时的方式来执行任务。怎么以延迟或定时的方式执行任务呢?咱们看一下该方法的返回类型<code>ScheduledExecutorService</code>里提供的几个方法:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln" style="width:1060px"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ScheduledExecutorService</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ExecutorService</span> </span>{ </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">public</span> ScheduledFuture<?> schedule(Runnable command, <span class="hljs-keyword">long</span> delay, TimeUnit unit);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">public</span> <V> <span class="hljs-function">ScheduledFuture<V> <span class="hljs-title">schedule</span><span class="hljs-params">(Callable<V> callable, <span class="hljs-keyword">long</span> delay, TimeUnit unit)</span></span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">public</span> ScheduledFuture<?> scheduleAtFixedRate(Runnable command, <span class="hljs-keyword">long</span> initialDelay, <span class="hljs-keyword">long</span> period, TimeUnit unit);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="8"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="9"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">public</span> ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, <span class="hljs-keyword">long</span> initialDelay, <span class="hljs-keyword">long</span> delay, TimeUnit unit);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="10"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>真的很抱歉,这段代码里出现了<code>ScheduledFuture</code>和<code>Callable</code>两个你可能不认识的东东,咱们先不理它们,以后说到的时候再看,返回这个线程池,看看这几个方法(参数中带<code>Callable</code>的先略过)都是干什么的:</p>
<p><img alt="" class="has" height="262" src="https://img2018.cnblogs.com/blog/1112483/201907/1112483-20190715215327144-366079967.png" width="676"></p>
<p>其中后两个方法的参数都是如出一辙的,并且意义貌似也有点像,那就举个现实生活的例子吧,好比有不少同窗在跑道上跑圈。</p>
<p><code>scheduleAtFixedRate</code>规定在前边的同窗出发1分钟后第后边同窗就能够跑了,若是在1分钟以内前边的同窗顺利跑完了1圈,后边的同窗就能够按照约定出发,不然的话虽然1分钟时间到了,后边的同窗仍是要等待前边的同窗到达终点后才能够出发。</p>
<p><code>scheduleWithFixedDelay</code>规定在前边的同窗跑完一圈以后再过1分钟才能出发。</p>
<p>示例</p>
<p>好了,知道了这些方法是神马意思,写个程序瞅瞅:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">import</span> java.util.concurrent.Executors;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">import</span> java.util.concurrent.ScheduledExecutorService;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">import</span> java.util.concurrent.TimeUnit;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ScheduleDemo</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PrintTask</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Runnable</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="8"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="9"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">private</span> String s;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="10"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="11"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">PrintTask</span><span class="hljs-params">(String s)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="12"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">this</span>.s = s;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="13"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="14"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="15"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-meta">@Override</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="16"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="17"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> System.out.println(s);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="18"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="19"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="20"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="21"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="22"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> ScheduledExecutorService service = Executors.newScheduledThreadPool(<span class="hljs-number">10</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="23"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="24"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-comment">//隔1秒后打印</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="25"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> service.schedule(<span class="hljs-keyword">new</span> PrintTask(<span class="hljs-string">"1"</span>), <span class="hljs-number">1</span>, TimeUnit.SECONDS);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="26"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="27"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-comment">//首次5秒后打印,每隔1秒打印一次</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="28"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> service.scheduleAtFixedRate(<span class="hljs-keyword">new</span> PrintTask(<span class="hljs-string">"2"</span>), <span class="hljs-number">5</span>, <span class="hljs-number">1</span>, TimeUnit.SECONDS);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="29"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="30"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>Callable与Future</p>
<p>咱们以前都是以<code>Runnable</code>表示任务的,再次看一下这个接口:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Runnable</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span></span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>它里边只有一个返回<code>void</code>的<code>run</code>方法,咱们定义一个计算两个值大小的<code>Runnable</code>:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AddTask</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Runnable</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> i;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> j;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AddTask</span><span class="hljs-params">(<span class="hljs-keyword">int</span> i, <span class="hljs-keyword">int</span> j)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="8"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">this</span>.i = i;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="9"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">this</span>.j = j;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="10"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="11"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="12"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-meta">@Override</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="13"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="14"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">int</span> sum = i + j;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="15"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> System.out.println(<span class="hljs-string">"线程t的运算结果:"</span> + sum);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="16"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="17"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>下边咱们在<code>main</code>线程中建立一个<code>t</code>线程去执行这个任务:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> AddTask task = <span class="hljs-keyword">new</span> AddTask(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> Thread t = <span class="hljs-keyword">new</span> Thread(task, <span class="hljs-string">"t"</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> t.start();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>执行结果是:</p>
<pre class="has" name="code"><code class="hljs">线程t的运算结果:3 </code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>以上是一个很常见的显式建立线程去运行<code>Runnable</code>任务的使用案例,可是这种方式有个问题,就是<code>main</code>线程一旦调用了<code>t</code>线程的<code>start</code>方法,这两个线程就好像不要紧了,也就是说main线程既不知道t线程如今是否是运行完了,也不知道t线程执行的任务的结果是什么,也不知道在运行任务的过程当中是否是发生了异常。因此设计java的大叔们在设计<code>Executor</code>的各类子类的时候特地把这个事情考虑了进去,提出了带返回值的<code>Callable</code>任务以及用于检测任务执行状况的<code>Future</code>接口。</p>
<p><code>Callable</code>是一个接口,它表明一个任务,与<code>Runnable</code>不一样的是,这个任务是有返回值的:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Callable</span><<span class="hljs-title">V</span>> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function">V <span class="hljs-title">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>咱们能够把<code>AddTask</code>定义成一个<code>Callable</code>任务:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">import</span> java.util.concurrent.Callable;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AddTask</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Callable</span><<span class="hljs-title">Integer</span>> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> i;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> j;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="8"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="9"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AddTask</span><span class="hljs-params">(<span class="hljs-keyword">int</span> i, <span class="hljs-keyword">int</span> j)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="10"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">this</span>.i = i;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="11"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">this</span>.j = j;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="12"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="13"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="14"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-meta">@Override</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="15"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> Integer <span class="hljs-title">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="16"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">int</span> sum = i + j;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="17"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> System.out.println(<span class="hljs-string">"线程main的运算结果:"</span> + sum);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="18"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">return</span> sum;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="19"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="20"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>其中的<code>call</code>方法返回了两个字段的和。这种带返回值的<code>Callable</code>任务不能像<code>Runnable</code>同样直接经过<code>Thread</code>的构造方法传入,在<code>Executor</code>的子接口<code>ExecutorService</code>中规定了<code>Callable</code>任务的提交方式:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ExecutorService</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Executor</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-comment">// 任务提交操做</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <T> <span class="hljs-function">Future<T> <span class="hljs-title">submit</span><span class="hljs-params">(Callable<T> task)</span></span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> Future<?> submit(Runnable task);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <T> <span class="hljs-function">Future<T> <span class="hljs-title">submit</span><span class="hljs-params">(Runnable task, T result)</span></span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="8"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-comment">// 生命周期管理</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="9"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shutdown</span><span class="hljs-params">()</span></span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="10"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function">List<Runnable> <span class="hljs-title">shutdownNow</span><span class="hljs-params">()</span></span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="11"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">boolean</span> <span class="hljs-title">isShutdown</span><span class="hljs-params">()</span></span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="12"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">boolean</span> <span class="hljs-title">isTerminated</span><span class="hljs-params">()</span></span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="13"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">boolean</span> <span class="hljs-title">awaitTermination</span><span class="hljs-params">(<span class="hljs-keyword">long</span> timeout, TimeUnit unit)</span> <span class="hljs-keyword">throws</span> InterruptedException</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="14"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="15"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-comment">// ... 省略了各类方便提交任务的方法</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="16"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>能够看到,<code>ExecutorService</code>主要扩展了任务的提交方式、生命周期管理(稍后立刻唠叨)和一系列方便提交任务的方法(这个没啥意思,等遇到了再说哈),咱们这一节主要唠叨任务的提交方式。这个<code>ExecutorService</code>咱们以前见过,<code>Executors</code>的各类建立<code>线程池</code>的工具方法的返回类型就是它,咱们再重温一遍:</p>
<p><img alt="" class="has" height="173" src="https://img2018.cnblogs.com/blog/1112483/201907/1112483-20190715215356046-1389129749.png" width="680"></p>
<p> </p>
<p>(注:<code>ScheduledExecutorService</code>继承了<code>ExecutorService</code>)</p>
<p>也就是说,各类线程池其实都是实现了 ExecutorService 的,Callable 任务须要提交到线程池中才能运行:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> ExecutorService service = Executors.newCachedThreadPool();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> service.submit(<span class="hljs-keyword">new</span> AddTask(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>));</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>执行结果是:</p>
<pre class="has" name="code"><code class="hljs">线程t的运算结果:3 </code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>带返回值的<code>Callable</code>任务经过<code>线程池</code>的<code>submit</code>方法总算是提交并执行了哈哈。其实无论是<code>Runnable</code>任务仍是<code>Callable</code>任务,线程池执行的任务能够划分为4个生命周期阶段:</p>
<ol><li> <p>建立:建立任务对象的时期。</p> </li> <li> <p>提交:调用线程池的<code>excute</code>或者<code>submit</code>方法后,将任务塞到任务队列的时期。</p> </li> <li> <p>执行中:某个线程从任务队列中将任务取出开始执行的时期。</p> </li> <li> <p>完成:任务执行结束。</p> </li> </ol><p>任务的生命周期只能前进,不能后退,也就是说若是一个任务已经执行完成了,也就是处于<code>完成</code>阶段,那么就没法后退到<code>提交</code>阶段。咱们能够在任务的执行过程当中取消任务,如何取消咱们以后会详细唠叨。若是咱们经过线程池的<code>submit</code>方法提交了任务,那咱们能够获得一个<code>Future</code>对象,它表示一个任务的实时执行状态,并提供了判断是否已经完成或取消的方法,也提供了取消任务和获取任务的运行结果的方法。线程池的<code>submit</code>方法的返回类型<code>Future</code>,实际上是一个接口:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln" style="width:959px"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Future</span><<span class="hljs-title">V</span>> </span>{ </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function">V <span class="hljs-title">get</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException, ExecutionException</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function">V <span class="hljs-title">get</span><span class="hljs-params">(<span class="hljs-keyword">long</span> timeout, TimeUnit unit)</span> <span class="hljs-keyword">throws</span> InterruptedException, ExecutionException, TimeoutException</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">boolean</span> <span class="hljs-title">isDone</span><span class="hljs-params">()</span></span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">boolean</span> <span class="hljs-title">cancel</span><span class="hljs-params">(<span class="hljs-keyword">boolean</span> mayInterruptIfRunning)</span></span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">boolean</span> <span class="hljs-title">isCancelled</span><span class="hljs-params">()</span></span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>各个方法的大体描述以下:</p>
<p><img alt="" class="has" height="222" src="https://img-blog.csdnimg.cn/20190530213238661.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpdWNoYW5namllMDExMg==,size_16,color_FFFFFF,t_70" width="681"></p>
<p>须要注意的是,若是该任务已经完成,那么<code>get</code>方法将会当即返回,若是任务正常完成的话,会返回执行结果,如果抛出异常完成的话,将会将该异常包装成<code>ExecutionException</code>后从新抛出,若是任务被取消,则调用<code>get</code>方法会抛出<code>CancellationExection</code>异常。下边咱们试试<code>Future</code>的用法:</p>
<pre class="has" name="code"><code class="hljs cs"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Test</span> {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(<span class="hljs-params">String[] args</span>) throws Exception</span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> ExecutorService service = Executors.newCachedThreadPool(); <span class="hljs-comment">//建立一个线程池</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> Future<Integer> future = service.submit(<span class="hljs-keyword">new</span> AddTask(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>)); <span class="hljs-comment">//提交一个任务</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">int</span> result = future.<span class="hljs-keyword">get</span>(); <span class="hljs-comment">//在任务执行完成以前,该方法将一直阻塞</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"线程main的运算结果:"</span> + result);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="8"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>执行结果是:</p>
<pre class="has" name="code"><code class="hljs"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">线程t的运算结果:3</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">线程main的运算结果:3</div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>有时候咱们但愿在指定时间内等待另外一个线程的执行结果,那就能够可使用带时间限制的<code>get</code>方法,另外的几个方法咱们以后再详细的说。</p>
<p>视线再返回到<code>ExecutorService</code>接口上来,除了参数类型为<code>Callable</code>的<code>submit</code>方法,这个接口还提供了两个重载方法:</p>
<pre class="has" name="code"><code class="hljs java"><ol class="hljs-ln"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">Future<?> submit(Runnable task); <span class="hljs-comment">//第1个重载方法</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><T> <span class="hljs-function">Future<T> <span class="hljs-title">submit</span><span class="hljs-params">(Runnable task, T result)</span></span>; <span class="hljs-comment">//第2个重载方法</span></div></div></li></ol></code><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div></pre>
<p>对于第1个只有一个<code>Runnable</code>参数的重载方法来讲,因为<code>Runnable</code>的<code>run</code>方法并无返回值,也就是说任务是没有返回值的,因此在该任务完成以后,对应的<code>Future</code>对象的<code>get</code>方法的返回值就是<code>null</code>。虽然不能得到返回值,可是咱们仍是能够调用<code>Future</code>的其余方法,好比<code>isDone</code>表示任务是否已经完成,<code>isCancelled</code>表示任务是否已经被取消,<code>cancel</code>表示尝试取消一个任务(稍后详细说明)。</p>
<p>对于第2个带有两个参数的重载方法来讲,其实和第1个的意思差很少,只不过咱们能够指定该<code>Runnable</code>任务对应的<code>Future</code>对象的<code>get</code>方法的返回值,也就是说方法参数<code>result</code>其实就是<code>get</code>方法的返回值。有的小伙伴会问了,你还没执行任务的时候就把任务返回值给指定了是几个意思???我也不是很清楚,可能有用到的地方吧,你先记住用法就行了。</p>
<p><span style="color:#7c79e5;">小贴士:</span></p>
<p><span style="color:#7c79e5;">若是你已经大体理解了Callable和Future的用法,有没有想过它们是怎么实现的呢?其实无非是线程间通讯的那一套东西,一个线程等待另外一个线程完成任务以后的通知,而后经过某个共享变量来传递数据,若是你有兴趣的话能够试着本身实现一个Callable和Future。</span></p>
<p>若是你从本文中学到的知识有助于你解决眼前的工做、学习问题,或者对你的升职加薪起到了做用,能够点击下方喜欢做者,谢谢~ </p> </div>