引言

FutureTask是Java并发编程中常用的一种类,它实现了RunnableFuture接口,用于异步执行任务。FutureTask可以用来表示一个异步计算的结果,它允许用户在任务完成时获取结果,或者在任务执行过程中取消任务。本文将深入解析FutureTask的源码,揭示其背后的奥秘与实现技巧。

FutureTask简介

FutureTask是Java并发包java.util.concurrent中的一个类,它实现了RunnableFuture接口,该接口继承自Runnable和Future接口。RunnableFuture接口定义了两个方法:run()和get()。

  • run():执行任务的具体逻辑。
  • get():获取任务执行的结果。

FutureTask内部维护了一个状态,用于表示任务的执行状态,包括NEW、RUNNING、FINISHED和CANCELLED等。

FutureTask源码解析

1. 构造函数

FutureTask的构造函数接受一个Runnable类型的参数,用于执行任务。以下是FutureTask构造函数的源码:

public FutureTask(Runnable runnable, V result) {
    this.state = -1;       // 初始化状态为NEW
    this.count = 0;
    this.runnable = runnable;
    this.result = result;
}

2. run()方法

run()方法是FutureTask的核心方法,用于执行任务。以下是run()方法的源码:

public void run() {
    if (this.state != NEW ||
        !U.compareAndSwapInt(this, STATE, NEW, RUNNING)) {
        return;
    }
    try {
        this.runnable.run();
        set(result);
    } catch (Throwable ex) {
        setException(ex);
    }
}

在run()方法中,首先检查任务的状态是否为NEW,如果不是,则直接返回。然后,使用compareAndSwapInt方法尝试将状态从NEW修改为RUNNING。如果修改成功,则执行任务;如果失败,则说明其他线程已经执行了任务,当前线程不再执行。

3. get()方法

get()方法是FutureTask提供的一个获取任务执行结果的方法。以下是get()方法的源码:

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s >= RUNNING)
        return report(s);
    else if (s == NEW)
        waitDone();
    throw new IllegalStateException();
}

private void waitDone() throws InterruptedException {
    U.putObject(this, WAITERS, Thread.currentThread());
    try {
        for (;;) {
            int s = state;
            if (s >= RUNNING)
                return;
            if (U.compareAndSwapInt(this, STATE, s, s | WAITING))
                LockSupport.park(this);
        }
    } finally {
        U.putObject(this, WAITERS, null);
    }
}

private V report(int s) throws ExecutionException {
    Object x = result;
    if (x == EXCEPTION_CAUGHT)
        throw (ExecutionException) x;
    if (x instanceof Error)
        throw (Error) x;
    return (V) x;
}

在get()方法中,首先检查任务的状态。如果状态大于等于RUNNING,则调用report()方法获取结果。如果状态为NEW,则调用waitDone()方法等待任务完成。

4. set()方法和setException()方法

set()方法和setException()方法用于设置FutureTask的结果。以下是这两个方法的源码:

public void set(V v) {
    if (!U.compareAndSwapInt(this, STATE, RUNNING, FINISHED)) {
        throw new IllegalStateException();
    }
    result = v;
    U.putObject(this, THREAD, null);
    U.putObject(this, WAITERS, null);
    LockSupport.unparkAll(this);
}

public void setException(Throwable ex) {
    if (!U.compareAndSwapInt(this, STATE, RUNNING, FINISHED)) {
        throw new IllegalStateException();
    }
    result = EXCEPTION_CAUGHT;
    U.putObject(this, THREAD, null);
    U.putObject(this, WAITERS, null);
    LockSupport.unparkAll(this);
}

在set()方法中,首先检查任务的状态。如果状态为RUNNING,则将状态修改为FINISHED,并将结果设置为v。在setException()方法中,将结果设置为EXCEPTION_CAUGHT,表示任务执行过程中发生了异常。

实现技巧

  1. 使用compareAndSwapInt方法实现状态转换:FutureTask使用compareAndSwapInt方法实现状态转换,该方法是一个原子操作,可以保证状态转换的原子性。

  2. 使用LockSupport.park()和LockSupport.unpark()实现线程阻塞和唤醒:FutureTask使用LockSupport.park()方法使线程阻塞,使用LockSupport.unpark()方法唤醒线程。

  3. 使用volatile关键字保证共享变量的可见性:FutureTask中的共享变量result使用volatile关键字修饰,保证其可见性。

总结

FutureTask是Java并发编程中常用的一种类,它实现了RunnableFuture接口,用于异步执行任务。本文深入解析了FutureTask的源码,揭示了其背后的奥秘与实现技巧。通过理解FutureTask的源码,可以帮助我们更好地使用它,提高程序的性能和可靠性。