FutureTask源码解析二

本篇主要介绍FutureTask源码

我们知道FutureTask实现了RunnableFuture接口,即Runnable接口和Future接口,Runable可以对应FutureTask的task,表示FutureTask本质上也是一个task任务,而Future对应FutureTask中的Future,表示了我们对于这个task任务可以执行某些操作,如判断任务是否执行完毕,获取任务的执行结果,取消任务的执行等等。

状态
FutureTask中,状态由state属性来表示,它是volatile类型的,保证了某个线程对它的改变其它线程可见。

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;

state属性是贯穿整个FutureTask的最核心的属性,该属性的值代表了任务在运行过程中的状态,随着任务的执行,状态将不断地进行转变,从上面的定义中可以看出,总共有7种状态:包括了1个初始态,2个中间态和4个终止态。

注意:任务的中间状态是一个瞬态,它非常的短暂。而且任务的中间态并不代表任务正在执行,而是任务已经执行完了,正在设置最终的返回结果。也可以这么说,只要state不处于NEW状态,就说明任务已经执行完毕。

这里的执行完毕是指传入的Callable对象的call方法执行完毕或者抛出了异常。所以这里的COMPLETING的名字显得有点迷惑性,它并不意味着任务正在执行中,而意味着call方法已经执行完毕,正在设置任务执行的结果。

而将一个任务的状态设置成终止态只有三种方法:set   setException   cancel

队列
FutureTask中,队列的实现是一个单向链表,它表示所有等待任务执行完毕的线程的集合。我们知道,FutureTask实现了Future接口,可以获取Task的执行结果,那么如果获取结果时,任务还没有执行完毕怎么办呢?那么获取结果的线程就会在一个等待队列中挂起,直到任务执行完毕被唤醒。这一点有点类似于我们之前学习的AQS中的sync queue。

在AQS源码分析中,我们知道对于挂起的线程,通常会被
包装成某种类型的数据结构扔到等待队列中,然后等待被唤醒,这里也不例外,FutureTask中结点结构如下

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

注意:FutureTask中的这个单向链表是当做栈来使用的,确切来说是当做Treiber栈来使用的,它是一个线程安全的栈,使用CAS来完成入栈出栈操作,为什么要使用一个线程安全的栈呢,因为同一时刻可能有多个线程都在获取任务的执行结果,如果任务还在执行过程中,则这些线程就要被包装成WaitNode扔到Treiber栈的栈顶,即完成入栈操作,这样就有可能出现多个线程同时入栈的情况,因此需要使用CAS操作保证入栈的线程安全,对于出栈的情况也是同理。

由于FutureTask中的队列本质上是一个Treiber栈,那么使用这个队列就只需要一个指向栈顶节点的指针就行了,在FutureTask中,就是waiters属性,事实上,它就是整个单向链表的头节点

/** Treiber stack of waiting threads */
private volatile WaitNode waiters;

FutureTask队列结构如下

CAS操作
CAS操作大多数是用来改变状态的,在FutureTask中也不例外。我们一般在静态代码块中初始化需要CAS操作的属性的偏移量

// Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long stateOffset;
    private static final long runnerOffset;
    private static final long waitersOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = FutureTask.class;
            stateOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("state"));
            runnerOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("runner"));
            waitersOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("waiters"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

从这个静态代码块中我们也可以看出,CAS操作主要针对3个属性,包括staterunnerwaiters,说明这3个属性基本是会被多个线程同时访问的。其中state属性代表了任务的状态,waiters属性代表了指向栈顶节点的指针,这两个我们上面已经分析过了。runner属性代表了执行FutureTask中的Task的线程。为什么需要一个属性来记录执行任务的线程呢?这是为了中断或者取消任务做准备的,只有知道了执行任务的线程是谁,我们才能去中断它。

定义完属性偏移量之后,接下来就是CAS操作本身。FutureTask的CAS操作最终调用的还是Unsafe的compareAndSwapXXX方法。

核心属性

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;

    /** The underlying callable; nulled out after running */
    private Callable<V> callable;
    /** The result to return or exception to throw from get() */
    private Object outcome; // non-volatile, protected by state reads/writes
    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;
    /** Treiber stack of waiting threads */
    private volatile WaitNode waiters;

关于statewaitersrunner三个属性我们上面已经解释过了。剩下的callable属性代表了要执行的任务本身,即FutureTask中的Task部分,为Callable类型,之所以用Callable而不用Runnable是因为FutureTask实现了Future接口,需要获取任务的执行结果。outcome属性代表了任务的执行结果或者抛出的异常,为Object类型,也就是说outcome可以是任意类型的对象,所以当我们将正常的执行结果返回给调用者时,需要进行强制类型转换,返回由Callable定义的V类型。这5个属性综合起来就完成了整个FutureTask的工作。

构造函数

// FutureTask构建初始态为NEW

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;      
}
 
// 如果参数是Runnable,通过适配器适配成Callable了类型,这里上一篇讲过。
public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;
}

接口实现
分析源码之前,我们先声明一下,FutureTask中会涉及到两类线程,一类是执行任务的线程,它只有一个,FutureTask的run方法就由该线程来执行;一类是获取任务执行结果的线程,它可以有多个,这些线程可以并发执行,每一个线程都是独立的,都可以调用get方法来获取任务的执行结果。如果任务还没有执行完,则这些线程就需要进入Treiber栈中挂起,直到任务执行结束或者等待的线程自身被中断。

public class FutureTask<V> implements RunnableFuture<V> {
    ...
}

①Runnable接口实现
FutureTask的run方法实现如下

public void run() {
    // 如果任务状态不为NEW,表明任务已经启动或结束,直接返回
    // 任务未启动,未初始态,但是cas更新执行当前任务的线程失败,直接返回
    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 {
                result = c.call();
                ran = true;
            } catch (Throwable ex) { // 执行出现异常
                result = null;
                ran = false;
                setException(ex);  // 设置错误信息,赋值outcome
            }
            if (ran) // 执行正常
                set(result); // 设置正常输出结果,赋值outcome
        }
    } 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) // 此时state为INTERRUPTING,INTERRUPTED
            handlePossibleCancellationInterrupt(s);
    }
}

protected void set(V v) {
    // 此处state先更新为COMPLETING中间状态,COMPLETING是一个非常短暂的中间态,表示正在设置执行的结果
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
        // 由于state属性被设置成volatil, putOrderedInt等价于putIntVolatile
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        finishCompletion();
    }
}

protected void setException(Throwable t) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = t;
        // 此处不能判断state是COMPLETING中间态还是INTERRUPTING中间态,因为在多线程的环境中,在当前线程执行run方法的同时,有可能其他线程取消了任务的执行,此时其他线程就可能对state状态进行改写,所以此处不用cas。
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
        finishCompletion();
    }
}

private void finishCompletion() {
    // assert state > COMPLETING;
    // 前面讲FutureTask队列结构是提过,它是栈结构,而waiters相当于栈顶指针
    for (WaitNode q; (q = waiters) != null;) {
        // 设置waiters为null,相当于清空整个栈
        if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
            for (;;) {
                // 逐个唤醒等待结果的线程
                Thread t = q.thread;
                if (t != null) {
                    q.thread = null;
                    LockSupport.unpark(t);
                }
                WaitNode next = q.next;
                if (next == null)
                    break;
                q.next = null; // unlink to help gc
                q = next;
            }
            break;
        }
    }

    done(); // 提供给子类实现
    callable = null; // 断开callable的引用
}

//该方法是一个自旋操作,如果当前的state状态是INTERRUPTING,我们在原地自旋,直到state状态转换成终止态
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)
            Thread.yield(); // wait out pending interrupt
}

②Future接口的实现
cancel(boolean mayInterruptIfRunning)
取消一个任务的执行

public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {    // in case call to interrupt throws exception
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally { // final state
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
        finishCompletion();
    }
    return true;
}

首先有以下三种情况之一的,cancel操作一定是失败的:
1.任务已经执行完成了
2.任务已经被取消过了
3.任务因为某种原因不能被取消

其它情况下,cancel操作将返回true。值得注意的是,cancel操作返回true并不代表任务真的就是被取消了,这取决于发动cancel状态时,任务所处的状态:
1.如果发起cancel时任务还没有开始运行,则随后任务就不会被执行;
2.如果发起cancel时任务已经在运行了,则这时就需要看mayInterruptIfRunning参数了:
       如果mayInterruptIfRunning 为true, 则当前在执行的任务会被中断
       如果mayInterruptIfRunning 为false, 则可以允许正在执行的任务继续运行,直到它执行完

isCancelled()

public boolean isCancelled() {
    return state >= CANCELLED;
}

isDone()

public boolean isDone() {
    return state != NEW;
}

 get()(超时版本get()类似)

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING) // 此处s=NEW、COMPLETING
        s = awaitDone(false, 0L);
    return report(s);
}

private int awaitDone(boolean timed, long nanos) throws InterruptedException {
    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) { // state = 2,3,4,5,6
            if (q != null)
                q.thread = null;
            return s;
        }
        else if (s == COMPLETING) // cannot time out yet
            Thread.yield(); // 让出cpu,等待任务state状态为终态
        else if (q == null)
            q = new WaitNode(); // 首次进入线程包装成waitNode
        else if (!queued) // 入对,这里是在栈顶
            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
            LockSupport.park(this); // 阻塞
    }
}

private void removeWaiter(WaitNode node) {
        if (node != null) {
            node.thread = null; //将需要去除的节点,thread赋值为null
            retry:
            for (;;) { 
                for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
                    s = q.next;
                    if (q.thread != null)
                        pred = q;
                    else if (pred != null) { //q.thread==null,表示该节点是需要在队列中去除的节点,故直接将pred.next=s,重组队列
                        pred.next = s;
                        if (pred.thread == null)  //如果这个pred节点恰好是需要去除的节点,则进行循环,重组队列
                            continue retry;
                    }

                    // 如果要删除的node是栈顶元素,则cas为node的下一个结点
                    else if (!UNSAFE.compareAndSwapObject(this, waitersOffset, q, s)) 
                        continue retry;
                }
                break;
            }
        }
    }

private V report(int s) throws ExecutionException {
    Object x = outcome;
    if (s == NORMAL) // state为正常状态,直接返回
        return (V)x;
    if (s >= CANCELLED) // CANCELLED,INTERRUPTING,INTERRUPTED时抛出异常
        throw new CancellationException();
    throw new ExecutionException((Throwable)x);
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值