Java Callable任务

由于有获取异步任务执行结果的需求,所以java又扩展了一种Callable任务。

@FunctionalInterface
public interface Callable {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

一个典型的用法是:

public class Main {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        Future future = executorService.submit(() -> {
            // 一个异步任务
            return 0;
        });
        try {
            // 获取异步结果
            System.out.println(future.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

那么get是如何获取异步任务的结果的呢?
秘密就在FutureTask和AbstractExecutorService里。
经过这两个类的联动,就在Runnable任务的基础上,扩展出了Callable任务。

首先看,AbstractExecutorService的submit和newTaskFor:

    public  Future submit(Callable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

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

Callable任务提交后,经FutureTask封装,其实真正异步执行的是FutureTask的run方法,最后submit返回的其实也是FutureTask对象。然后调用get方法获取异步任务的结果,其实调用的也是FutureTask的get方法。

那么重点就是,FutureTask是如何在run方法里执行异步任务,并在get方法里获取异步任务的结果?

其实靠的就是两者共享的一个中间变量,靠这个中间变量中转异步任务的执行结果。

    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            // 这个callable就是newTaskFor时传进来的异步任务
            Callable c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    // 执行Callable的call方法
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    // 如果发生异常,set异常
                    setException(ex);
                }
                if (ran)
                    //   任务没有异常,set执行结果
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

call的执行有两种情况,1. 发生异常,通过setException方法设置结果。2. 正常执行,通过set方法设置结果。

    // outcome就是异步结果的中转变量
    private Object outcome;

    protected void setException(Throwable t) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            // 把Throwable对象放到outcome里
            outcome = t;
            // 终态置为EXCEPTIONAL
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            // 唤醒所有阻塞在get方法上的线程
            finishCompletion();
        }
    }

    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            // 正常的执行结果放入outcome
            outcome = v;
            // 终态置为NORMAL
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            // 唤醒所有阻塞在get方法上的线程
            finishCompletion();
        }
    }

再来看看get方法

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            // 异步任务还没完成,阻塞当前想要获取结果的线程
            s = awaitDone(false, 0L);
        // 返回异步结果
        return report(s);
    }

    // 参数s是任务执行状态
    private V report(int s) throws ExecutionException {
        // 根据outcome里的执行结果和任务执行状态,返回正常结果或抛出异常
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

总结:对于Callable任务来说,其实真正异步的还是Runnable任务,Callable只是对Runnable的封装,通过中间变量,让我们可以方便的获取异步执行的结果。

你可能感兴趣的:(Java Callable任务)