# 一、什么是 Future和 FutureTask
FutureTask
的Future
就源自于它的异步工作机制,如果我们在主线程中直接写一个函数来执行任务,这是同步的任务,也就是说必须要等这个函数返回以后我们才能继续做接下的事情,但是如果这个函数返回的结果对接下来的任务并没有意义,那么我们等在这里是很浪费时间的,而 FutureTask就提供了异步返回结果的机制,当执行一个FutureTask
任务的时候,系统可以接着做别的任务,在将来某个时间,FutureTask
任务完成后会返回FutureTask
对象来包装返回的结果,只要调用这个对象的get()
方法即可获取返回值。当然多线程中继承ThreadPoolExecutor
和实现Runnable
也可以实现异步工作机制,可是他们没有返回值。这时可以使用FutureTask
包装Runnable
或者Callable
对象,再使用FutureTask
来执行任务。
Future
接口和其唯一的实现类FutureTask
类一般用于表示异步计算的结果。Future
接口下提供方法来检查计算是否完成,等待其完成,并检索计算结果。 结果只能在计算完成后使用get()
进行检索,如有必要可进行阻塞,直到准备就绪。 取消由cancel
方法执行,isCancelled
方法用于检测计算是否被取消,isDone
方法用于检测计算是否完成。 提供其他方法来确定任务是否正常完成或被取消。

# 二、FutureTask 的使用
根据FutureTask
被执行的进度,FutureTask
对象共有3种状态:
【1】未启动:创建了一个FutureTask
对象但没有执行futureTask.run()
;
【2】已启动:futureTask.run()
方法正在执行;
【3】已完成:futureTask.run()
正常执行结束,或者futureTask
被取消futureTask.cancel()
,或者执行futureTask.run()
时抛出异常而异常结束;
# FutureTask 的启动
FutureTask
实现了Future
接口和Runnable
接口,因此FutureTask
对象的执行有两种方式:
【1】交给线程池的execute()
或submit()
执行;
import java.util.concurrent.*;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
class test{
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor tpe = new ThreadPoolExecutor(5, 10,100, MILLISECONDS, new ArrayBlockingQueue<Runnable>(5));
//用FutureTask包装Runnable或者Callable对象
FutureTask<String> future = new FutureTask<String>(new Callable<String>() {
@Override
public String call() {
try{
String a = "return String";
return a;
}
catch(Exception e){
e.printStackTrace();
return "exception";
}
}
});
//交给线程池的Execute或submit方法执行
tpe.submit(future);
try{
System.out.println(future.get());
}
catch(Exception e){
e.printStackTrace();
}
finally{
tpe.shutdown();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
FutureTask
是一个基于AQS同步队列实现的一个自定义同步组件,通过对同步状态state
的竞争实现acquire
或者release
操作。FutureTask
的内部类Sync
实现了AQS
接口,通过对tryAcquire
等抽象方法的重写和模板方法的调用来实现内部类Sync
的tryAcquireShared
等方法,然后聚合Sync
的方法来实现FutureTask
的get
,cancel
等方法;
FutureTask
的get
方法最终会调用AQS.acquireSharedInterruptibly
方法,这个方法操作成功的条件是同步状态为RAN
或者CANCELLED
,也就是说如果这个FutureTask
有线程E正在执行,那么这个FutureTask
的状态是RUN
,因此AQS.acquireSharedInterruptibly
方法调用失败,此时调用get
方法的线程被阻塞,添加到等待队列中(如下图线程D,其中A,B,C是已经被阻塞添加到等待队列中的线程)。当前面执行FutureTask
的线程E执行完毕,那么以原子方式更新同步状态state
的值为RAN
,并执行AQS.release
方法,然后唤醒等待队列中的第一个节点中的线程A,此时线程A出队列获得同步状态,并原子设置state
为RUN
,当线程A执行完毕,把state
原子更新为RUN
,然后唤醒线程B,以此类推,因此对于FutureTask
,同一时间只有一个线程执行这个任务。

# 四、FutureTask使用场景
当一个线程需要等待另一个线程把某个任务执行完以后它才能继续执行时;
有若干线程执行若干任务,每个任务最多只能被执行一次;
当多个线程试图执行同一个任务,但只能允许一个线程执行此任务,其它线程需要等这个任务被执行完毕以后才能继续执行时;
WARNING
Future 接口的局限性: 我们获取包含结果的Future
时,我们可以使用get
方法等待线程完成并获取返回值,该方法会阻塞主线程。