并发编程理论 - Future体系和FutureTask实现原理

本文深入解析FutureTask的工作原理,包括其内部结构、生命周期状态变化及其实现机制,特别是LockSupport的park/unpark方法如何用于线程的等待唤醒。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1、Future

2、RunnableFuture

3、FutureTask

    1)、FutureTask结构(结构和可能的生命周期路径)

    2)、FutureTask实现原理(LockSupport的park、parkNanos + 自旋、unpark实现的等待唤醒机制)


    我们知道并发编程可以使用Runnable【源码标注@since jdk1.0】不带返回值的回调函数,也可以使用Callable<V> 【源码标注Callable @since 1.5 并且由并发大师Doug Lea编写】带返回值的回调函数。也就是从jdk5之后才有带返回值的编程方式,那么需要对原有的进行扩展、兼容,再具体进行实现。
扩展:Future接口
兼容:RunnableFuture接口
实现类:FutureTask类(或者其扩展子类;或者RunnableFuture的其他实现类)

1、Future

    针对带返回值方式的异步线程任务,设置了一个标准(接口Future),其最最要的接口如上如:SheduledFuture、CompletableFuture、ForkJoinFuture这些之前都大概分析过,但是有一个比较重要的分支是RunnableFuture,下面专门分析:

      Future的接口规范如下,后面在TaskFuture中的实现进行分析:

public interface Future<V> {
    // 取消任务
    boolean cancel(boolean mayInterruptIfRunning);

    // 是否被取消了
    boolean isCancelled();

    // 是否执行完成
    boolean isDone();

    // 阻塞获取结果
    V get() throws InterruptedException, ExecutionException;

    // 带超时地阻塞获取结果
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

 

2、RunnableFuture

    RunnableFuture的主要实现就是TaskFuture,先看看RunnableFuture的结构(实现了两个接口Future、Runnable,所以个人理解是即适配了原来的jdk1.0的Runnable,又扩展了Future):

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

 

3、FutureTask

public class FutureTask<V> implements RunnableFuture<V> {
    /**
     * The run state of this task, initially NEW.  The run state
     * transitions to a terminal state only in methods set,
     * setException, and cancel.  During completion, state may take on
     * transient values of COMPLETING (while outcome is being set) or
     * INTERRUPTING (only while interrupting the runner to satisfy a
     * cancel(true)). Transitions from these intermediate to final
     * states use cheaper ordered/lazy writes because values are unique
     * and cannot be further modified.
     *
     * Possible state transitions:
     * NEW -> COMPLETING -> NORMAL
     * NEW -> COMPLETING -> EXCEPTIONAL
     * NEW -> CANCELLED
     * NEW -> INTERRUPTING -> INTERRUPTED
     */
    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;

    /** Object类型,存放回调函数的真正结果,或者存放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;

    // 既然有 volatile修饰的state(解决可见性、有序性),那么CAS肯定少不了
    private static final sun.misc.Unsafe UNSAFE;
}

    1)、FutureTask结构(结构和可能的生命周期路径)

设置TaskFuture的生命周期状态state,并且设置了所有可能的路径,结合Future定义的接口方法,已经具体的FutureTask实现:

NEW -> COMPLETING -> NORMAL
NEW -> COMPLETING -> EXCEPTIONAL
NEW -> CANCELLED
NEW -> INTERRUPTING -> INTERRUPTED

NEW                    = 0; // 创建完成(在构造器中设置)

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

COMPLETING     = 1; // 正在完成(在cas中完成,通过set、setException方法)

protected void set(V v) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        finishCompletion();
    }
}

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

NORMAL             = 2; // 正常执行完成(在putOrderedInt,中完成set方法【如上】)
EXCEPTIONAL   = 3; // 异常执行完成(在putOrderedInt,中完成setException方法【如上】)
CANCELLED      = 4; // 取消(在Cancel方法中判断完成)


INTERRUPTING = 5; // 正在被中断

也是在cancel方法中,只有在running的状态下才会去interrupt,否则就是取消【如果任务都没有执行,就没必要中断线程了】。
INTERRUPTED  = 6; // 被中断完成了(在finally中,cas设置为取消的过去式)

    线程的中断问题可以参见:并发编程模式 - Two-Phase Termination两阶段终止模式

 

volatile WaitNode waiters; 属性定义的下一个节点的waiters,可以理解成链表,只是链表的key为线程,结构如下:
static final class WaitNode {
    volatile Thread thread;
    volatile WaitNode next;
    WaitNode() { thread = Thread.currentThread(); }
}

在哪里处理设置的呢,finishCompletion中   WaitNode next = q.next;  后面分析。

 

    2)、FutureTask实现原理(LockSupport的park、parkNanos + 自旋、unpark实现的等待唤醒机制

get

    Future定义的get()方法就是阻塞获取结果、get(time, TimeUnit)带超时地阻塞获取结果,其实现原理可能不陌生就是等待唤醒机制。等待唤醒机制使用 synchronized + Object类的 wait、notify(notifyAll)方法可以实现之前在分析Dubbo的DefaultFuture的get方法,异步转同步的过程(并发编程模式 - Guarded Suspension设计模式);而当前使用LockSupport的park、unpark方法实现。

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING) // 状态小于等待完成,即New或者COMPLETING
        s = awaitDone(false, 0L);
    // 最终返回结果
    return report(s);
}

report比较简单,处理异常,执行返回 Object类型的outcome属性:

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

awaitDone

// 不带超时,传入的参数为: false, 0L
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
	final long deadline = timed ? System.nanoTime() + nanos : 0L;
	WaitNode q = null;
	boolean queued = false;
	for (;;) {
		// 自旋中,判断是否被中断,中断则移除该 NodeWait节点
		if (Thread.interrupted()) {
			removeWaiter(q);
			throw new InterruptedException();
		}
		// 判断状态大于正在完成, 那么也肯定是正常或者异常完成、或者中断
		int s = state;
		if (s > COMPLETING) {
			if (q != null)
				q.thread = null;
			return s;
		}
		// 状态为正在完成,则让渡当前的线程的CPU使用权,当前线程状态从 【运行】 切换为 【可运行】
		else if (s == COMPLETING) // cannot time out yet
			Thread.yield();
		// 如果当前线程还没有创建节点,则创建节点并添加到“链表”中去,表明当前线程也准备进入等待该任务完成的队列中去
		else if (q == null)
			q = new WaitNode();
		// queued第一次进入为false,则会进入该判断,在cas中将上一步创建的节点添加到链表中
		else if (!queued)
			queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
					q.next = waiters, q);
		// 最后如果自旋时没有超时,则将线程挂起(LockSupport的park或者parkNanos),等待其他操作的 LockSupport#unpark(当前挂起的线程)
		else if (timed) {
			nanos = deadline - System.nanoTime();
			if (nanos <= 0L) {
				removeWaiter(q);
				return state;
			}
			LockSupport.parkNanos(this, nanos);
		}
		else
			LockSupport.park(this);
	}
}

 

我们找一下发现只要 finishCompletion方法【自旋,将节点cas设置为null】中会调用 LockSupport#unpark(线程):

private void finishCompletion() {
	// assert state > COMPLETING;
	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;
		}
	}
    // 留给子类实现的空方法,当前类为空(比如ExecutorCompletionService就有实现扩展)
	done();

	callable = null;        // to reduce footprint
}

    调用finishCompletion的方法有:cancel、set、setException,刚好与上面的state状态的切换相对应。

我们就看正常流程的,调用方法为TaskFuture的run方法。之前分析了FutureTask是Future和Runnable的实现,那么只要该Runnable调用了start方法,就会等待被CPU切中,执行run方法中的回调函数,如下:

public void run() {
	// 状态不是刚创建 或者使用当前线程去控制处理该节点,则返回
	if (state != NEW ||
			!UNSAFE.compareAndSwapObject(this, runnerOffset,
					null, Thread.currentThread()))
		return;
	try {
		Callable<V> c = callable;
		//该Callable回调函数不为null,并且状态还是New状态
		if (c != null && state == NEW) {
			V result;
			boolean ran;
			try {
				// 执行回调方法,
				result = c.call();
				ran = true;
			} catch (Throwable ex) {
				result = null;
				ran = false;
				// 设置异常,最终会调用 LockSupport#unpark
				setException(ex);
			}
			if (ran)
				// 正常执行完 最终会调用 LockSupport#unpark
				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;
		if (s >= INTERRUPTING)
			handlePossibleCancellationInterrupt(s);
	}
}

至此,整个流程就全梳理通了...

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值