FutureTask

FutureTask是Callable的核心实现

 

多线程的实现方式:

  1、继承Thread类

  2、实现Runnable接口

  3、实现Callable<V> 接口

 

通过实现Callable接口来创建多线程,在执行完成后可获取执行结果。

Callable接口:

对于需要执行的任务需要实现Callable接口,Callable接口定义如下:

public interface Callable<V> {
    /**
     * 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;
}

Callable是个泛型接口,泛型V就是要call()方法返回的类型。

 

Future接口:

Future接口代表异步计算的结果,通过Future接口提供的方法可以查看异步计算是否执行完成,或者等待执行结果并获取执行结果,同时还可以取消执行。Future接口的定义如下:

    public interface Future<V> {

        /**
         * 尝试取消任务的执行
         * 如果异步任务已经完成或者已经被取消,或者由于某些原因不能取消,则会返回false。
         * 如果任务还没有被执行,则会返回true并且异步任务不会被执行。
         * 如果任务已经开始执行了但是还没有执行完成,若mayInterruptIfRunning为true,则会立即中断执行任务的线程并返回true,若mayInterruptIfRunning为false,则会返回true且不会中断任务执行线程
         * @param mayInterruptIfRunning
         * @return
         */
        boolean cancel(boolean mayInterruptIfRunning);

        /**
         * 判断任务是否被取消,如果任务在结束(正常执行结束或者执行异常结束)前被取消则返回true,否则返回false
         * @return
         */
        boolean isCancelled();

        /**
         * 判断任务是否已经完成,如果完成则返回true,否则返回false。
         * 需要注意的是:任务执行过程中发生异常、任务被取消也属于任务已完成,也会返回true
         * @return
         */
        boolean isDone();

        /**
         * 获取任务执行结果,如果任务还没完成则会阻塞等待直到任务执行完成。
         * 如果任务被取消则会抛出CancellationException异常,如果任务执行过程发生异常则会抛出ExecutionException异常,如果阻塞等待过程中被中断则会抛出InterruptedException异常。
         * @return
         * @throws InterruptedException
         * @throws ExecutionException
         */
        V get() throws InterruptedException, ExecutionException;

        /**
         * 带超时时间的get()版本,如果阻塞等待过程中超时则会抛出TimeoutException异常
         * @param timeout
         * @param unit
         * @return
         * @throws InterruptedException
         * @throws ExecutionException
         * @throws TimeoutException
         */
        V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
    }

 

 使用Callable和Future获取线程执行结果的示例:

 // 创建一个单线程的线程池,仅测试用,因其使用无界队列,存在OOM的风险,实际开发使用ThreadPoolExecutor
        ExecutorService testExecutor = Executors.newSingleThreadExecutor();

        Future<String> stringFuture = testExecutor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "success";
            }
        });

        String result = stringFuture.get();
        System.out.println(result); // success

线程池submit源码(AbstractExecutorService):

    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
        // newTaskFor
        protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }    

  Step 1 : 当通过 submit 调用的时候 , 底层会调用 :

     newTaskFor:return new FutureTask<T>(runnable, value);

  Step 2 : 在外层会被提升为父类 RunnableFuture ,

    RunnableFuture<T> ftask = newTaskFor(task, result);

  Step 3:

    在返回的时候又会被提成 Future,返回ftask,方法返回类型为Future

  总结 : 所以 , 底层的实现类主要是 FutureTask , 而FutureTask 是 Runnable 的实现类

 

 

 

FutureTask:

 

 

FutureTask实现了RunnableFuture接口,则RunnableFuture接口继承了Runnable接口和Future接口,所以FutureTask既能当做一个Runnable直接被Thread执行,也能作为Future用来得到Callable的计算结果。

FutureTask一般配合ExecutorService来使用,也可以直接通过Thread来使用,示例:

        /**
         * 第一种方式:Future + ExecutorService
         * Task task = new Task();
         * ExecutorService service = Executors.newCachedThreadPool();
         * Future<Integer> future = service.submit(task1);
         * service.shutdown();
         */

        // 第二种方式:FutureTask + ExecutorService
        // 创建一个单线程的线程池,仅测试用,因其使用无界队列,存在OOM的风险,实际开发使用ThreadPoolExecutor
        ExecutorService testExecutor = Executors.newSingleThreadExecutor();

        FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "success";
            }
        });
        testExecutor.submit(futureTask);

        // 第三种方式:FutureTask + Thread
        new Thread(futureTask).start();

        String result = futureTask.get();
        System.out.println(result); // success

 

 FutureTask 源码解读:

  1)构造方法:

    private Callable<V> callable;
    
    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
    }

  

private volatile int state; // volatile修饰,保证内存可见性,避免任务被执行多次

 state字段用来保存FutureTask内部的任务执行状态,一共有7种状态,初始状态是NEW,每种状态及其对应的值如下:

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; // 保存任务执行结果(如果异常将保存异常Throwable)

  

 run()方法:

public void run() {
        // 1、如果状态不是NEW,说明任务已经执行过,或者被取消,直接返回
        // 2、如果状态为NEW则接着会通过unsafe类把任务执行线程引用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 {
                    // 执行任务,调用callable实现的call()方法
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    // 任务异常,将异常Throwable赋给outcome
                    setException(ex);
                }
                if (ran)
                    // 任务正常执行完毕,将执行结果赋给outcome
                    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);
        }
    }

  run()方法执行流程:

    1. 判断当前任务的state是否等于NEW,如果不为NEW则说明任务或者已经执行过,或者已经被取消,直接返回。

    2. 如果状态为NEW则接着会通过unsafe类把任务执行线程引用CAS的保存在runner字段中,如果保存失败,则直接返回。

    3. 执行任务。

    4. 如果任务执行发生异常,则调用setException()方法保存异常信息。

 

 

  get()方法:

    public V get() throws InterruptedException, ExecutionException {
        // 获取状态
        int s = state;
        // 如果任务还未执行完毕
        if (s <= COMPLETING)
            // 阻塞等待任务执行完毕
            s = awaitDone(false, 0L);
        // 返回任务执行结果
        return report(s);
    }

  任务发起线程可以调用get()方法来获取任务执行结果,如果此时任务已经执行完毕则会直接返回任务结果,如果任务还没执行完毕,则调用方会阻塞直到任务执行结束返回结果为止

  get()方法执行流程:

    1. 判断任务当前的state <= COMPLETING是否成立。前面分析过,COMPLETING状态是任务是否执行完成的临界状态。

    2. 如果成立,表明任务还没有结束(这里的结束包括任务正常执行完毕,任务执行异常,任务被取消),则会调用awaitDone()进行阻塞等待(死循环等待)。

    3. 如果不成立表明任务已经结束,调用report()返回结果

 

  

参考:https://mp.weixin.qq.com/s/lwaOYr96cAOtSVhuE2vGNg

 

 

END.

posted @ 2021-03-21 22:30  杨岂  阅读(40)  评论(0编辑  收藏  举报