• Future简介


    1 Future接口

    Future接口表示异步计算的结果,一旦计算完成,计算将不能再取消。整个Future的继承体系结构如下:

    代表异步结算结果。该接口提供方法:

    • 检查计算是否完成(isDone)
    • 等待计算完成(get)
    • 检索计算结果(get)
    • 取消任务(cancel)。

    cancel()方法只是试图取消任务执行。若已经被取消或计算已经完成,将不能再被取消。该方法返回后,isDone()和isCancelled()方法将会始终返回true

    public interface Future<V> {
        boolean cancel(boolean mayInterruptIfRunning);
        boolean isCancelled();
        boolean isDone();
        V get() throws InterruptedException, ExecutionException;
        V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
    }
    

    Future接口通常配合Executor接口来实现,如下:

    // 方式1:Future接口实现
    Future<String> future = Executor.submit(new Callable<String>() {
            public String call() {
                return doSomething();
            }
        });
    // 方式2:FutureTask实现
    FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
            public String call() {
                return doSomething();
            }
        });
    executor.execute(futureTask);
    

    2 FutureTask

    FutureTaskFuture接口的典型实现类。FutureTask是一个可取消的异步计算。其类图结构如下所示:
    image

    FutureTask任务的异步执行,可以使用如下2种方式执行:

    • 利用Executor#execute或 ExecutorService#submit()方式执行
    • 直接放到Thread线程里执行
      FutureTask<String> futureTask = new FutureTask<>(() -> "hello");
      Thread t = new Thread(futureTask);
      t.start();
      System.out.println(futureTask.get());
      

    因为FutureTask实现了Runnable接口,因此该类天然支持异步线程执行。所有的异步执行,都是通过FutureTask#run()方法实现,run()方法执行完毕后,将结果缓存,通过调用FutureTask#get()方法获取执行的结果。

    2.1 执行流程

    FutureTask类的state字段用于控制异步任务的执行流,如下:

    /* state字段的状态流转
    NEW -> COMPLETING -> NORMAL
    NEW -> COMPLETING -> EXCEPTION
    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;
    
    private Object outcome;  // 代表执行结果
    private volatile Thread runner;  // 正在执行的任务的线程
    private Callable<V> callable;    // 需要执行的任务
    

    目前state字段的数值大小有顺序规律:

    • 0~2为正常状态
    • 3为异常状态
    • 4为取消
    • 5~6为中断

    注意:取消和中断,都可以认为是取消情况。可以直接跳到cancel()方法,该方法会根据入参情况,来决定最终的状态为4或6。即cancel()会导致状态为CANCELLED or INTERRUPTED。

    根据字段以及构造方法可知,FutureTask是一个异步执行框架,而其内部封装了需要执行的任务callable。异步执行由FutureTask本身作为Runnable的实现类完成,在异步的Runnable#run()中会同步的调用Callable#run()方法。

    2.2 等待节点

    FutureTask是异步执行任务的框架,因此必然存在这种情况:当任务尚在执行过程中,其他线程尝试过去该任务的执行结果。在FutureTask中会将这些线程打包成WaitNode节点,等待执行结果:

    static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
    }
    

    即所有获取结果的线程组成单向链表。在任务执行完成后,将会将这些线程unpark()。

    2.3 构造方法

    由构造方法可以得知,那些"种类"的任务可以构造出FutureTask

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;
    }
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;
    }
    

    由构造方法可知,FutureTask的初始状态为NEW。

    2.4 执行任务

    FutureTask的核心在于任务的异步执行,而异步执行的核心在于Runnable#run()方法。该方法注意如下几点:

    • 因为可能出现异常情况,因此outcome是Object类
    • 在执行完任务只会,首先将state状态设置为COMPLETING的瞬时态
    • 最后,执行finishCompletion()进行完成处理
    public void run() {
        // 判断task的状态流转,并使用CAS方式以runner来记录当前线程
        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);
        }
    }
    // 任务正常结束
    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();  // 任务正常执行完成,则upark()所有等待获取结果的线程
        }
    }
    // 任务异常终止
    protected void setException(Throwable t) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }
    

    在执行完毕后finishCompletion()方法将会unpark()所有获取结果的线程:

    // 完成处理的核心在于:
    // 1. 循环移除所有的WaitNode节点,并LockSupport.unpark(t)该节点上记录的线程
    // 2. 调用done()方法,该方法是预留方法,用于实现类扩展。具体可以参考ExecutorCompeletionService类
    private void finishCompletion() {
        // assert state > COMPLETING;  即此方法state 一定大于 COMPLETING,即任务已经是完成
        for (WaitNode q; (q = waiters) != null;) {
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                for (;;) {  // 移除并且unpark()所有等待的线程
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }
        done();
        callable = null;        // to reduce footprint
    }
    

    2.5 获取结果

    get()方法由重载方法,其核心在于:若计算尚未完成,则进行等待:创建WaitNode节点进入等待队列中。若最后完成,则调用report()方法获取结果:

    • state == NORMAL正常结束,返回结果;
    • state >= CANCELLED,取消结束,抛出CancellationException;
    • state 为其余任何情况,抛出执行异常
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
    public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        if (unit == null)
            throw new NullPointerException();
        int s = state;
        if (s <= COMPLETING && (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
        return report(s);
    }
    //// report()方法比较简单,先看此方法
    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()方法

    /// 3种结果:完成、中断、超时
    private int awaitDone(boolean timed, long nanos) throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {   // 自旋操作,只有被中断抛出异常,或者state>COMPLETING时,才会跳出方法
            if (Thread.interrupted()) { // 清空等待队列,然后抛出异常以响应等待中的线程
                removeWaiter(q);        // 该方法只会清空等待队列
                throw new InterruptedException();
            }
    
            int s = state;
            if (s > COMPLETING) {  // 表明任务已经结束,返回数据
                if (q != null)     // 如果已经创建了WaitNode节点,则重新将其清空(等待队列中的数据,会在任务完成后的finishCompletion()方法中清空)
                    q.thread = null;
                return s;
            } else if (s == COMPLETING) {  // COMPLETING表明任务即将完成,无需park(),只需要yield()然后继续尝试
                Thread.yield(); 
            } else if (q == null) {    // 如果任务一直处于NEW状态,则第1次循环执行到这里,为q创建一个WaitNode()对象
                q = new WaitNode();
            } else if (!queued) {      // 如果任务一直处于NEW状态,则第2次循环执行到这里,将q塞入等待队列中
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
            } else if (timed) {        // 如果任务一直处于NEW状态,则第3次循环执行到此处或最后的else,将当前线程parkNanos或park()
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            } else {
                LockSupport.park(this);
            }
        }
    }
    

    2.6 取消任务

    任务在执行期间可以被取消或被中断。取消方法会将状态从:

    • 若被取消则:NEW -> CANCELLED
    • 若被中断则:NEW -> INTERRUPTING -> INTERRUPTED。

    中断or取消,取决于cancel()的入参mayInterruptIfRunning,为true则中断;否则将取消。
    注意,状态由 INTERRUPTING -> INTERRUPTED之间,存在时间差,先设置为INTERRUPTING状态后,将线程进行中断,然后设置为INTERRUPTED。由于时间差较短,因此常用Thread.yield()方法让出线程进行等待

    public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW & UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            // 取消后,需要unpark()所有在等待的线程
            finishCompletion();
        }
        return true;
    }
    
  • 相关阅读:
    oracle创建函数和调用存储过程和调用函数的例子(区别)
    oracle存储过程的创建和使用
    oracle恢复已删除数据
    存储管理工具StorageExplorer的基本使用
    Azure CLI对ASM,ARM资源的基本操作
    Windows系统安装Azure CLI
    Azure Powershell对ASM资源的基本操作
    Azure Powershell对ARM资源的基本操作
    安装Windows Azure Powershell
    Linux虚拟机之间实现密钥登陆
  • 原文地址:https://www.cnblogs.com/wolfdriver/p/10487413.html
Copyright © 2020-2023  润新知