Java源码解读系列5—FutureTask(JDK1.8 )

1 概述

FutureTask类是Future接口和Runnable接口的实现类,主要为了解决异步计算并获取返回值,缺点也很明显,获取返回值的方法get是阻塞的,如果获取不到返回值会阻塞当前线程,是典型的阻塞编程

在这里插入图片描述

下面是一个使用用例:

public class CallableApp implements Callable<String> {

    private String name;

    public CallableApp(String name) {
        this.name = name;
    }

    @Override
    public String call() throws Exception{
        Thread.sleep(5000);
        return name;
    }


    public static void main(String[] args) throws  Exception {
       //构造方法1
        FutureTask task = new FutureTask(new CallableApp("Hello World!"));

          //构造方法2
//        FutureTask task = new FutureTask(new Runnable() {
//            @Override
//            public void run()  {
//                try {
//                    Thread.sleep(5000);
//                    System.out.println(Thread.currentThread().getName() + "执行完了!");
//                }catch (InterruptedException e){
//                    e.printStackTrace();
//                }
//
//
//            }
//        }, "Hello World!");

        //启动线程
        new Thread(task).start();

        //阻塞当前线程直到获取返回值
        System.out.println(task.get());
    }

}

那么如何获得非阻塞编程?答案是通过谷歌Guava库的ListenableFuture或者JDK8自带的CompletableFuture类。有问题才会有创新,本文主要围绕着使用讲解FutureTask的源码,其余的见后续博文。

2 构造函数

FutureTask有两种构造方法,第一种的入参是一个Callable对象,线程启动时候执行Callable种的call方法

  public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

第二种有两个入参,分别是Runnable接口的实现类和返回结果,但是本质和第一种是一样的,会通过适配器模式进行封装为Callable接口的实现类。

public FutureTask(Runnable runnable, V result) {
        //将封装Runnable接口的实现类成Callable接口的实现类
        this.callable = Executors.callable(runnable, result);
        //将状态设置为初始化
        this.state = NEW;       // ensure visibility of callable
    }
    
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;
        }
    }

3 全局变量

运行状态state使用volatile修饰,保证所有调用get方法获取结果的线程的可见性。
其主要有以下4种转换状态:
1)NEW -> COMPLETING -> NORMAL(正常执行完)
2)NEW -> COMPLETING -> EXCEPTIONAL(发生异常)
3)NEW -> CANCELLED(被取消)
4)NEW -> INTERRUPTING -> INTERRUPTED(被中断)

   //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接口的实现类,是具体干活的
    private Callable<V> callable;

    //存储返回结果或异常信息
    private Object outcome; // non-volatile, protected by state reads/writes
    
   //调用Callable中call方法的线程
    private volatile Thread runner;
  
    //使用一种叫Treiber stack的栈,栈里面的每个节点存储阻塞线程,特点是先进后出,可以用于并发场景
    private volatile WaitNode waiters;

WaitNode类是栈中存储信息的等待节点,用于记录排队的线程,通过next引用指向下一个节点

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

4 正常运行

线程启动调用run方法

  public void run() {
        //判断状态是否为初始化状态
        //通过CAS操作将执行全局变量runnerrun指向调用Callable的线程,即执行到run方法的线程
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            //再次校验状态是否为NEW且传入的callable不为空
            if (c != null && state == NEW) {
                V result;
                //标记变量,ture表示正常执行完,false表示发生异常
                boolean ran;
                try {
                   //调用Callable中的call方法,获取返回值
                    result = c.call();
                    ran = true;
             
                } catch (Throwable ex) {       //捕获异常
                    //返回结果设置为空
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                   //执行成功,调用set修改状态
                    set(result);
            }
        } finally {
             //判断任务是否被中断
            int s = state;
            if (s >= INTERRUPTING)
            //调用中断处理逻辑
                handlePossibleCancellationInterrupt(s);
        }
    }

线程call中方法正常执行完,调用set,

   protected void set(V v) {
        //将状态从初始化改为完成中
        //如果CAS执行失败,有可能当前任务被取消或者中端,即调用了cancel方法
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {          
           //将返回结果赋值给outcome
            outcome = v;
            //将状态从完成中改为已经完成
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL);
            //将等待队列中的线程唤醒和清空
            finishCompletion();
        }
    }   

当状态为取消, 发生异常或者正常结束时,才会调用finishCompletion方法,用于将等待队列中的线程唤醒和清空。

private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            //通过CAS将栈顶的引用指向null
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
               //自选
                for (;;) {
                    //当前等待节点中包含的线程
                    Thread t = q.thread;
                    //等待线程不为null
                    if (t != null) {
                       //当前节点的线程引用指向null
                        q.thread = null;
                        //当前节点包含的线程处于阻塞状态,唤醒该线程
                        LockSupport.unpark(t);
                    }
                    //当前等待节点的下一个节点
                    WaitNode next = q.next;
                    //下一个等待节点为空
                    if (next == null)
                         //结束自旋
                         break;
                     //将当前节点的next引用指向空
                    q.next = null; // unlink to help gc
                    //将下一个等待节点赋值给q
                    q = next;
                }
                break;
            }
        }

        done();
         //将callable的引用指向空
        callable = null;        // to reduce footprint
    }

5 发生异常

FutrueTask类在run()方法中执行Callable中的call方法如果发生异常,会调用setException处理异常信息

 protected void setException(Throwable t) {
        //将状态修改为完成中
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {         
           //将异常信息添加到返回结果
            outcome = t;
            //修改任务的执行状态,没有使用 UNSAFE的putIntVolatile方法而是采用putOrderedInt,
           // 这是因为state状态使用volatile修饰,可以保证可见性,同时putOrderedInt方法的写入代价低于putIntVolatile
           //通过CAS将完成中状态转换为异常状态
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); 
            //将栈中等待的线程唤醒,清空栈
            finishCompletion();
        }
    }

6 取消任务

取消任务使用cancel方法,入参mayInterruptIfRunning是布尔状态。
true表示使用中断逻辑,false使用取消逻辑。

 public boolean cancel(boolean mayInterruptIfRunning) {
         //校验状态,说明任务不可能进入Callable中的call方法,且最终状态可能要转换为中断/取消/正常完成这三种状态之一,
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {   
            //tuew表示被中断
            if (mayInterruptIfRunning) {
                try {
                    //获取调用call方法的线程
                    Thread t = runner;
                    //判断线程是否为空
                    if (t != null)
                        //不为空则打上中断标记
                        t.interrupt();
                } finally { // final state
                     //将状态修改为已中断
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }

7 获取任务结果

获取任务结果使用get方法,这里以不带超时参数为例进行讲解

 public V get() throws InterruptedException, ExecutionException {
        int s = state;
        //判断线程是否处于初始化状态
        if (s <= COMPLETING)
           //false表示不使用超时时间
            s = awaitDone(false, 0L);
        return report(s);
    }

awaitDone是比较复杂逻辑,主要用get方法获取结果时,但call方法还在执行,可以让当前线程阻塞,从而不会占用CPU。

 private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        //获取终止时间,time为ture时才有用
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        //创建一个栈队列等待节点
        WaitNode q = null;
        //是否入栈标准,false表示还没有
        boolean queued = false
        //自旋
        for (;;) {
            //判断线程是否被标记为中断
            //cancel方法入参为true时会给线程打上中断标志 
            if (Thread.interrupted()) {
                //移除中断节点
                removeWaiter(q);
                //抛出中断异常,此时无法得到返回结果
                throw new InterruptedException();
            }

           //将状态值赋予s
            int s = state;
            
           //说明任务正常执行完成/被取消/被中断,因此需要不需要再执行awaitDone方法
            if (s > COMPLETING) {
                if (q != null)
                    //help gc
                    q.thread = null;
                return s;
            }
            //状态已经从初始化为变为完成中状态,但是最终状态还不确定,此时需要让出CPU,重新进入自旋
            else if (s == COMPLETING) 
                Thread.yield();
                //等待节点为空,装件
            else if (q == null)
                q = new WaitNode();
          //等待节点还没入栈
            else if (!queued)
               //通过CAS将栈的头节点指向当前节点
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
           //处理超时逻辑
            else if (timed) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    //移除当前节点
                    removeWaiter(q);
                    return state;
                }
                //阻塞当前线程直到超时时间为止
                LockSupport.parkNanos(this, nanos);
            }
            else
               //将当前线程阻塞
               //在没有使用超时时间的前提下,当前线程进入阻塞最少需要自旋3次
                LockSupport.park(this);
        }
    }

将当前获取等待结果的线程从等待栈中移除

 private void removeWaiter(WaitNode node) {
       //校验节点是否为空
        if (node != null) {
            node.thread = null;
            retry:
            //自旋
            for (;;) {       
                
                //当node是栈顶节点,q.thread  == null, pred == null
                执行CAS将node的下一个节点置为栈顶。
                如果CAS失败,说明栈顶节点已经被替换为其他等待线程的节点
                
                //当node非栈顶节点,将node的前驱节点的next'引用指向node的后继节点,释放node
                
                for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
                    s = q.next;
             
                    if (q.thread != null)
                        pred = q;
                    else if (pred != null) {
                        pred.next = s;
                        if (pred.thread == null) // check for race
                            continue retry;
                    }
                    else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                          q, s))
                        continue retry;
                }
                break;
            }
        }
    }

8 参考文献

  1. JDK7在线文档
    https://tool.oschina.net/apidocs/apidoc?api=jdk_7u4
  2. JDK8在线文档
    https://docs.oracle.com/javase/8/docs/api/
  3. Bruce Eckel,Java编程思想 第4版. 2007, 机械工业出版社
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值