Callable<V>、Future<V>详解 | Executor框架

一:关于 Callable<V>的源码

Java代码   收藏代码
  1. package java.util.concurrent;  
  2.   
  3. public interface Callable<V> {  
  4.     /** 
  5.      * Computes a result, or throws an exception if unable to do so. 
  6.      * 
  7.      * @return computed result 
  8.      * @throws Exception if unable to compute a result 
  9.      */  
  10.     V call() throws Exception;  
  11. }  

Callable<V>: 返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call的方法。Callable<V>接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable不会返回结果,并且无法抛出经过检查的异常。 Executors类包含一些从其他普通形式转换成 Callable<V>类的实用方法。

 

 

在并发编程时,一般使用Runnable接口,然后扔给线程池完事,这种情况下不需要线程的结果。 所以run()的返回值是void类型。如下代码所示:

 
Java代码   收藏代码
  1. package future;  
  2. import java.util.concurrent.ExecutorService;  
  3. import java.util.concurrent.Executors;  
  4. public class FutureTest {  
  5.     public static class Task implements Runnable{  
  6.         @Override  
  7.         public void run() {  
  8.             System.out.println("做任务----"+Thread.currentThread().getName());  
  9.         }  
  10.     }  
  11.     public static void main(String[] args) {  
  12.         //创建线程池  
  13.         ExecutorService es = Executors.newCachedThreadPool();  
  14.         for(int i=0;i<100;i++){  
  15.             es.submit(new Task());  
  16.         }  
  17.     }  
  18. }  
 

 

如果是一个多线程协作程序,比如菲波拉切数列,1,1,2,3,5,8...使用多线程来计算。 
但后者需要前者的结果,就需要用Callable<V>接口了。如下代码所示:

 

Java代码   收藏代码
  1. package future;  
  2. import java.util.concurrent.Callable;  
  3. import java.util.concurrent.ExecutorService;  
  4. import java.util.concurrent.Executors;  
  5. public class FutureTest2 {  
  6.     public static class Task implements Callable<String>{  
  7.         @Override  
  8.         public String call() throws Exception {  
  9.             System.out.println("做任务----"+Thread.currentThread().getName());  
  10.             return "返回运算结果";  
  11.         }  
  12.     }  
  13.     public static void main(String[] args) {  
  14.         //创建线程池  
  15.         ExecutorService es = Executors.newCachedThreadPool();  
  16.         for(int i=0;i<100;i++){  
  17.             es.submit(new Task());  
  18.         }  
  19.     }  
  20. }  

 

 

我们看到,当Task类实现Callable接口后,重写了call()方法,call()方法是有返回值的。但我们如何来接受call()方法的返回值呢?

 

Java代码   收藏代码
  1. package future;  
  2. import java.util.ArrayList;  
  3. import java.util.List;  
  4. import java.util.concurrent.Callable;  
  5. import java.util.concurrent.ExecutionException;  
  6. import java.util.concurrent.ExecutorService;  
  7. import java.util.concurrent.Executors;  
  8. import java.util.concurrent.Future;  
  9.   
  10. public class FutureTest3 {  
  11.     public static class Task implements Callable<String>{  
  12.         @Override  
  13.         public String call() throws Exception {  
  14.             System.out.println("做任务----"+Thread.currentThread().getName());  
  15.             return "返回运算结果";  
  16.         }  
  17.     }  
  18.     public static void main(String[] args) throws InterruptedException, ExecutionException {  
  19.           
  20.         List<Future<String>> result = new ArrayList<Future<String>>();  
  21.           
  22.         //创建线程池  
  23.         ExecutorService es = Executors.newCachedThreadPool();  
  24.         for(int i=0;i<100;i++){  
  25.             result.add(es.submit(new Task()));  
  26.         }  
  27.         for(Future<String> f : result){  
  28.             System.out.println(f.get());  
  29.         }  
  30.     }  
  31. }  

 

从这里可以看出,这时候,Future<V>就出场了,使用Future<V>接口的get()方法可以获得call()方法返回的结果,当调用Future的get()方法时,当前线程就开始阻塞,直到call()方法结束返回结果。

 

Future模式可以这样来描述:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。就相当于下了一张订货单,一段时间后可以拿着提订单来提货,这期间可以干别的任何事情。其中Future接口就是订货单,真正处理订单的是Executor类,它根据Future接口的要求来生产产品。

 

 

二:关于 Future<V>的源码

 

 

Java代码   收藏代码
  1. package java.util.concurrent;  
  2.   
  3. /** 
  4. * Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使 
  5. * 用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确     * 定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结     * 果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。 
  6. * 
  7.   * @see FutureTask 
  8.   * @see Executor 
  9.   * @since 1.5 
  10.   * @author Doug Lea 
  11.   * @param<V> The result type returned by this Future's <tt>get</tt> method 
  12.   */  
  13. publicinterface Future<V> {  
  14.   
  15.     /** 
  16.      * 试图取消对此任务的执行。如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败。当调用 cancel * 时,如果调用成功,而此任务尚未启动,则此任务将永不运行。如果任务已经启动,则 mayInterruptIfRunning 参数      
  17.      * 确定是否应该以试图停止任务的方式来中断执行此任务的线程。 
  18.      */  
  19.     boolean cancel(boolean mayInterruptIfRunning);  
  20.     /** 
  21.      * 如果在任务正常完成前将其取消,则返回 true。    
  22.      */  
  23.     boolean isCancelled();  
  24.   
  25.     /** 
  26.      * 如果任务已完成,则返回 true。可能由于正常终止、异常或取消而完成,在所有这些情况中,此方法都将返回 true。     
  27.      */  
  28.     boolean isDone();  
  29.   
  30.     /** 
  31.      * 如有必要,等待计算完成,然后获取其结果。  
  32.      */  
  33.     V get() throws InterruptedException, ExecutionException;  
  34.   
  35.     /** 
  36.      * 如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。 
  37.      */  
  38.     V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;  
  39. }  

 

1)   Future接口是一个泛型接口,严格的格式应该是Future<V>,其中V代表了Future执行的任务返回值的类型

2)   Future接口提供方法来检测任务是否被执行完,等待任务执行完获得结果,也可以设置任务执行的超时时间。这个设置超时的方法就是实现Java程序执行超时的关键。

 

 

 

 

三:关于 Future实现

 

 
  



 

 

Future的实现类有FutureTask和SwingWork,其中SwingWork用于GUI编程模块,在并发包里经常用到的是FutureTask类,  这里主要介绍一下FutureTask类

 

 

类声明部分

 

Java代码   收藏代码
  1. public class FutureTask<V> implements RunnableFuture<V> {}  

 

 

FutureTask实现了RunnableFuture<V>接口,而RunnableFuture<V>又是extends(继承) Runnable和Future<V>两个接口,所以它既可以作为Runnable被线程执行,又可以作为Future<V>得到 Callable<V>的返回值,那么这个组合的使用有什么好处呢?假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组 合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future<V>得到,岂不美哉!

 

变量部分


Java代码   收藏代码
  1. private volatile int state;  
  2. private static final int NEW          = 0;               //新建    
  3. private static final int COMPLETING   = 1;              //执行中   
  4. private static final int NORMAL       = 2;               //正常  
  5. private static final int EXCEPTIONAL  = 3;              //异常         
  6. private static final int CANCELLED    = 4;              //取消  
  7. private static final int INTERRUPTING = 5;             //中断中  
  8. private static final int INTERRUPTED  = 6;             //被中断  
 

state:任务的状态,最初是 NEW,完成期间,状态也许暂时呈现为COMPLETING(当结果已经被设值)或者INTERRUPTING(only while interrupting the runner to satisfy a cancel(true)),

 

可能出现的状态转换情况:

* NEW -> COMPLETING -> NORMAL                    正常完成的流程

* NEW -> COMPLETING -> EXCEPTIONAL              出现异常的流程

* NEW -> CANCELLED                                 被取消的流程

* NEW -> INTERRUPTING -> INTERRUPTED            被中断的流程

构造函数:

Java代码   收藏代码
  1. /** 
  2.   *  创建一个 FutureTask,一旦运行就执行给定的 Callable。     
  3.   */  
  4.  public FutureTask(Callable<V> callable) {  
  5.      if (callable == null)  
  6.          thrownew NullPointerException();  
  7.      this.callable = callable;  
  8.      this.state = NEW;       // ensure visibility of callable  
  9.  }  
  10.   
  11.  /** 
  12.   *  创建一个 FutureTask,一旦运行就执行给定的 Runnable,并安排成功完成时 get 返回给定的结果 
  13.   */  
  14.  public FutureTask(Runnable runnable, V result) {  
  15.      this.callable = Executors.callable(runnable, result);  
  16.      this.state = NEW;       // ensure visibility of callable  
  17.  }  
 

从构造函数中可以看出,当构造一个FutureTask时,state会被置为NEWNEW也就是所有状态变化路径的起始状态

 

FutureTask生命周期的变化,主要取决于 run()方法先被调用还是cancel ()方法会被调用,这两个方法的执行顺序决定了FutureTask的生命周期的四种走向。

 

先来看run()方法

 

Java代码   收藏代码
  1. publicvoid run() {  
  2.      //首先判断任务的状态,如果任务的状态值不为NEW,或 runner变量的值不为null,则返回(说明正在走或已经走了4种状态变化的一种)  
  3.         if (state != NEW ||  
  4.             !UNSAFE.compareAndSwapObject(this, runnerOffset,  
  5.                                          null, Thread.currentThread()))  
  6.             return;  
  7. //如果状态值是NEW,则开始执行任务  
  8.         try {  
  9.             Callable<V> c = callable;  
  10.             if (c != null && state == NEW) { //如果任务不为空,且状态为NEW,开始执行  
  11.                 V result;                         //任务返回的结果  
  12.                 boolean ran;  
  13.                 try {  
  14.                     result = c.call();          //执行任务并返回结果  
  15.                     ran = true;                  //标记任务执行成功  
  16.                 } catch (Throwable ex) {  //任务执行中发生异常  
  17.                     result = null;  
  18.                     ran = false;  
  19.                     setException(ex);           //设置异常  
  20.                 }  
  21.                 if (ran)                          //任务执行成功,设置结果  
  22.                     set(result);  
  23.             }  
  24.         } finally {  
  25.             runner = null;                       //runner置为null  
  26.             // state must be re-read after nulling runner to prevent  
  27.             // leaked interrupts  
  28.             int s = state;  
  29.             if (s >= INTERRUPTING)  
  30.                 handlePossibleCancellationInterrupt(s);  
  31.         }  
  32.     }  
 

 

1)任务执行成功,会调用set()方法设置结果

Java代码   收藏代码
  1. protected void set(V v) {  
  2. //如过state是NEW,把state设置成COMPLETING   
  3.      if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {  
  4.           outcome = v;  
  5.         //将任务设置成NORMAL   over the task    
  6.           UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state  
  7.           finishCompletion();  
  8.      }  
  9. }  
 

 

从set()方法可以看出,把任务运行的结果赋值给了outcome变量,这个执行流程导致的状态变化就是  NEW->COMPLETING->NORMAL   

 

2)任务执行中发生异常,会调用setException()方法

Java代码   收藏代码
  1. protecte dvoid setException(Throwable t) {  
  2. //如过state是NEW,把state设置成COMPLETING      
  3.      if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {  
  4.           outcome = t;  
  5.         //将任务设置成EXCEPTIONAL    
  6.           UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state  
  7.           finishCompletion();  
  8.      }  
  9. }  
 

 

这个执行流程导致的状态变化就是  NEW->COMPLETING->EXCEPTIONAL

 

再来看cancel()方法:

 

Java代码   收藏代码
  1. //参数:mayInterruptIfRunning   是否中断running  
  2. publicboolean cancel(boolean mayInterruptIfRunning) {   
  3.      if (state != NEW)      //状态不为NEW,返回  
  4.           returnfalse;  
  5.      if (mayInterruptIfRunning) { //如果应该中断执行此任务的线程  
  6.           //如过state是NEW,把state设置成INTERRUPTING  
  7.           if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING))  
  8.                returnfalse;  
  9.           Thread t = runner;  
  10.           if (t != null)  
  11.                 t.interrupt();  
  12.          //将任务设置成INTERRUPTED  
  13.           UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state  
  14.      }  
  15.      //如过state是NEW,把state设置成CANCELLED  
  16.      elseif (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED))  
  17.             returnfalse;  
  18.      finishCompletion();  
  19.      returntrue;  
  20. }  

 

如果mayInterruptIfRunning==true,则流程为: NEW-> INTERRUPTING -> INTERRUPTED, 否则流程为: NEW-> CANCELLED

 

到此,四个流程走完了!


转自:http://15838341661-139-com.iteye.com/blog/2246817

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值