FutureTask源码了解

FutureTask分析
基于jdk11 但是和1.8有一些小差别
先看一下简单使用


//静态内部类创建单例线程池  -利用了类机制
    static class SinglePool{
  public static   ThreadPoolExecutor  executor = new ThreadPoolExecutor(4, 50, 2000, TimeUnit.SECONDS, new ArrayBlockingQueue(100));
    }

    /**
     * 通过Calllable来创建线程,这个接口你补了前面两种执行完任务没有返回值的缺点,
     * 这个方法执行完任务需要返回值
     */
    static class Thread3 implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            int sum=0;
            TimeUnit.MILLISECONDS.sleep(1000);
            for (int i=0 ; i< 100 ; i++){
                sum+=i;
            }
            return sum;
        }
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1.创建一个实现了Calllable的类
        Thread3 thread3 = new Thread3();
        //2.创建FutureTask 把步骤一的类(任务)丢个FutureTask去执行
        //
        FutureTask task = new FutureTask(thread3);
        //3.把FutureTask的任务开辟一条线程去执行。
        new Thread(task,"线程三").start();
              if (!task.isDone()){
            System.out.println("任务没完成"+task.isDone());
        }
        if (!task.isCancelled()){
            System.out.println("任务是否取消"+task.isCancelled());
        }

//        if (task.cancel(true)) {
//            System.out.println("取消任务");
//        }
//        if (task.isCancelled()){
//            System.out.println("再去确认任务是否取消"+task.isCancelled());
//        }
       //线程一new Thread(()-> System.out.println(Thread.currentThread().getName()+"线程A"),"线程A").start();
        //线程二
new Thread(()-> System.out.println(Thread.currentThread().getName()+"线程B"),"线程B").start();
  
        System.out.println(task.get());
        System.out.println(Thread.currentThread().getName()+"任务执行完毕");
    }
}
控制台输出:
     	任务没完成false
        任务是否取消false
        线程A线程A
        线程B线程B
        4950
        main任务执行完毕

看看类图
FutureTask源码了解_第1张图片
先来分析一下全局变量

//futureTask是继承了RunnableFuture接口,而这个接口继承了runnable和future接口
public class FutureTask implements RunnableFuture {
//记录当前状态,使用了volatile关键字,作用使线程之间修改状态是可见的
   private volatile int state;
   //任务初始化状态
    private static final int NEW          = 0;
    //任务call方法已经结束设置任务结果但还没设置到outCome
    private static final int COMPLETING   = 1;
    //正常执行任务成功
    private static final int NORMAL       = 2;
    //异常状态,保存异常信息
    private static final int EXCEPTIONAL  = 3;
    //正在取消任务,只有调用了cancel才会使用
    private static final int CANCELLED    = 4;
    //正在尝试终端线程,但不一定成功
    private static final int INTERRUPTING = 5;
    //线程被中断后的状态
    private static final int INTERRUPTED  = 6;

    //继承了callable接口的任务类
    private Callable callable;
    ///执行完的结果
    private Object outcome; // non-volatile, protected by state reads/writes
    //负责运行callable任务的线程
    private volatile Thread runner;
    //用于指向单链表的头节点的节点
    private volatile WaitNode waiters;
}

看一下内部类的数据结果

 //内部wiatNode类,是一个单链表结果
 static final class WaitNode {
 //全部都使用了volatile标识,当状态改变在各个线程之间都是可见的
        volatile Thread thread;
        //后继节点(每一个节点存储的是调用该方法时的线程线程)
        volatile WaitNode next;
        //把当前线程设置为thread
        WaitNode() { thread = Thread.currentThread(); }
    }

接着看一下构造方法

FutureTask由于继承了Runnable,所以支持callable和runnable,如果传入的是runnable,它会调用一个适配器类把把runnable和callable 适配。

   //callable的构造方法,正常赋值
   public FutureTask(Callable callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // 状态是初始化状态
    }

    /**
     * runnable的构造方法
     */
    public FutureTask(Runnable runnable, V result) {
      //通过Executors内部的适配器类进行适配
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // 设置为初始化状态
    }

接着看一下run方法这些执行任务的方法

run方法有几种情况,

  1. 任务已经执行过,则直接返回
  2. 初始化状态(第一次),则进行 各自判断然后执行任务
  3. 任务可能被进行了中断处于中断状态

    public void run() {
    //判断状态和cas尝试操作把当前线程设置,判断是否是第一次,如果不是直接返回
        if (state != NEW ||
            !RUNNER.compareAndSet(this, null, Thread.currentThread()))
            return;
        try {
        //把c指向callable
            Callable c = callable;
            //如果c不为空,而且state是初始化状态
            if (c != null && state == NEW) {
            //用来装指向完的结果
                V result;
                //判断任务是否执行完成
                boolean ran;
                try {
                //执行实际任务,并赋值结果给result
                    result = c.call();
                    //设置为true表示已经完成任务
                    ran = true;
                } catch (Throwable ex) {
                //异常则设置为false
                    result = null;
                    ran = false;
                    //调用设置异常状态
                    setException(ex);
                }
                //不管有没有异常都要把结果设置
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            //最后把执行任务的线程指控方便 GC回收
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            //判断状态是否处于正在中断/或者中断
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
    //该方法是一直处于自旋一直到状态为中断状态才进行礼让
   private void handlePossibleCancellationInterrupt(int s) {
      
        if (s == INTERRUPTING)
            while (state == INTERRUPTING)
                Thread.yield(); // wait out pending interrupt
    }

接着方向set(result)方法

    protected void set(V v) {
    //修改状态,把new,该往正在设置结果
        if (STATE.compareAndSet(this, NEW, COMPLETING)) {
        //把结果赋值给outcome
            outcome = v;
            //状态修改为正常结束
            STATE.setRelease(this, NORMAL); // final state
            //最后调用下面的方法唤醒链表中的线程节点
            finishCompletion();
        }
    }
该方法是内部类waitNode的方法
  private void finishCompletion() {
        // 遍历链表,如果头节点不为空的情况,如果为空则证明没有等待阻塞的线程
        //否则遍历唤醒
        for (WaitNode q; (q = waiters) != null;) {
        //如果需要唤醒的头节点(waiters是唤醒节点的头节点)就是当前节点则把当前线程节点置空
            if (WAITERS.weakCompareAndSet(this, q, null)) {
                for (;;) {//死循环唤醒
                //获取当前节点的线程
                    Thread t = q.thread;
                    //线程不为空
                    if (t != null) {
                    //将等待阻塞线程置空
                        q.thread = null;
                        //并通过lockSupport唤醒在等待阻塞的唤醒线程
                        LockSupport.unpark(t);(partk()方法是让当前阻塞等待)
                    }
                    //获取下一个等待节点
                    WaitNode next = q.next;
                    //如果为空则结束
                    if (next == null)
                        break;
                        否则把q的后继节点置空
                    q.next = null; // unlink to help gc
                    //把下一个节点置给q,继续循环上述步骤一直把所以等待的线程唤醒为止
                    q = next;
                }
                break;
            }
        }
	// 这个方法在FutureTask是空实现的,是一个钩子方法
        done();
//将callable置空
        callable = null;        // to reduce footprint
    }

接着看获取结果的get方法是如何执行的

get方法有两种,

一种是一直阻塞式获取结果

判断当前状态,如果状态标识已经处于初始化或者正在设置结果状态则直接通过report获取结果,
换句话说,除了new和Completing状态其他状态一律任务是任务执行完成,是否正常完成则由report方法来进一步判断。如果是处于前面两种状态则 开始调用awaitDone方法阻塞其他线程等待任务执行结束

一种是限时阻塞时获取结果

执行逻辑与上述差不多

   //这是一直阻塞式 的get方法,如果任务没执行完成会让其他线程一直处于阻塞状态,阻塞状态的的线程则会用waitnode节点存储并通过调用part方法挂起来(从用户态切到内核态)
   public V get() throws InterruptedException, ExecutionException {
  //判断当前状态,如果状态标识已经处于初始化或者正在设置结果状态则直接通过report获取结果,
  //换句话说,除了new和Completing状态其他状态一律任务是任务执行完成,是否正常完成则由report方法来进一步判断。如果是处于前面两种状态则 开始调用awaitDone方法阻塞其他线程等待任务执行结束
        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();
            //判断和上面是一样的只是awaitDonw方法参数改变为有时间判断了
        int s = state;
        if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
        return report(s);
    }

private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        //设置开始时间为0
        long startTime = 0L;  
        //阻塞等待节点为q 当前为null
        WaitNode q = null;
        //是否出队为false
        boolean queued = false;
       
        for (;;) {
        //获取当前状态并且进行判断
            int s = state;
           //如果大于1则任务线程的任务已经执行完成或者取消/前面有讲只有前面两种状态才任务任务没执行完成
           if (s > COMPLETING) {
           //节点不为null则把节点的线程置空,任务的完成了并返回当前线程状态
                if (q != null)
                //则把
                    q.thread = null;
                return s;
            }
            //否则正在设置结果状态
            else if (s == COMPLETING)
                // We may have already promised (via isDone) that we are done
                // so never return empty-handed or throw InterruptedException
                //如果当前线程正在执行设置执行完成的结果,则当前线程进行礼让,让出cpu资源给任务线程进行任务,进入等待状态
                Thread.yield();
            else if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }
            //如果节点是空,则根据当前线程创建一个等待节点
            else if (q == null) {//判断时间
                if (timed && nanos <= 0L)
                    return s;
                q = new WaitNode();
            }
            //如果没入队,则通过waiteters调用cas把当前线程入队而且是头节点
            else if (!queued)
                queued = WAITERS.weakCompareAndSet(this, q.next = waiters, q);
            else if (timed) {//判断时间
                final long parkNanos;
                if (startTime == 0L) { // first time
                    startTime = System.nanoTime();
                    if (startTime == 0L)
                        startTime = 1L;
                    parkNanos = nanos;
                } else {
                //如果超时则异常该线程节点
                    long elapsed = System.nanoTime() - startTime;
                    if (elapsed >= nanos) {
                        removeWaiter(q);
                        return state;
                    }
                    parkNanos = nanos - elapsed;
                }
                // nanoTime may be slow; recheck before parking
                //如果还没完成,则继续挂起
                if (state < COMPLETING)
                    LockSupport.parkNanos(this, parkNanos);
            }
            else//否则阻塞线程,等待唤醒
                LockSupport.park(this);
        }
    }

  
  
 //report方法是获取结果返回的方法
private V report(int s) throws ExecutionException {
//把outcome结果赋值给x
        Object x = outcome;
        //判断是否是正常执行任务完成,如果是则正常返回
        if (s == NORMAL)
            return (V)x;
            //如果是取消任务则抛取消异常
        if (s >= CANCELLED)
            throw new CancellationException();
            //否则抛执行异常
        throw new ExecutionException((Throwable)x);
    }

取消任务的方法

//传入参数 是否取消
public boolean cancel(boolean mayInterruptIfRunning) {
//如果当前状态不等于new,并且修改状态为中断状态失败则返回false
        if (!(state == NEW && STATE.compareAndSet
              (this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
            //否则进行中断
        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                //获取运行任务线程,不等于kon则进行中断
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                        //最后修改状态
                } finally { // final state
                    STATE.setRelease(this, INTERRUPTED);
                }
            }
        } finally {
        //然后唤醒其他等待阻塞的线程节点
            finishCompletion();
        }
        return true;
    }

总结:FutureTask是一个异步任务类,整个大概运行过程是:创建一个FutureTask,并且传入任务类,如果任务是callable类型则直接创建,否则就是Runnable类型,则通过Executors的内部适配器类,对Callable和Runnable进行适配(这里使用了适配器模式),创建FutureTask完成后,则交给线程池或者线程,开启异步任务,这时候进入run方法,

run方法大致分三种情况

  • 情况1:可能任务已经非初始化状态(线程已经启动,当中途礼让,自己进行了阻塞等待状态),则通过cas修改运行callable任务的线程runner的状态置为当前线程
  • 情况2:该FutureTask还是初始化状态,则获取calllable,并且判断状态是否是NEW,然后执行任务方法call,如果中途遇到异常则修改状态为异常,执行任务结束后设置任务结果(不管是否正常完成)
  • 情况3: 最后进行状态判断是否处于中断中或者,中断状态,则调用方法进行进行中断,一直自旋并且线程进行礼让,到状态已经为中断成

但是如果正在执行run方法,中途杀出个程咬金调用get方法的时候这个时候跳转到get方法看如何出来调用get方法的线程(这里就不分,是否限制时间获取结果,统一分析无限制,一直阻塞的get方法)

  • get方法首先进行状态判断,如果处于初始化状态或者正在设置任务结果状态,则调用awaitDone,方法把调用线程进行阻塞,等待任务线程执行完毕,
  • 到这里是进入了awaitDoner方法,该方法是首先判断当前状态是否已经方法,
  • 情况1:如果完成,则把执行任务线程置空,并返回状态,
  • 情况2:任务线程正在执行设置结果状态,则当前线程线程(获取任务结果的线程或者其****他线程)进行礼让,把cpu资源让出来,给任务线程进行任务
  • 情况3:当前线程调用了中断状态,则把任务线程移出等待队列(线程)(这是jdk11才有的)
  • 情况4:如果q是空,则创建一个等待节点,并赋值给q
  • 情况5:如果没如等待队列,则通过cas,把q线程节点设置为头节点
  • 情况6(这是有时间的限制才有的):判断是否超时等待。
  • 情况7:如果都不是以上情况,则把线程挂起来进行
  • 任务执行完毕后就调用report方法获取结果,该方法还是对状态进行分析返回不同的结果

FutureTask+Callable+线程/或者线程池,内部是通过维护任务的状态、阻塞等待队列(链表)和CAS操作来实现的,而且底层使用了大量的Volatle+Cas操作

你可能感兴趣的:(源码系列,Java系列)