面试必问--手撕FutureTask源码

FutureTask作为面试必问的知识点,重要性可想而知。因此,下面给大家逐条分析Doug Lea大神的源码。

首先是介绍下FutureTask类中的变量

//表示当前task的状态
private volatile int state;
//表示当前task尚未执行
//场景:1、任务刚新建还未入队列
//     2、任务刚新建已入队列,还未被线程执行
//     3、任务刚新建已入队列,正在被线程执行run() 注意:当执行任务有结果后才会修改当前任务状态
private static final int NEW          = 0;
//表示当前task正在结束,但是还未完全结束的一种临界状态
private static final int COMPLETING   = 1;
//表示当前task正常结束
private static final int NORMAL       = 2;
//表示当前task执行过程中发生异常,内部封装的callable.run() 向上抛出了异常
private static final int EXCEPTIONAL  = 3;
//表示当前task被取消
private static final int CANCELLED    = 4;
//表示当前task正在中断中
private static final int INTERRUPTING = 5;
//表示当前task已中断
private static final int INTERRUPTED  = 6;

//submit(runnable/callable)     runnable 使用装饰者模式装饰为 callable
private Callable<V> callable;
//正常情况下:任务执行结束,outcome保存执行结果。 即callable的返回值
//异常情况下:callable向上抛出异常信息,outcome保存异常信息
private Object outcome; 
//当前任务被执行时,保存执行当前任务的当前线程的对象引用
private volatile Thread runner;
//因为会有很多线程去get当前任务的结果。所以,这里使用 链表 这种数据结构
private volatile WaitNode waiters;

static final class WaitNode {
    volatile Thread thread;
    volatile WaitNode next;
    //thread变量 保存线程对象引用
    //next变量 保存当前节点的下一个节点
    WaitNode() { thread = Thread.currentThread(); }
}

执行一个task的步骤是:submit(runnable/callable) -> newTaskFor(runnable/callable) -> execute(task) -> threadpool。
1、调用submit()提交我们自定义的runnable/callable程序。
2、调用newTaskFor()将传入的runnable/callable程序封装成FutureTask。
3、调用execute()将传入的task放入到线程池中。
4、当线程池中有空闲线程,就会执行任务的run()。否则,就会进入任务队列等待被执行

//submit(runnable/callable) -> newTaskFor(runnable/callable) -> execute(task) -> threadpool
//当线程池中没有空闲线程来执行当前任务,则会先进入任务队列中,等待空闲线程调用;如果有就直接执行。
//任务执行入口
public void run() {
    //条件1:true -> 当前任务的状态不是新建状态。(可能已经被执行或者取消)
    //条件2:true -> cas失败,表示当前有其它线程抢占了这个任务
    if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
        return;

    //当前线程开始执行当前任务
    //前置条件:1、当前任务的状态是新建状态 2、当前线程抢占到当前任务的执行权
    try {
        //表示外部传入的自定义的业务程序。 是callable或者是用runnable装饰后的callable
        Callable<V> c = callable;
        //条件1:防止空指针异常
        //条件2:防止有外部线程将任务cancel了
        if (c != null && state == NEW) {
            //要返回的结果
            V result;
            //true -> callable程序执行成功,未抛出异常
            //false -> callable程序执行失败,抛出异常
            boolean ran;
            try {
                //执行任务
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                //说明当前任务正常结束
                //设置result给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;
        //只会在当前任务被cancel时执行
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}
protected void setException(Throwable t) {
    //使用cas把任务当前状态设置成完成中
    //执行失败的情况:外部线程在当前线程执行set之前把当前任务cancel(很小概率事件)
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        //将callable
        outcome = t;
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
        //唤醒waiters中的所有线程
        finishCompletion();
    }
}
protected void set(V v) {
    //使用cas把任务当前状态设置成完成中
    //执行失败的情况:外部线程在当前线程执行set之前把当前任务cancel(很小概率事件)
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
        //将结果赋值给outcome后,将当前任务的状态直接修改为NORMAL(正常完成)状态
        //putOrderedInt(Object obj, long offset, int value) obj:包含要修改field的对象 offset:obj中整型field的偏移量 value:field将被设置的新值
        //设置obj对象中offset偏移地址对应的整型field的值为指定值。
        //这是一个有序或者有延迟的putIntVolatile方法,并且不保证值的改变被其他线程立即看到。
        //只有在field被 volatile 修饰并且期望被意外修改的时候使用才有用。
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        //唤醒waiters中的所有线程
        finishCompletion();
    }
}
private void handlePossibleCancellationInterrupt(int s) {
    // It is possible for our interrupter to stall before getting a
    // chance to interrupt us.  Let's spin-wait patiently.
    if (s == INTERRUPTING)
        while (state == INTERRUPTING)
            //释放cpu资源
            Thread.yield(); // wait out pending interrupt
}

说完run(),接下来继续手撕get()方法。
这里需要注意的是:
1、get任务执行结果的线程并不只有一个,而是有很多个在同一链表中的线程竞争获取。
2、当任务还未执行完毕,线程就会进行休眠,等待其它线程唤醒(可能是正常唤醒,也可能是任务中断)

//场景:多个线程等待当前任务执行完成后的结果
public V get() throws InterruptedException, ExecutionException {
    //获取当前任务状态
    int s = state;
    //true -> 任务状态可能为 未执行、正在执行、完成中
    //当任务状态为以上情况时,调用get()的外部线程会被阻塞
    if (s <= COMPLETING)
        //返回线程当前状态
        s = awaitDone(false, 0L);
    return report(s);
}
private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    //System.nanoTime() 返回的是纳秒,nanoTime返回的可能是任意时间,甚至可能是负数
    //timed true -> 表示带超时时间,deadline = System.nanoTime()
    //      false -> 表示不带超时时间,deadline = 0
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    //引用当前线程封装成WaitNode对象
    WaitNode q = null;
    //表示是否入队
    boolean queued = false;
    //自旋
    for (;;) {
        //ture -> 说明当前线程是被其它线程中断而唤醒的
        //interrupted() 返回true后 会将Thread的中断标记重置为false
        if (Thread.interrupted()) {
            //当前线程node出队
            removeWaiter(q);
            //向get()抛出中断异常
            throw new InterruptedException();
        }

        //当前线程被其它线程正常唤醒(使用了unpark(thread))的场景,继续执行自旋逻辑
        //获取当前任务最新状态
        int s = state;
        //true -> 表示当前任务已经有结果了
        if (s > COMPLETING) {
            //true -> 说明已经为当前线程创建node节点,需要执行 node.thread = null (helpGC)
            if (q != null)
                q.thread = null;
            //返回任务状态
            return s;
        }
        //true -> 表示当前任务正在完成中
        else if (s == COMPLETING) // cannot time out yet
            //将当前cpu的资源释放,进行下次抢占cpu资源
            //注意:yield()会释放CPU资源,但是是与其它线程一起重新抢占资源。当前线程可能还会获取到执行权,也有可能被其他线程获取到
            Thread.yield();

        //场景:第一次自旋
        //true -> 当前线程还未创建WaitNode对象
        else if (q == null)
            //为当前线程创建WaitNode对象,并把当前线程赋值到thread全局变量中
            q = new WaitNode();
        //场景:第二次自旋
        //true -> 当前线程已经创建WaitNode对象,但是还未入队
        else if (!queued){
            //把当前线程node节点的next指针 指向到原队列的头结点
            //waiters 表示队列头指针
            q.next = waiters;
            //用cas方式设置waiters指针指向当前线程的node节点(头插法)
            //true -> 入队成功
            //false -> 有其它线程抢先入队,进入下次自旋
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset, waiters, q);
        }
        //场景:第三次自旋
        //判断是否有带入超时时间
        else if (timed) {//有超时时间的场景
            nanos = deadline - System.nanoTime();
            //true -> 表示等待超时了
            if (nanos <= 0L) {
                //当前线程node出队
                removeWaiter(q);
                //无论完成与否,直接返回当前状态
                return state;
            }
            //当前线程进入有时间限制的休眠状态,有其它线程将其 唤醒 或者 中断 或者 时间到了 就会重新激活
            LockSupport.parkNanos(this, nanos);
        }
        else//无超时时间的场景
            //park() 会将当前线程状态变为waitting(休眠)
            //当前线程进入休眠状态, 有其它线程将其 唤醒 或者 中断 就会重新激活
            LockSupport.park(this);
    }
}
private V report(int s) throws ExecutionException {
    //正常情况下:outcome保存的是callable运行出来的结果
    //非正常情况下:outcome保存的是callable运行时抛出的异常信息
    Object x = outcome;
    //true -> 表示当前任务正常完成
    if (s == NORMAL)
        //直接返回callable运行结果
        return (V)x;
    //true -> 表示当前任务被取消
    if (s >= CANCELLED)
        //抛出取消中断异常
        throw new CancellationException();
    //抛出自定义的callable程序执行时产生的异常
    throw new ExecutionException((Throwable)x);
}

接下来就说下cancel()。

//mayInterruptIfRunning 设成false话,不允许在线程运行时中断,设成true的话就允许。
public boolean cancel(boolean mayInterruptIfRunning) {
        //state == NEW:true -> 表示当前任务处于运行中 或者 处于线程池队列中
        //UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)
        //  true -> 表示cas成功,将当前任务状态修改为 中断中 或者 取消
        //注意,这里的if取反,上述条件都为true才会执行下面的逻辑。否则直接返回false
        if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;

        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    //获取执行当前task的线程
                    Thread t = runner;
                    //可能会为null,因为可能当前task还在任务队列中排队
                    if (t != null)
                        //如果不为null,则给runner线程一个中断信号
                        //只是会给任务一个中断标志,能否中断要看task中是否有响应中断的程序
                        t.interrupt();
                } finally { // final state
                    //执行完中断后将任务的状态改为已中断
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            //唤醒waiters中的所有线程
            finishCompletion();
        }
        return true;
    }

最后要讲下finishCompletion()这个方法,看了上面的源码可以发现,有3个地方都执行了这个方法,分别是cancel()、set()、setException()。
其作用就是当任务执行有结果了(无论好坏)都会唤醒waiters链表中的 所有 线程继续执行自旋逻辑。

//唤醒所有waiters中的线程
private void finishCompletion() {
    // assert state > COMPLETING;
    //将waiters头结点赋值给q
    for (WaitNode q; (q = waiters) != null;) {
        //用cas将头结点置为null,防止该任务被其它线程取消 且 help gc
        if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
            for (;;) {//自旋
                //获取当前节点的thread对象
                Thread t = q.thread;
                //true -> thread对象存在防止空指针
                if (t != null) {
                    //清空当前节点引用的thread对象 help gc
                    q.thread = null;
                    //唤醒该线程
                    LockSupport.unpark(t);
                }
                //获取当前节点的下一个节点
                WaitNode next = q.next;
                //true -> 已经处于队列末尾
                if (next == null)
                    //退出自旋
                    break;
                //清空当前节点的next指针指向的对象引用 help gc
                q.next = null; // unlink to help gc
                //当前节点指针指向下一个节点
                q = next;
            }
            //所有waiters队列中的线程唤醒完成
            break;
        }
    }
    //自定义扩展的操作
    done();
    //释放资源 help gc
    callable = null;        // to reduce footprint
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值