使用java多线程解决问题的时候,为了提高效率,我们常常会异步处理一些计算任务并在最后异步的获取计算结果,这个过程的实现离不开Future接口及其实现类FutureTask。FutureTask类实现了Runnable, Future接口,接下来我会通过源码对该类的实现进行详解。

我们先看下FutureTask中的主要方法如下,可以看出FutureTask实现了任务及异步结果的集合功能。看到这块的方法,大家肯定会有疑问,Runnable任务的run方法返回空,FutureTask如何依靠该方法获取线程异步执行结果

//以下五个方法实现接口Future中方法
public boolean isCancelled(); 
public boolean isDone(); 
public boolean cancel();
public V get() throws InterruptedException, ExecutionException;
public V get(long timeout, TimeUnit unit);
//实现接口Runnable中方法
public void run();

我们在使用中会构造一个FutureTask对象,然后将FutureTask扔到另一个线程中执行,而主线程继续执行其他业务逻辑,一段时间后主线程调用FutureTask的get方法获取执行结果。

public class FutureTaskTest {
	private static ExecutorService executorService = Executors.newFixedThreadPool(1);
	public static void main(String []args) {
		Callable callable = new AccCallable(2, 3);
		FutureTask futureTask = new FutureTask(callable);
		executorService.execute(futureTask);
		System.out.println("go to do other things in main thread");
		try {
			Thread.sleep(1000);
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("go back in main thread");
		try {
			int result = (int) futureTask.get();
			System.out.println("result is " + result);
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}
		catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
	static class AccCallable implements Callable<Integer> {
		private int a;
		private int b;
		public AccCallable(int a, int b) {
			this.a = a;
			this.b = b;
		}
		@Override
		public Integer call() throws Exception {
			System.out.println("acc a and b in threadId = " + Thread.currentThread().getName());
			return a + b;
		}
	}
}

在分析实现前,我们先想下如果让我们实现一个类似FutureTask的功能,我们会如何做?因为需要获取执行结果,需要一个Object对象来存执行结果。任务执行时间不可控性,我们需要一个变量表示执行状态。其他线程会调用get方法获取结果,在没达到超时的时候需要将线程阻塞或挂起。

因此需要一个队列类似的结构存储等待该结果的线程信息,这样在任务执行线程完成后就可以唤醒这些阻塞或挂起的线程,得到结果。FutureTask的实际实现也是类似的逻辑,具体如下

//futureTask执行状态
private volatile int state;
//具体的执行任务,会在run方法中抵用callable.call()
private Callable<V> callable;
//执行结果
private Object outcome; 
//获取结果的等待线程节点
private volatile WaitNode waiters;
/**
* Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
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;
public FutureTask(Callable<V> callable) {
	if (callable == null)
	throw new NullPointerException();
	this.callable = callable;
	this.state = NEW;
	// ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
	//构造函数传入runnable对象时调用静态工具类Executors的方法转换为一个callable对象
	this.callable = Executors.callable(runnable, result);
	this.state = NEW;
	// ensure visibility of callable
}

如前所述,FutureTask的执行线程中会调用其run()方法执行任务,我们看下这块逻辑

public void run() {
	//1.如果执行状态不是NEW或者有其他线程执行该任务,直接返回
	if (state != NEW ||
	!UNSAFE.compareAndSwapObject(this, runnerOffset,
	null, Thread.currentThread()))
	return;
	try {
		Callable<V> c = callable;
		//2.如果执行状态是NEW,即任务还没执行,直接调用callable.call()方法获取执行结果
		if (c != null && state == NEW) {
			V result;
			Boolean ran;
			try {
				result = c.call();
				ran = true;
			}
			catch (Throwable ex) {
				result = null;
				ran = false;
				//3.发生异常,更新status为EXCEPTIONAL,唤醒挂起线程
				setException(ex);
			}
			//4.如果结果成功返回,调用set方法将设置outcome,更改status执行状态,唤醒挂起线程
			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;
		if (s >= INTERRUPTING)
		handlePossibleCancellationInterrupt(s);
	}
}
protected void set(V v) {
	//将执行状态变更为COMPLETING
	if (UNSAFE.compareAndSwapint(this, stateOffset, NEW, COMPLETING)) {
		//设置执行结果
		outcome = v;
		//设置执行状态为NORMAL
		UNSAFE.putOrderedint(this, stateOffset, NORMAL);
		// final state
		//执行完成后处理操作,具体就是遍历阻塞链表,删除链表节点,并唤醒每个节点关联的线程
		finishCompletion();
	}
}

以上就是任务执行线程做的逻辑,以上逻辑也回答了FutureTask如何得到执行结果的疑问。下面我们看下用户调用get方法获取执行结果时的实现逻辑,这个时候FutureTask可能处理各种状态,即可能没有执行,执行中,已完成,发生异常等

public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
	if (unit == null)
	throw new NullPointerException();
	int s = state;
	//执行状态是NEW或者COMPLETING时执行awaitDone将线程加入等待队列中并挂起线程
	if (s <= COMPLETING &&
	(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
	throw new TimeoutException();
	//根据执行状态status进行结果封装
	return report(s);
}
//我理解这块是get的核心逻辑
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) {
			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();
		//将节点加入到等待队列中,waiters相当于头阶段,不断将头结点更新为新节点 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);
	}
}

这里需要说明一点,FutureTask中的阻塞队列新加入的节点都在头结点并且next指向之前的头结点,waitars指针总是指向新加入节点,通过waitars可以遍历整个等待队列,具体截图如下。此外等待队列节点结构很简单成员变量只有线程引用和next指针,这里再列出器接口