FutureTask源码分析

FutureTask的类图关系如下
FutureTask源码分析_第1张图片

1.Runable与Callable

java.lang.Runnable是一个接口,只有一个run()方法

public interface Runnable { public abstract void run(); }

run()方法的返回值是void,故在执行完任务后无法返回任何结果
  Callable是java.util.concurrent包下的,也是一个接口,也只有一个call()方法,类似于java.lang.Runnable的run()方法,实现Callable接口的类和实现Runnable接口的类都是可以被其它线程执行的任务

public interface Callable { 
    V call() throws Exception; 
}

可以看到call()方法是有返回值的,可以将执行的结果返回

Callable和Runnable的区别:

(1)Callable中定义的是call()方法,Runnable中定义的是run()方法
(2)Callable中的call()方法可以返回执行任务后的结果,Runnable中的run()方法无法获得返回值
(3)Callable中的call()方法定义了throws Exception抛出异常,抛出的异常可以在主线程
(4)Future.get()时被主线程捕获;Runnable中的run()方法没有定义抛出异常,运行任务时发生异常时也会上抛,因为即使不加默认也会上抛RuntimeException,但异常无法被主线程获取
(5)运行Callable任务可以拿到一个Future对象代表异步运算的结果
另外,Executors类中内部实现了RunableAdapter类,通过它可以将一个Runable和一个返回值result封装为一个Callable对象。

2.Future

Future是java.util.concurrent包下的一个接口,代表着一个异步计算的结果,可以通过get()获取线程执行的返回值
Future提供了三种功能:
(1)获取任务执行的结果
(2)取消任务
(3)判断任务是否完成 或 是否取消
因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask

3.FutureTask源码分析

public class FutureTask<V> implements RunnableFuture<V>

FutureTask实现了RunnableFuture接口,那么RunnableFuture又是什么呢?

public interface RunnableFuture<V> extends Runnable, Future<V> { 
    /** 
    * Sets this Future to the result of its computation * unless it has        
    been cancelled. 
    */ 
    void run(); 
}

RunnableFuture接口继承了Runnable和Future,所以它既是一个可以让线程执行的Runnable任务,又是一个可以获取Callable返回值的Future

3.1FutureTask的属性

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 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;
  • callable:表示要运行的任务对象,由构造FutureTask时生成;
  • outcome: 是任务执行后的结果或异常
  • runner:执行callable的线程;
  • waiters:是一个链表结构的头部指针,每一个节点表示等待获取结果的阻塞线程,后等待线程的会排在链表头部;
  • state 是任务的运行状态
    (1)初始化时是NEW;
    (2)任务终止的状态有NORMAL(正常结束)、EXCEPTIONAL(异常结束)、CANCELLED(被取 消)、INTERRUPTED(执行中被中断),这些状态是通过set()、setException、cancel()方法触发的;
    (3)COMPLETING 和 INTERRUPTING是两个中间状态,当正常结束设置outcome属性前是COMP LETING,设置后变成NORMAL;当中断运行中线程前是INTERRUPTING,调用thread.interrupt ()后是INTERRUPTED;
    FutureTask源码分析_第2张图片

3.2构造方法

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

public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

(1)构造方法参数是Callable定义的任务,并将state置为NEW,只有当state为NEW时,callable才能被执行;
(2)构造方法参数为Runnable和带泛型的result对象,由于Runnable本身是没有返回值的,故线程的执行结果通过result返回;可以看到通过runnable和result封装了个Callable,实际上是new RunnableAdapter(task, result),这个Adapter适配器将Runnable和result转换成Callable,并返回result;

3.3get实现

  get()方法有两个实现,一个是一直等待获取结果,直到任务执行完;一个是等待指定时间,超时后任务还未完成会上抛TimeoutException

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

public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

内部通过awaitDone()对调用线程进行阻塞,具体实现如下:

private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    //如果有超时,基于当前时间计算最终超时时间
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;
    for (;;) {
    //1.如果调用线程设置了中断标记,则从等待队列中移除节点并抛出异常
        if (Thread.interrupted()) {
            removeWaiter(q);
            throw new InterruptedException();
        }

        int s = state;
        // 2. 获取当前状态,如果状态大于COMPLETING
        // 说明任务已经结束(要么正常结束,要么异常结束,要么被取消)
        // 则把thread显示置空,并返回结果
        if (s > COMPLETING) {
            if (q != null)
                q.thread = null;
            return s;
        }
        // 3. 如果状态处于中间状态COMPLETING
        // 表示任务已经结束但是任务执行线程还没来得及给outcome赋值
        // 这个时候让出执行权让其他线程优先执行,等待状态发生改变后返回
        else if (s == COMPLETING) // cannot time out yet
            Thread.yield();
        else if (q == null)
           // 4. 如果等待节点为空,则构造一个等待节点
            q = new WaitNode();
        else if (!queued)
            // 5.如果还没有入队列,则把当前节点加入waiters指向的队列的对头            
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                 q.next = waiters, q);
        else if (timed) {
            // 如果需要等待特定时间,则先计算要等待的时间
            // 如果已经超时,则删除对应节点并返回对应的状态
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
                removeWaiter(q);
                return state;
            }
             // 6.阻塞等待特定时间
            LockSupport.parkNanos(this, nanos);
        }
        else
            // 7.阻塞等待直到被其他线程唤醒
            LockSupport.park(this);
    }
}

上面这段代码有两个生僻函数的使用
(1) compareAndSwapObject
改方法解决的是并发中原子的更新引用对象
//如果offset指向的值等于expected,则原子的将Java变量更新为x
compareAndSwapObject(Object o, long offset, object expected, object x)
(2)LockSupport
LockSupport为Java并发的工具类,park表示表示阻塞当前线程,另外还有unpark表示唤醒阻塞的线程;
  awaitDone方法执行的结果就是,在任务未执行结束时,所有希望获取结果的线程都将在waiters指向的队列中进行等待;添加的顺序为,后执行get的线程被添加在队首。在threadA和threadB都阻塞等待之后的waiters结果如图。
这里写图片描述
report方法根据awaitDown返回的状态进行判断,然后返回任务执行的结果或异常。

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);
}

3.4run实现

public void run() {
//1
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
            //2
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
            //3
                setException(ex);
            }
        //4
            if (ran)
                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;
    //5
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

(1) 标记1
状态如果不是NEW,说明任务或者已经执行过,或者已经被取消,直接返回;状态如果是NEW,则尝试把当前执行线程保存在runner字段中;
(2) 标记2
执行任务,如果任务执行过程中出现了异常则执行“标记3”,顺利的话就会执行“标记4”;
(3) 标记3

protected void setException(Throwable t) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = t;
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
        finishCompletion();
    }
}

原子的改变状态为COMPLETING,然后将异常结果复制给outcome,再将状态设置为EXCEPTIONAL;这样就完成了前面说的状态变化过程:NEW->COMPLETING->EXCEPTIONAL,并且将最终结果保存在outcome中;最后调用finishCompletion方法,唤醒所有等待的线程并返回结果outcome。
(4) 标记4
set方法和setException方法调用基本相同,唯一不同的是状态变化过程:NEW->COMPLETING->NORMAl;
(5) 标记5
如果执行任务时,cancel被调用了,那么状态将被修改为INTERRUPTING或CANCELLED,handlePossibleCancellationInterrupt方法将被执行

private void handlePossibleCancellationInterrupt(int s) {
    if (s == INTERRUPTING)
        while (state == INTERRUPTING)
            Thread.yield(); // wait out pending interrupt
}

该方法的理解需要结合cancel方法进行应用场景的举例:
当任务正在执行但并未结束,用户调用了cancel方法且参数为true,那么状态被设置INTERRUPTING
,此时任务执行完成开始调用handlePossibleCancellationInterrupt方法,因为cancel方法还未执行如下代码

if (mayInterruptIfRunning) {
    try {
        Thread t = runner;
        if (t != null)
            t.interrupt();
    } finally { // final state
        UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
    }
}

所以会调用Thread.yield去放弃cpu的资源停止运行,直到上面的代码被执行状态变INTERRUPTED,最后任务运行结束。
finishCompletion方法如下

for (WaitNode q; (q = 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;        // to reduce footprint

其核心作用就是换新waiters队列中所有等待的线程,唤醒的方法就是使用LockSupport.unpark,在get方法中是使用的park方法让线程休眠的。

3.5cancel实现

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;
}

(1) 修改状态为NEW-> INTERRUPTING-> INTERRUPTED或NEW-> CANCELLED;
(2) 如果是调用参数为true,则会设置认为执行线程的中断标记,这个中断标记如果是单纯的从FutrueTask来看是没有任何作用的,它的作用在于callable所执行的任务是否调用了阻塞方法并且捕获了InterruptedException异常进行处理,否则调用t.interrupt并无作用;
(3) 最后调用finishCompletion线程唤醒等待队列;

4思考

Future.cancel()真的能取消任务的执行吗?

  答案是“不一定”,根据JDK中的方法注释“Attempts to cancel execution of this task”,即尝试去取消执行的任务;
如果任务正在执行,且调用cancel()时参数mayInterruptIfRunning传的是true,那么会对执行线程调用interrupt()方法。
那么问题就变成了interrupt()方法能中断线程执行吗?
  interrupt()方法不会中断正在运行的线程。这一方法实际上完成的是在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait()、Thread.join()、Thread.sleep()等阻塞,那么它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。
  如果线程没有被阻塞,调用interrupt()将不起作用
  那么即使线程正在阻塞状态,并抛出了InterruptedException,线程能否真的取消执行还要看代码中是否捕获了InterruptedException和有没有做相应的对中断标示的判断逻辑

你可能感兴趣的:(Java并发,FutureTask,源码,分析,callable,runable)