前提:Java的Future虽然已经提供异步操作,但是不能直接回调。Guava对Future进行了增强,核心接口就是ListenableFuture。
一、ListenableFuture源码解析
guava对JDK的异步增强可以通过看MoreExecutor和Futures两个类的源码入手。
-
ListenableFuture继承了Future,额外新增了一个方法,listener是任务结束后的回调方法,executor是执行回调方法的执行器(通常是线程池)。guava中对future的增强就是在addListener这个方法上进行了各种各样的封装,所以addListener是核心方法。
-
jdk的FutureTask类是对Future接口的实现,guava中ListenableFutureTask继承了FutureTask并实现了ListenableFuture。
addListener方法:(无返回值)
@Override
public void addListener(Runnable listener, Executor exec) {
executionList.add(listener, exec);
}
public void add(Runnable runnable, Executor executor) {
// Fail fast on a null. We throw NPE here because the contract of Executor states that it throws
// NPE on null listener, so we propagate that contract up into the add method as well.
checkNotNull(runnable, "Runnable was null.");
checkNotNull(executor, "Executor was null.");
// Lock while we check state. We must maintain the lock while adding the new pair so that
// another thread can't run the list out from under us. We only add to the list if we have not
// yet started execution.
synchronized (this) {
if (!executed) {
//如果还没执完,放入任务队列中(单链表),等待被执行。
runnables = new RunnableExecutorPair(runnable, executor, runnables);
return;
}
}
// Execute the runnable immediately. Because of scheduling this may end up getting called before
// some of the previously added runnables, but we're OK with that. If we want to change the
// contract to guarantee ordering among runnables we'd have to modify the logic here to allow
// it.
executeListener(runnable, executor);
}
如果task已经执行完成,执行executeListener方法:(执行task的回调任务)
private static void executeListener(Runnable runnable, Executor executor) {
try {
//这里指的是执行task的所有回调函数,因为回调函数也是一种任务,这里的runnable是执行task的回调任务。
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);
}
}
如果task还没被执行,则把回调函数保存到队列中,这个队列是一个单链表,等待task执行完,再依次执行这个队列所有等待的回调函数。这个单链表的节点类:
为什么要用链表呢?因为一个task任务可以有多个回调函数。
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;
}
}
重点!实际上listener模式只是重写了FutureTask的done方法,因为在futureTask中任务执行后在finishCompletion方法会调用done方法。
@Override
protected void done() {
executionList.execute(); //执行任务队列
}
public void execute() {
// Lock while we update our state so the add method above will finish adding any listeners
// before we start to run them.
//在我们开始执行run方法前,先完成了增加listen监听。
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.
}
//将任务链表反转,存到reversedList中,因为按先来先服务的原则,先添加进来的runnable一般先执行。
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;
}
}
这里指的是某项task执行完之后,会调用它的所有回调函数列表,按照先监听先服务的原则,依次执行该task的所有回调函数。
addCallback(future, futureCallback, pool):(有返回值)
public static <V> void addCallback(
final ListenableFuture<V> future,
final FutureCallback<? super V> callback,
Executor executor) {
Preconditions.checkNotNull(callback);
future.addListener(new CallbackListener<V>(future, callback), executor);
}
底层用CallbackListener封装了一下,还是调用了addListener方法。
为什么要封装一下呢?因为要拿到task任务的返回值。
private static final class CallbackListener<V> implements Runnable {
final Future<V> future;
final FutureCallback<? super V> callback;
CallbackListener(Future<V> future, FutureCallback<? super V> callback) {
this.future = future;
this.callback = callback;
}
@Override
public void run() {
final V value;
try {
//在这里拿到任务执行完成后的返回值
value = getDone(future);
} catch (ExecutionException e) {
callback.onFailure(e.getCause());
return;
} catch (RuntimeException | Error e) {
callback.onFailure(e);
return;
}
callback.onSuccess(value);
}
}
在这个run方法里面就调用getDone等待返回值,如果有异常就调用callback.onFailure,没有异常就调动callback.onSuccess(value)。
public static <V> V getDone(Future<V> future) throws ExecutionException {
checkState(future.isDone(), "Future was expected to be done: %s", future);
return getUninterruptibly(future);
}
public static <V> V getUninterruptibly(Future<V> future) throws ExecutionException {
boolean interrupted = false;
try {
while (true) {
try {
//future.get()就是到等待任务执行结束
return future.get();
} catch (InterruptedException e) {
interrupted = true;
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
listener中的回调函数在哪执行。
private final class TrustedFutureInterruptibleTask extends InterruptibleTask<V> {
private final Callable<V> callable;
@Override
void afterRanInterruptibly(V result, Throwable error) {
if (error == null) {
TrustedListenableFutureTask.this.set(result);
} else {
setException(error);
}
}
}
在任务执行完之后,这里回调执行调用了set方法:
protected boolean set(@Nullable V value) {
Object valueToSet = value == null ? NULL : value;
if (ATOMIC_HELPER.casValue(this, null, valueToSet)) {
complete(this);
return true;
}
return false;
}
调用complete方法,执行该task的所有回调任务:
在这里插入代码片
二、ListenableFuture的使用
最简单的单线程使用:
//ListenableFutureTask通过静态create方法返回实例,还有一个重载方法,不太常用
ListenableFutureTask<String> task = ListenableFutureTask.create(new Callable<String>() {
@Override
public String call() throws Exception {
return "";
}
});
//新开一个线程,启动任务,使用Thread封装启动。
new Thread(task).start();
//增加回调方法,MoreExecutors.directExecutor()返回guava默认的Executor,执行回调方法不会新开线程,所有回调方法都在当前线程做(可能是主线程或者执行ListenableFutureTask的线程,具体可以看最后面的代码)。
//guava异步模块中参数有Executor的方法,一般还会有一个没有Executor参数的重载方法,使用的就是MoreExecutors.directExecutor()
task.addListener(new Runnable() {
@Override
public void run() {
System.out.println("done");
}
}, MoreExecutors.directExecutor()); //当前线程执行回调方法
//MoreExecutors.directExecutor()源码,execute方法就是直接运行,没有新开线程
public static Executor directExecutor() {
return DirectExecutor.INSTANCE;
}
private enum DirectExecutor implements Executor {
INSTANCE;
@Override
public void execute(Runnable command) {
command.run();
}
@Override
public String toString() {
return "MoreExecutors.directExecutor()";
}
}
线程池使用:
public class ListenableFutureTest {
public static void main(String[] args) {
testListenFuture();
}
public static void testListenFuture() {
System.out.println("主线程start");
ListeningExecutorService pool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5));
Task task1 = new Task();
task1.args = "task1";
Task task2 = new Task();
task2.args = "task2";
//往执行器里面放任务,让线程中某个线程去执行任务
ListenableFuture<String> future = pool.submit(task1);
ListenableFuture<String> future2 = pool.submit(task2);
future2.addListener(() -> System.out.println("addListener 不能带返回值"), pool);
/**
* FutureCallBack接口可以对每个任务的成功或失败单独做出响应
*/
FutureCallback<String> futureCallback = new FutureCallback<String>() {
@Override
public void onSuccess(String result) {
System.out.println("Futures.addCallback 能带返回值:" + result);
}
@Override
public void onFailure(Throwable t) {
System.out.println("出错,业务回滚或补偿");
}
};
//为任务绑定回调接口
Futures.addCallback(future, futureCallback, pool);
System.out.println("主线程end");
}
}
class Task implements Callable<String> {
String args;
@Override
public String call() throws Exception {
Thread.sleep(1000);
System.out.println("任务:" + args);
return "dong";
}
}
在java中的线程池-ThreadPoolExecutor,里面主要是使用execute去执行任务的
MoreExecutors.listeningDecorator就是包装了一下ThreadPoolExecutor,目的是为了使用ListenableFuture:
private static class ListeningDecorator extends AbstractListeningExecutorService {
private final ExecutorService delegate;
ListeningDecorator(ExecutorService delegate) {
this.delegate = (ExecutorService)Preconditions.checkNotNull(delegate);
}
public final void execute(Runnable command) {
this.delegate.execute(command);
}
}
这里的delegate就是ThreadPoolExecutor。虽然还重写了execute,不过还是直接调用ThreadPoolExecutor里面的execute。