Guava异步编程之ListenableFuture

前提: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。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值