Future 与 FutureTask
A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation. The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready.
重点: Future类是 异步计算 的结果类,isDone()
方法来判断当前计算是否完成,get()
取得异步计算结果值,cancel(boolean mayInterrupt)
来中断该任务,这里的中断需要由传入参数来决定:
- 如果当前任务还没有开始,那么调用
cancel()
会正常取消任务执行 - 如果当前任务已经开始,调用
cancel(false)
会允许任务执行完毕,而calcel(true)
则会强制当前任务结束
而我们知道,在Java实现异步,需要用到多线程,那么很自然的我们需要一个子类来实现Runnable
接口与Future
接口,通过另外开辟一个新线程的方式来计算结果
从类关系图可以看出,FutureTask
实现了RunnableFuture
这个整合了Runnable
接口与Future
接口的类。通过给新线程传递FutureTask参数并使用start()
方法,便可创建多线程环境:
FutureTask<Integer> futureTask = new FutureTask<>(new MyCall());
Thread thread = new Thread(futureTask);//先使用Thread来创建线程
thread.start();
int res = futureTask.get();
System.out.println("结果 : " + res);
public class MyCall implements Callable<Integer> {
public Integer call() throws Exception {
int res = 0;
for(int i = 0; i < 100; i++){
res += i;
}
return res;
}
}
注意到这里使用了FutureTask对Callable对象作封装,我们先来看一下FutureTask构造函数:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
FutureTask
需要一个Callable
对象作为参数,并检查Callable
对象是否为空,然后会将成员变量private Callable<V> callable
设置为传入对象,并初始化当前任务状态为NEW
使用Thread创建线程(补充,可直接快速划过)
随后使用Thread
创建新线程,传入FutureTask
对象
public Thread(Runnable target) {
this(null, target, "Thread-" + nextThreadNum(), 0);
}
启动start()方法,由Java内置start0()创建新线程:
public synchronized void start() {
if (threadStatus != 0)//threadStatus表明线程状态为NEW,跟上面FutureTask一样
throw new IllegalThreadStateException();
group.add(this);//通过SecurityManager取得system线程组,将新的Thread加到线程组中,如果构造函数传入了线程组,则使用此线程组
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
如果当前线程启动发生错误,由于当前线程被加入了当前的线程组(在不创建其他ThreadGroup
时默认加到system
线程组),所以调用当前线程组threadStartFailed(Thread t)方法:
void threadStartFailed(Thread t) {
synchronized(this) {
remove(t);
nUnstartedThreads++;
}
}
而进一步调用了remove(Thread t)方法:
private void remove(Thread t) {
synchronized (this) {
if (destroyed) {
//并发处理,如果当前线程已经被销毁,则直接返回
return;
}
for (int i = 0 ; i < nthreads ; i++) {
//循环ThreadGroup中threads[]线程数组,找到未能启动线程,然后覆盖此线程
if (threads[i] == t) {
System.arraycopy(threads, i + 1, threads, i, --nthreads - i);
// Zap dangling reference to the dead thread so that
// the garbage collector will collect it.
threads[nthreads] = null;
break;
}
}
}
}
这里多说一句,上面的threads[nthreads] = null
是 'Effective Java’推荐的做法,如果当前对象引用不再使用,应该手动置为null
使其被GC以防内存泄露
说了这些,现在线程也启动起来了,这个线程会自动执行我们传入的Runnable
对象的run()
方法,当然了由于我们传入的是FutureTask
对象,那又有什么不同呢?
FutureTask增强的run方法:
public void run() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
//当前任务不是新任务(比如COMPLETING,NORMAL,INTERRUPTED等,直接返回)
return;
try {
Callable<V> c = callable;//使用了我们之前传入的Callable对象
if (c != null && state == NEW) {//检查合法性
V result;//我们需要的异步结果
boolean ran;
try {
result = c.call();//手动调用call()方法,并将返回值给result
ran = true;
} catch (Throwable ex) {//如果发生非运行期错误,设置STATE = EXCEPTIONAL
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);//运行成功后,将类成员变量outcome设置为result
}
} finally {
runner = null;//运行结束后,将当前运行线程置null
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
来看一下上面的set(V val)
方法:
protected void set(V v) {
//通过内置run()方法中调用此方法,设置本次运行结果的最终状态为NORMAL
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = v;
STATE.setRelease(this, NORMAL); // final state
finishCompletion();
}
}
好啦!现在我们的Runnable
对象FutureTask task1
已经执行完毕了,我们调用T get()
来获取执行的结果:
注意到T get()
方法有重载方法
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)//如果当前状态是待完成或者NEW
s = awaitDone(false, 0L);//等待执行完毕
return report(s);
}
IMPORTANT :
注意,调用了awaitDone()
后,会将其他线程挂起,执行完当前任务所在线程,举个例子,修改上述的MyCall
类,使线程休眠1000毫秒,在主线程中调用封装了MyCall
对象的FutureTask
时(使用新Thread
),如果执行其他操作,1s以后尝试获取结果,那么会顺利的获取。如果调用MyCall
对象后立即使用int res = futureTask.get()
,会造成主线程挂起,等待返回结果后再执行
如果get()
方法传入了long参数和TimeUnit参数:
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
//如果在timeout时间之类没有执行完毕,awaitDone返回<=1的数,get抛出超时错误
throw new TimeoutException();
return report(s);
}
取得异步结果的最后一步便是调用report(int state)
方法:
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)//结果正常情况下,转为泛型类型直接返回outcome
return (V)x;
if (s >= CANCELLED)//CANCELLED,INTERRUPTING,INTERRUPTTED级别错误
throw new CancellationException();
throw new ExecutionException((Throwable)x);//EXCEPTIONAL级别错误
}
综上,FutureTask对于Callable对象的封装可表示如下:
当然了这篇文章主要是来介绍FutureTask
对于Callable
的封装使用,Runnable
对象可通过Executor类中
的RunnableAdapter<T>
适配器转换为Callable
对象:
private static final class RunnableAdapter<T> implements Callable<T> {
private final Runnable task;
private final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;//注意这里返回了传入的值,所以使用get方法拿到的是传入的值
}