Guava中的ListenableFuture提供了添加监听的方法addListener(),该方法添加的监听会在任务执行完成之后执行(看到这就应该想起FutureTake类的done()方法)。就像事件触发回调一样,方便我们进行复杂业务时的逻辑处理。
ListenableFuture接口
该接口继承Future接口,并在Future接口上添加addListener()方法,目的就是添加任务执行完成后的处理逻辑,可以多次调用。不管Future任务为何种状态,都可以调用该方法。
方法定义如下:
void addListener(Runnable listener, Executor executor);
ExecutionList类
在理解ListenableFutureTask之前,我们必须要清楚ExecutionList的作用,因为ListenableFutureTask是将监听器代理给ExecutionList处理的。
- RunnableExcutorPair类
ExecutionList的内部类,相当于一个结点的定义,维护了任务runnbale信息和执行任务的executor信息,以及下一个监听结点的信息。这是一个典型的链表结构。因为我们可以理解为该类就是链表结点的定义
源码如下:
private static final class RunnableExecutorPair {
final Runnable runnable;
final Executor executor;
@Nullable RunnableExecutorPair next;
RunnableExecutorPair(Runnable runnable, Executor executor, RunnableExecutorPair next) {
this.runnable = runnable;
this.executor = executor;
this.next = next;
}
}
从源码的构造方法的最后一句this.next=next,我们可以看出新构建的结点都是插入到最前端,这是典型的链表头插法,说明链表中结点的顺序与提交监听器的任务顺序是相反的。
- 成员变量
RunnableExcutorPaire:链表结构, 用于维护执行器,每一个节点代表一个执行器
executed:任务(指的是ListenableFuturetask的任务)是否已经执行完,如果任务已经执行完,那么在此之后提交的任务将直接执行(因为任务执行完成后,回调方法已经执行了,不会触发该监听器,所以应该在调用addListener()方法时直接运行)。该状态保证了,addListener()方法可以在任意时刻添加,不受任务执行状态的影响
- add(Runnable runnable, Executor executor)方法
将runnable和executor构造为RunableExecutorPaire对象,添加到链表中。如果executed为true,那么执行执行刚添加的监听。
源码如下:
public void add(Runnable runnable, Executor executor) {
Preconditions.checkNotNull(runnable, "Runnable was null.");
Preconditions.checkNotNull(executor, "Executor was null.");
synchronized (this) {
if (!executed) {
//构建一个节点,添加到runnables链表中,并发环境,所以要加锁
//头插法
runnables = new RunnableExecutorPair(runnable, executor, runnables);
return;
}
}
//已经执行过了,则立即执行
executeListener(runnable, executor);
}
从源码中可以看出,add()操作会在多线程环境下操作,所以在执行前需要先加锁
- execute()
执行监听器链表中的任务,首先将链表进行反转,反转的目的就是为了让监听器任务按照添加的顺序执行。(添加是通过头插法插入的,后添加的在链表头),然后执行每一个任务
源码如下:
public void execute() {
RunnableExecutorPair list;
synchronized (this) {
if (executed) {
return;
}
executed = true;
list = runnables;
runnables = null; // allow GC to free listeners even if this stays around for a while.
}
//反转链表,保证先添加的任务先执行。
RunnableExecutorPair reversedList = null;
while (list != null) {
RunnableExecutorPair tmp = list;
list = list.next;
tmp.next = reversedList;
reversedList = tmp;
}
//依次执行任务
while (reversedList != null) {
executeListener(reversedList.runnable, reversedList.executor);
reversedList = reversedList.next;
}
}
- executeListener(Runnable runnable, Executor executor)
使用executor执行runnable方法,仅此而已。
源码如下:
private static void executeListener(Runnable runnable, Executor executor) {
try {
//使用执行器执行任务
executor.execute(runnable);
} catch (RuntimeException e) {
// Log it and keep going, bad runnable and/or executor. Don't
// punish the other runnables if we're given a bad one. We only
// catch RuntimeException because we want Errors to propagate up.
log.log(Level.SEVERE, "RuntimeException while executing runnable "
+ runnable + " with executor " + executor, e);
}
}
ListenableFutureTask
其继承FutureTask(主要就是为了重写done()方法),实现ListenableFuture接口
FutureTask的具体工作原理与实现,可以查看《线程池系列一》-FutureTask原理讲解与源码剖析
- 成员变量
ExecutionList 将添加的监听器任务代理给该对象执行。
- 构造
构造方法直接调用的FutureTask类的构造方法,不再累述。Guava不建议直接调用构造方法构造对象(构造方法的方法限制为default),而是通过静态的create()方法构造,源码如下:
public static ListenableFutureTask create(Callable callable) {
return new ListenableFutureTask(callable);
}
public static ListenableFutureTask create(
Runnable runnable, @Nullable V result) {
return new ListenableFutureTask(runnable, result);
}
- addListener(Runnable listener, Executor exec)
实现ListenableFuture接口定义的方法,直接代理给executionList,调用executionList的add()方法,源码如下:
@Override
public void addListener(Runnable listener, Executor exec) {
executionList.add(listener, exec);
}
- done()
重写FutureTask的done()方法,当任务执行完成之后会调用done()方法,从而执行后续的处理逻辑,在ListenableFutreTask类中,后续的处理逻辑就是添加的监听任务。执行操作直接代理给executionList执行。源码如下:
@Override
protected void done() {
executionList.execute();
}
总结
该类与ExecutorCompletionService类功能类似,都是通过继承FutureTask,重写done()方法来完成特定的需求。不同之处在于,该类添加了后处理执行任务的操作,而这些操作都是通过代理为ExecutionList执行的,而ExecutorCompletionService只是简单的将执行完成的任务添加到了阻塞队列中。
换句话说:ExecutorCompletionService只是识别哪些任务完成了,ListenableFutureTask是在任务执行完成后添加其他处理逻辑。