jdk源码分析之 FutureTask

**FutureTask作用:**支持创建有返回值的线程。 FutureTask可以很容易获得异步任务的执行结果,无论异步任务是通过线程池 ThreadPoolExecutor 执行的,还是通过手工创建子线程来执行的
总体架构
FutureTask从关系图中发现实现了RunnableFuture,而FunnableFuture继承了Future接口和Runnable接口。其关系类图及主要方法如下:
在这里插入图片描述
类注解信息:

  • 可取消的异步计算。这个类提供了一个基于Future实现的,带有开始和取消方法一个计算,查询看是否计算完成,和检索计算结果。结果只能是计算完成后检索,如果计算尚未完成,方法将被阻塞。一次计算已完成,无法重新开始计算或取消(除非计算被调用 #runAndReset
  • 一个FutureTask可以用来包装一个Callable或者Runnable对象,因为FutureTask实现了Runnable接口,同时一个FutureTask可以提交给 Executor执行(submit)。
  • 除了作为一个独立的类,这个类还提供在创建时可能有用的功能自定义任务类。

FutureTask是Future的实现,Future定义了一些重要的接口,其源码如下:

public interface Future<V> {

    /**
     * 取消任务的方法,,但一旦任务计算完成,就无法被取消了
     */
    boolean cancel(boolean mayInterruptIfRunning);

    /**
     * 判断任务是否已取消的方法
     */
    boolean isCancelled();

    /**
     * 判断任务是否已结束的方法
     */
    boolean isDone();

    /**
     * 等待结果返回
     * 如果任务被取消了,抛 CancellationException 异常
     * 如果等待过程中被打断了,抛 InterruptedException 异常
     */
    V get() throws InterruptedException, ExecutionException;

    /**
     * 等待,但是带有超时时间的,如果超时时间外仍然没有响应,抛 TimeoutException 异常
     */
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Callable
Callable约定了任务中具体执行的业务逻辑,其与Runnable功能大体一致(都是函数式接口),主要区别为Callable是有返回值,而Runable没有返回值,Callable和FutureTask结合使用效果更佳哦。
其源码如下

@FunctionalInterface // 通过这个注解表示为函数式接口
public interface Callable<V> {
    V call() throws Exception;
}

// 根据Callable接口的泛型返回一个泛型的结果

@FunctionalInterface
public interface Runnable {
  // 无返回值
    public abstract void run();
}

使用实例
通过编写一个分工合作的实例,来模拟实际开发中FutureTask的作用(模拟业务场景)


public class FutureDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("通过普通的新建线程方式启动");
        FutureTask<String> ft2 = new FutureTask<>(new Task2());
        FutureTask<String> ft1 = new FutureTask<>(new Task1(ft2));
        Thread thread1 = new Thread(ft1);
        Thread thread2 = new Thread(ft2);
        thread1.start();
        thread2.start();
        System.out.println("----------------------------");
        // 首先我们创建了一个线程池
        System.out.println("通过线程池的方式提交futuretask");
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 3,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>());
        executor.submit(ft1);
        executor.submit(ft2);
        String s = ft1.get();
        String s2 = ft2.get();
        System.out.println(s);
        System.out.println(s2);
    }
}

class Task1 implements Callable<String> {

    private FutureTask<String> task2;

    public Task1(FutureTask<String> futureTask) {
        this.task2 = futureTask;
    }

    @Override
    public String call() throws Exception {
        System.out.println("Task2 ————>在等待任务一的时候准备一下其他必要的事情");
        System.out.println("Task2 ————>什么都好了,稍微等待任务2");
        if ("".equals(task2.get()) || task2.get() != null) {
            System.out.println("Task2 ————>拿到任务2提供的材料了");
            task2.get();
            System.out.println("Task2 ————>开始组装了");
            System.out.println("Task2 ————>完成");
            return "Task2 ————>success";
        }
        return null;
    }
}

class Task2 implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("Task1 ————>正在准备阶段不要慌");
        System.out.println("Task1 ————>已经ok");
        return "Task1 ————>你要的材料,给你!";
    }
}


FutureTask重要成员变量

  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; //任务被打断成功

    /** 通过组合模式 组合了 Callable (主要为兼容Callable和Runnable) */
    private Callable<V> callable;
    /** 异步线程返回的结果 */
    private Object outcome; // non-volatile, protected by state reads/writes
    /** 当前任务所运行的线程 */
    private volatile Thread runner;
    /** 记录调用 get 方法时被等待的线程 */
    private volatile WaitNode waiters;

从FutureTask的一些成员变量和它的关系类图,可知FutureTask实现了RunnableFuture 接口,也就是说间接实现了 Runnnable 接口(RunnableFuture 实现了 Runnnable 接口),就是说 FutureTask 本身就是个 Runnnable,同时 FutureTask 也实现了 Future,也就是说 FutureTask 具备对任务进行管理的功能(Future 具备对任务进行管理的功能)。同时通过组合的方式内部成员变量维护着Callable属性,就可以供了两者(Callable和Runnable)一系列的转化方法,这样 FutureTask 就统一了 Callable 和 Runnable

FutureTask初始化方式:
FutureTask有且仅有两种初始化方式:

/**
     * 传入Callable的方式初始化
     */
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        // 任务处于初始化中()
        this.state = NEW;       // ensure visibility of callable
    }

    /**
     * 使用Runnable的方式作为初始化,V result作为返回类型,
     * 因为Runnable没有返回值的,所以通过V 指定FutureTask返回类型
     * 是通过Executors进行转换使其为Callable类型
     */
    public FutureTask(Runnable runnable, V result) {
      /**
       * Executors.callable 方法把 runnable 适配成 RunnableAdapter,
       * RunnableAdapter 实现了 callable,所以也就是把 runnable 
       * 直接适配成了 callable。
       * 适配的源码如下Executors.callable
     */
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
    
    // 主要是Calledable和Runnable两者直接不能做相互转换,
    // 所以需要借助RunnableAdapter进行,两者作为转换进行适配
 public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }

   static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }

FutureTask一些重要方法
1、get方法.
FutureTask实现了Future接口的两个get方法,其中无参数的get()如下:

  /**
     * 等待结果返回如果任务被取消了则会抛出CancellationException 异常,
     * 而在执行的任务的时候被中断会抛出InterruptedException
     */
    public V get() throws InterruptedException, ExecutionException {
    // 查看任务状态
        int s = state;
        //  COMPLETING  表示任务执行中,如果任务处于属于任务执行中或者新建的时候。
        // 则使用awaitDone方法进行等待,其中无参的get方法是不做等待处理。
        if (s <= COMPLETING)
        // 无限阻塞(false) 有点傻逼慎用,
            s = awaitDone(false, 0L);
        return report(s);
    }
    
    // 可以被打断,打断则抛出InterruptedException 
    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        // 计算出任务的等待时长,如果一直等待的话,终止时间为 0
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        // 声明一个为空的等待节点
        WaitNode q = null;
        // 默认不排队
        boolean queued = false;
        // 进入无线死循环
        for (;;) {
           // 再次期间,如果执行任务的线程被终止,则将其移除等待节点
           // 并抛出终止异常
            if (Thread.interrupted()) {
                // 把当前线程移除等待节点
                removeWaiter(q);
                throw new InterruptedException();
            }
            // 查看当前任务的状态
            int s = state;
            // 执行完成之后,返回
            if (s > COMPLETING) {
                // 如果在等待节点不为空,则首先将thread置为空
                if (q != null)
                    q.thread = null;
                return s;
            }
            // 如果任务正在执行,则使其放弃使用当前的cpu的使用权(yield()),重新竞争
            // 主要是为了防止cpu在等待获取任务结果时cpu飙升
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
           // 如果等待节点为空,则新建一个用于存放等待的节点*(节点属性为当前线程) 
            else if (q == null)
                q = new WaitNode();
          // 默认不排队(false) 
            boolean queued = false; 
            // 默认第一次都会执行这里,执行成功之后,queued 就为 true,就不会再执行了 
            // 把当前 waitNode 当做 waiters 链表的第一个
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
              // 如果设置了超时时间,并过了超时时间的话,从 waiters 链表中删除当前 wait                                        
            else if (timed) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                 // 没有过超时时间,线程进入 TIMED_WAITING 状态
                LockSupport.parkNanos(this, nanos);
            }
            else
                // 没有设置超时时间,进入 WAITING 状态,无线等待,不可取。
                LockSupport.park(this);
        }
    }
  // 等待任务的节点
  static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
    }
    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);
    }

带参数的get方法
带参数的get方法( get(long timeout, TimeUnit unit))源码如下:

 public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        if (unit == null)
            throw new NullPointerException();
        int s = state;
        // 如果没有完成则,调用awaitDone()方法进行等待,等待时间过长则抛出超时异常。
        if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
        return report(s);
    }

get 方法虽然名字叫做 get,但却做了很多 wait 的事情,当发现任务还在进行中,没有完成时,就会阻塞当前进程,等待任务完成后再返回结果值。阻塞底层使用的是 LockSupport.park 方法,使当前线程进入 WAITING 或 TIMED_WAITING 状态。
运行方法run
1、run 方法是没有返回值的,通过给 outcome 属性赋值(set(result)),get 时就能从 outcome 属性中拿到返回值;
2、FutureTask 两种构造器,最终都转化成了 Callable,所以在 run 方法执行的时候,只需要执行 Callable 的 call 方法即可,在执行 c.call() 代码时,如果入参是 Runnable 的话, 调用路径为 c.call() -> RunnableAdapter.call() -> Runnable.run(),如果入参是 Callable 的话,直接调用。

   /**
     * run 方法可以直接被调用,也可以开启新的线程进行调用(Excetors.submit或者Thread.start)
     */
 public void run() {
        // 状态不是任务创建,或者当前任务已经有线程在执行了,直接返回
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable; // 为内部维护的Callable对象
            // Callable 不为空,并且已经初始化完成
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    // 赋值给返回值
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                   // 异常则将结果置为null
                    result = null;
                    ran = false;
                    setException(ex);
                }
                 // 给 outcome 赋值
                if (ran)
                    set(result);
            }
        } finally {
            // 运行程序必须是非空的,直到状态被确定为防止并发调用运行()
            runner = null;
            // 在任务状态经过转轮后必须重新读取状态以防止中断泄露
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

FutureTask的取消方法

   // 取消任务,如果任务正在执行,尝试打断
    public boolean cancel(boolean mayInterruptIfRunning) {
       //任务状态不是创建 并且不能把 new 状态置为取消,直接返回 false
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {  //  进行取消操作,打断可能会抛出异常,选择 try finally 的结构
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // 状态设置成已打断
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
           // 清除线程
            finishCompletion();
        }
        return true;
    }

FutureTask的是否取消方法

  public boolean isCancelled() {
         // 直接放回状态
        return state >= CANCELLED;
    }

以上就是对FutureTask源码分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值