JAVA多线程之——Future与Callable

来源:互联网 发布:sql server 2012 密钥 编辑:程序博客网 时间:2024/04/28 18:21

Futrue与Callable

在学习了多线程之后,可以知道线程的实现方式是通过继承Thread或者实现Runnable接口,当线程被启动后,会执行run方法。都知道run方法是一个void方法,也就是说线程执行完毕之后,不会返回任何东西。那么,如果我们想得到线程执行的结果怎么呢?这就要用到JUC中的Futrue与Callable了。
Callable

public interface Callable<V> {    V call() throws Exception;}

Callable方法只有一个call方法,该方法返回一个泛型V对象。对比Runnable接口,该接口只有run方法,无返回值。
在调用run方法的时候,我们通常通过new一个Thread方法,传入一个Runnable接口。同理,Callable一般情况下是配合ExecutorService来使用的,在ExecutorService接口中声明了若干个submit方法的重载版本:

 <T> Future<T> submit(Callable<T> task);<T> Future<T> submit(Runnable task, T result);Future<?> submit(Runnable task);

常用的是第一个和第三个。先来看一下第一个:

public <T> Future<T> submit(Callable<T> task) {       if (task == null) throw new NullPointerException();       RunnableFuture<T> ftask = newTaskFor(task);//把Callable对象封装成RunnableFuture对象。       execute(ftask);//执行execute方法       return ftask;//返回一个Future对象。   }

在AbstractExecutorService里面的实现如上。在学习线程池的时候,学习过execute方法的基本执行过程,也知道它的参数是一个Runnable类型的command。因此 RunnableFuture肯定是Runnable的一个子类。 返回的是一个Future对象,那么它应该也是Future的子类。这名字取的也是够直白。把Callable对象封装成RunnableFuture调用的是 newTaskFor
newTaskFor

   protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {        return new FutureTask<T>(callable);    }

调用构造方法

RunnableFuture

public interface RunnableFuture<V> extends Runnable, Future<V> {    void run();}

注意:这里多继承是因为是接口的多继承。JAVA中只有类是单继承。别误会
Future

//取消任务,任务我们都知道有三种基本情况,已经执行完毕,正在执行,还未执行。//1.如果任务已经执行完毕,那么不管mayInterruptIfRunning是什么返回false//2.任务未执行,不管mayInterruptIfRunning方法都返回true//3.任务正在执行,那么返回的就是mayInterruptIfRunningboolean cancel(boolean mayInterruptIfRunning);//判断任务是否被取消,如果在任务正常完成前被取消成功,则返回 true。boolean isCancelled();//任务是否完成,已经完成返回trueboolean isDone();//获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;V get() throws InterruptedException, ExecutionException;获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;

再来看看RunnableFuture接口的实现类:
FutureTask

    /**     * Possible state transitions:状态的四种变化     * NEW -> COMPLETING -> NORMAL  正常完成     * NEW -> COMPLETING -> EXCEPTIONAL 出现了异常     * NEW -> CANCELLED  被取消     * NEW -> INTERRUPTING -> INTERRUPTED 被中断     */    private volatile int state;    private static final int NEW          = 0;//新建    private static final int COMPLETING   = 1;//完成    private static final int NORMAL       = 2;//正常    private static final int EXCEPTIONAL  = 3;//异常    private static final int CANCELLED    = 4;//被取消    private static final int INTERRUPTING = 5;//中断中    private static final int INTERRUPTED  = 6;//被中断

构造方法

  public FutureTask(Callable<V> callable) {        if (callable == null)            throw new NullPointerException();        this.callable = callable;        this.state = NEW;       // ensure visibility of callable    }

FutureTask实现了RunnableFuture,RunnableFuture继承了Runnable接口,所以FutureTask实现run方法
run方法

public void run() {      /**         *首先判断任务的状态 如果任务的状态不是new 说明任务的状态已经改变(说明他已经走了4种可能变化的一种)         * 如果状态是new就会把 当前执行任务的线程付给runner, 这里用的cmpandset如果runner不为空 说明已经有线程在执行     * 任务也会退出执行,如果状态是new并且runner为空并且把当前的线程付给了runner那么就继续执行任务(runner state 都是 volatile         *类型的变量是一个很轻量机的线程安全操作)         *引起state状态变化的原因 就是调用了cancel 或是 run     if (state != NEW ||        !UNSAFE.compareAndSwapObject(this, runnerOffset,                                     null, Thread.currentThread()))        return;    try {        Callable<V> c = callable;        if (c != null && state == NEW) {  //如果是新建状态,并且c不等于空就开始执行任务            V result;            boolean ran;            try {                result = c.call(); //调用callable对象的call方法                ran = true;            } catch (Throwable ex) {                result = null;                ran = false;                setException(ex);            }            if (ran)                set(result);   //如果正常执行完毕,设置结果        }    } finally {        runner = null;        int s = state;        if (s >= INTERRUPTING)            handlePossibleCancellationInterrupt(s);    }}

总结
在多线程环境中,通过任务实现Callable接口,然后调用线程池的submit方法,submit方法分两步

  1. 把Callable对象封装成一个FutureTask对象。(实际就是在FutureTask中有一个Callable类型的属性。)FutureTask类是RunnableFuture的实现类,RunnableFuture继承了Runnable和Future接口。在FutureTask实现了Runnable方法的run方法。run方法中对调用callable方法的call方法,然后把结果设置给FutureTask的另外一个属性。
  2. 调用线程池的execute方法,等待执行FutrueTask中的run方法。

当然这里只是总结一个正确的运行结果。其中可能任务会被取消,线程发生中断,异常等等情况。可以参照源码学习。其它的情况都只是一个上面状态的一个变化过程而已。

0 0
原创粉丝点击