美文网首页程序员
Android concurrent(二)—— Future和F

Android concurrent(二)—— Future和F

作者: 备忘君 | 来源:发表于2016-04-04 18:48 被阅读1479次

    Future接口

    官方解释

    先让我们看一下,官方对它的解释:

    A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation. The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready. Cancellation is performed by the cancel method. Additional methods are provided to determine if the task completed normally or was cancelled. Once a computation has completed, the computation cannot be cancelled. If you would like to use a Future for the sake of cancellability but not provide a usable result, you can declare types of the form Future<?> and return null as a result of the underlying task.

    要点:

    1. 一个Future代表了一个异步计算的结果。 它提供了可以坚持计算是否完成、等待计算完成、检索计算结果的方法。
    2. get方法获得计算结果的唯一方法,如果计算没有完成,此方法会堵塞直到计算完成。
    3. cancel方法可以用来取消这次计算。一个已完成的计算是不能被取消的。
    4. isDone和isCancelled方法可以查询计算是否正常完成还是被取消掉了。
    5. 如果我们不想知道此异步计算的结果,只是想随时取消这次计算,可以通过声明Future<?>并将get的返回值设为null。

    源码分析

    public interface Future<V> {
        //取消方法
        boolean cancel(boolean mayInterruptIfRunning);
        //计算是否被取消:如果计算在正常结束前被取消了,则返回true
        boolean isCancelled();
        //计算是否完成:不管是正常完成、异常结束、还是被取消了,都返回true
        boolean isDone();
        //检索返回结果,如果计算未完成,则等待任务完成。
        V get() throws InterruptedException, ExecutionException;
        //和get()方法类似,我们可以通过参数timout和unit指定等待的时间上限,如果时间结束了,计算还未完成,就会抛出TimeOutException异常
        V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
    }
    

    V是实际计算结果的类型,也就是get()方法返回的类型。在其中,一共五个方法。
    两个状态查询方法(isCancelled和isDone方法), 一个取消计算方法(cancel),两个检索结果方法(get()和get(long,TimeUnit))方法。
    对于已经结束任务、已经取消过的任务、不能被取消的任务,调用cancel会失败并返回false; 如果任务还未开始,调用cancel后,任务将不会在被执行,并返回true; 如果任务正在进行中,参数mayInterruptIfRunning为true,则中断执行此任务的线程,false,任务则继续执行,直到完毕。

    总结:到这儿,Future之所以被设计的原因已经很明了了,其实就是帮助我们可以自由的控制异步任务:可以通过它来查询异步任务的执行状态,取消任务,也可以获得正常的结果。
    下面我们来看看FutureTask,Future接口的实际实现类。

    Future的实现:FutureTask

    其实FutureTask并非直接实现自Future接口,而是RunnableFuture接口,RunnableFutrue是什么,看下源码,便一目了然。

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

    所以,FutureTask同时实现了Runnable和Future两大知名接口,所以FutureTask可以提交给Executor

    FutureTask源码分析

    首先我们来看一下FutureTask的两个个构造函数

    //构造函数一
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
    //构造函数二
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
    

    源码显示,不管我们使用哪个构造函数,其内部都是把将传入的参数保存为callable,并且把状态置为NEW。FutureTask一共声明了7个状态。

    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;
    
    状态 说明
    NEW 初始状态
    COMPLETING 进行中状态,表示正在设置结果。很短暂的一个状态
    NORMAL 正常结束的状态
    EXCEPTIONAL 异常状态,任务异常结束
    CANCELLED 任务成功被取消的状态
    INTERRUPTING 很短暂的状态,当在NEW状态下,调用了cancel(true),则状态就会转换为INTERRUPTING,直到执行了Thread#interrupt()方法,状态转换为INTERRUPTED
    INTERRUPTED 任务被中断后的状态

    run()方法讲解

    FatureTask被创建后,下面进入run()方法,

    public void run() {
       if (state != NEW ||
          !UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))
          return;
       try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
               V result;
               boolean ran;
                try {
                    result = c.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);
        }
    }
    

    在run()方法中,先进行状态检查,检查是否处于NEW状态,然后将执行线程的引用保存在runner的变量。FurtureTask的runner变量用来引用任务执行所在的线程。然后执行Callable的call的方法,进行任务执行。接下来会出现两种情况:
    情况一: 如果执行顺利完成,则调用set(result)的方法。在set()方法中,先将状态置为COMPLETING,然后将执行结果保存到全局变量outcome中,然后将状态置为NORMAL。然后调动finishCompletion()方法,通知所有等待结果的线程,并调用done()(在这里是个空方法)
    情况二:如果执行出现了异常。则执行setException()方法。在setException()方法中,操作基本和set()方法一样,只是outcome保存的是Throwable。

    全局outcome变量:

    private Object outcome;
    

    流程图

    st=>start: Start
    cond1=>condition: 处于NEW&&runner赋值成功
    op1=>operation: 执行任务
    cond2=>condition: 发生异常?
    op2=>operation: 设置结果
    op3=>operation: 设置异常结果
    op4=>operation: runner设置为null
    e=>end
    op=>operation: My Operation
    
    st->cond1
    cond1(yes)->op1->cond2
    cond1(no)->e
    cond2(yes)->op2->op4->e
    cond2(no)->op3->op4->e
    

    run()方法的大致操作流程就是这样的。

    FutureTask对Future接口界面的实现

    isCanceled()和isDone()
    public boolean isCancelled() {
        return state >= CANCELLED;
    }
    
    public boolean isDone() {
        return state != NEW;
    }
    

    当前任务的状态保存在全局变量state中。这里检查是否取消和是否完成,只要检查一下state的值即可。

    cancel(boolean)方法

    下面,我们看一下cancel方法的实现

    public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW,mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }
    

    cancel方面会直接检测当前状态是否是NEW,如果不是,说明任务已经完成或取消或中断,所以直接返回。当符合条件后,检查mayInterruptIfRunning的值,

    1. 如果mayInterruptIfRunning == false,则直接将状态设置为CANCELLED,并且调用finishCompletion()方法,通知正在等待结果的线程。
    2. 如果mayInterruptIfRunning == true,则暂时将状态设置为INTERRUPTING,然后试着中断线程,完成后将状态设置为INTERRUPTED,最后调用finishCompletion()方法,通知正在等待结果的线程。
    get()方法的实现

    get()方法首先还是进行状态监测,如果现在正处于NEW和COMPLETING状态,则会调用awaitDone(),直到状态的转变为其他状态,然后调用report()方法,在report()方法中,首先监测状态:如果是NORMAL状态,直接返回保存在outCome中的结果;如果是CANCELLED、INTERRUPTING、INTERRUPTED状态,则抛出CancellationException();如果处于其他状态则抛出ExecutionException(比如调用了get(true,atime)方法,时间到期后状态可能还处于NEW状态)。
    源码

    /**
     * @throws CancellationException {@inheritDoc}
     */
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
    
    /**
     * Returns result or throws exception for completed task.
     *
     * @param s completed state value
     */
    @SuppressWarnings("unchecked")
    private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }
    

    其中awaitDone(boolean timed, long nanos)方法用于等待任务完成、任务中断或时间到期。在此方法体中,显示将这次等待保存到全局变量watiers中,用于记录所有调用了get()渴望获得结果并堵塞的Thread,然后不停的循环查询state。直到时间到期或执行完成,则将循环中断,返回转变后的state供report()方法使用。

    注:在FutureTask的的源码中,使用了sun.misc.Unsafe进行状态的赋值等操作,这是一个强大的对内存进行操作的类,可以通过它绕过jdk的很多限制。

    相关文章

      网友评论

        本文标题:Android concurrent(二)—— Future和F

        本文链接:https://www.haomeiwen.com/subject/cazulttx.html