Loading

java 并发相关(3) - Future:获取异步结果

1、 Future定义

Future是java1.5版本引进的一个接口,可以用来获取异步执行的结果,提供了一些查看异步任务状态的方法。

FutureTask是Future接口的一个实现类,常用来配合Callable任务,实现异步执行并能获取到任务的执行结果。

PS: 注意future接口获取异步任务返回的get方法是一个阻塞方法,要对其合理的使用。

  • FutureTask关系图

  • Future定义的一些方法

1、boolean cancel(boolean mayInterruptIfRunning)
尝试取消当前任务的执行。如果任务已经取消、已经完成或者其他原因不能取消,尝试将失败。如果任务还没有启动就调用了cancel(true),任务将永远不会被执行。如果任务已经启动,参数mayInterruptIfRunning将决定任务是否应该中断执行该任务的线程,以尝试中断该任务。
如果任务不能被取消,通常是因为它已经正常完成,此时返回false,否则返回true

2、boolean isCancelled()
查询任务是否取消成功

3、boolean isDone()
查询任务是否已经完成

4、V get() 阻塞方法
等待任务结束,然后获取结果,如果任务在等待过程中被终端将抛出InterruptedException,如果任务被取消将抛出CancellationException,如果任务中执行过程中发生异常将抛出ExecutionException。

5、V get(long timeout, TimeUnit unit)
任务最多在给定时间内完成并返回结果,如果没有在给定时间内完成任务将抛出TimeoutException。

  • Future的简单例子
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        Long startTime = System.currentTimeMillis();

        FutureTask<String> futureTask1 = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(5000);
                return "future1";
            }
        });

        new Thread(futureTask1).start();

        FutureTask<String> futureTask2 = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(3000);
                return "future2";
            }
        });

        new Thread(futureTask2).start();

        //等待future1和future2执行完 ,get()阻塞
        System.out.println(futureTask1.get() + "执行完!");
        System.out.println(futureTask2.get() + "执行完!");

        Long endTime = System.currentTimeMillis();

        // 5004 可以看出future1和future2是异步执行的
        System.out.println("共用时: " + (endTime - startTime));

    }

2、FutureTask的源码解析

从上述例子,我们可以看到,FutureTask任务执行的调用是 new Thread(futureTask).start()这样的,即线程将FutureTask当成一个Runnable任务体执行。

注:FutureTask本身就实现了RunnalbeFuture接口(即Runnable接口和Future接口)

可以着重看下FutureTask中的run()方法

    public void run() {
        // java不能直接访问操作系统底层,而是通过本地方法来访问,Unsafe类提供了硬件级别的原子操作。
        // UNSAFE.compareAndSwapObject在obj的offset位置比较object field和期望的值,如果相同则更新。field值修改会返回true
        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(); // 这里保存了Callable任务的返回值
                    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);
        }
    }

set(result)方法:

    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v; //保存任务的返回结果,FutureTask的内部属性outcome
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }

可以看出,FutureTask只是将自己的run方法中实现对Callabe任务的执行和保存结果。

获取任务的返回值 FutureTask.get()方法

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            // 等待任务完成,并且移除waiters这个等待线程的节点
            s = awaitDone(false, 0L);
        return report(s); // 对返回值的封装,s其实是当前FutureTask的状态
    }

可以看出FutureTask.get()是一个阻塞方法,在FutureTask中,state(当前任务运行状态)是用来判断任务是否完成的关键,

3、扩展

3.1、其实在set(result)方法中,UNSAFE方法操作state的地址状态为COMPLETING -> NORMAL,state也是FutureTask中其他方法判断是否执行的一个关键属性。

state的几种流转情况:
NEW -> COMPLETING -> NORMAL 正常情况
NEW -> COMPLETING -> EXCEPTIONAL 异常情况
NEW -> CANCELLED 被取消
NEW -> INTERRUPTING -> INTERRUPTED 被打断

3.2、在线程的相关工具中,state状态都是十分重要的属性,不同的状态会使相应的工具类呈现不同的任务执行。

posted @ 2021-06-01 21:38  逝zxq  阅读(207)  评论(0编辑  收藏  举报