Netty 中的异步编程 Future 和 Promise
Netty 中大量 I/O 操作都是异步执行,本篇博文来聊聊 Netty 中的异步编程。
Java Future 提供的异步模型
JDK 5 引入了 Future 模式。Future 接口是 Java 多线程 Future 模式的实现,在 java.util.concurrent包中,可以来进行异步计算。
对于异步编程,我们想要的实现是:提交一个任务,在任务执行期间提交者可以做别的事情,这个任务是在异步执行的,当任务执行完毕通知提交者任务完成获取结果。
那么在 Future 中是怎么实现的呢?我们先看接口定义:
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
我们看一个示例:
public class FutureTest { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(2); System.out.println("start"); Future<Integer> submit = executorService.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return 1; } }); Integer value = null; try { value = submit.get(); } catch (Exception e) { e.printStackTrace(); } System.out.println(value); System.out.println("end"); } }
Futrue 的使用方式是:投递一个任务到 Future 中执行,操作完之后调用 Future#get() 或者 Future#isDone() 方法判断是否执行完毕。从这个逻辑上看, Future 提供的功能是:用户线程需要主动轮询 Future 线程是否完成当前任务,如果不通过轮询是否完成而是同步等待获取则会阻塞直到执行完毕为止。所以从这里看,Future并不是真正的异步,因为它少了一个回调,充其量只能算是一个同步非阻塞模式。
executorService.submit()方法获取带返回值的 Future 结果有两种方式:
一种是通过实现 Callable接口;
第二种是中间变量返回。继承 Future 的子类: FutureTask,通过 FutureTask 返回异步结果而不是在主线程中获取(FutureTask 本质也是使用 Callable 进行创建)。
上面两种方式的代码就变为这样:
public class FutureTest2 { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(2); System.out.println("start"); //方式1 通过 executorService 提交一个异步线程 //Future<Integer> submit = executorService.submit(new NewCallableTask()); //方式2 通过 FutureTask 包装异步线程的返回,返回结果在 FutureTask 中获取而不是 在提交线程中 FutureTask<Integer> task = new FutureTask<>(new NewCallableTask()); executorService.submit(task); //-------------方式2-------------- Integer value = null; try { value = task.get(); } catch (Exception e) { e.printStackTrace(); } System.out.println(value); System.out.println("end"); } /** * 通过实现 Callable 接口 */ static class NewCallableTask implements Callable<Integer> { @Override public Integer call() throws Exception { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return 1; } } }
一般在使用线程池创建线程执行任务的时候会有两种方式,要么实现 Runnable 接口,要么实现 Callable 接口,它们的区别在于:
Callable 可以在任务结束的时候提供一个返回值,Runnable 无法提供这个功能;
Callable 的 call 方法分可以抛出异常,而 Runnable 的 run 方法不能抛出异常。
而我们的异步返回自然是使用 Callable 方式。那么 Callable 是如何实现的呢?
从 Callable 被提交的地方入手:executorService.submit(task), ExecutorService 是一个接口,他的默认实现类是:AbstractExecutorService,我们看这里的 submit()实现方式:
public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; }
可以看到将 Callable 又包装成了 RunnableFuture。而这个 RunnableFuture 就比较神奇,它同时继承了 Runnable 和 Future ,既有线程的能力又有可携带返回值的功能。
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
所以再看 submit()方法,其实是将 RunnableFuture 线程送入线程池执行,执行是一个新线程,只是这个执行的对象提供了 get()方法来获取执行结果。
那么 Callable 优势如何变为 RunnableFuture 的呢?我们看 newTaskFor(task)方法:
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); }
将 Callable 包装为 FutureTask 对象,看到这里又关联到 FutureTask ,
可以看到 FutureTask 是 RunnableFuture 的子类,这也就解释了上面的示例为什么在线程池中可以提交 FutureTask 实例。
更详细的执行过程这里就不再分析,重点剖析 Future 的实现过程,它并不是真正的异步,没有实现回调。所以在Java8 中又新增了一个真正的异步函数:CompletableFuture。
CompletableFuture 非阻塞异步编程模型
Java 8 中新增加了一个类:CompletableFuture,它提供了非常强大的 Future 的扩展功能,最重要的是实现了回调的功能。
使用示例:
public class CallableFutureTest { public static void main(String[] args) { System.out.println("start"); /** * 异步非阻塞 */ CompletableFuture.runAsync(() -> { try { Thread.sleep(3000); System.out.println("sleep done"); } catch (InterruptedException e) { e.printStackTrace(); } }); try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("done"); } }
CompletableFuture.runAsync()方法提供了异步执行无返回值任务的功能。
ExecutorService executorService = Executors.newFixedThreadPool(100); CompletableFuture future = CompletableFuture.supplyAsync(() -> { // do something return "result"; }, executorService);
CompletableFuture.supplyAsync()方法提供了异步执行有返回值任务的功能。
CompletableFuture源码中有四个静态方法用来执行异步任务:
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier){..} public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor){..} public static CompletableFuture<Void> runAsync(Runnable runnable){..} public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor){..}
前面两个可以看到是带返回值的方法,后面两个是不带返回值的方法。同时支持传入自定义的线程池,如果不传入线程池的话默认是使用 ForkJoinPool.commonPool()作为它的线程池执行异步代码。
合并两个异步任务
如果有两个任务需要异步执行,且后面需要对这两个任务的结果进行合并处理,CompletableFuture 也支持这种处理:
ExecutorService executorService = Executors.newFixedThreadPool(100); CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> { return "Task1"; }, executorService); CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> { return "Task2"; }, executorService); CompletableFuture<String> future = future1.thenCombineAsync(future2, (task1, task2) -> { return task1 + task2; // return "Task1Task2" String });
通过 CompletableFuture.thenCombineAsync()方法获取两个任务的结果然后进行相应的操作。
下一个依赖上一个的结果
如果第二个任务依赖第一个任务的结果:
ExecutorService executorService = Executors.newFixedThreadPool(100); CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> { return "Task1"; }, executorService); CompletableFuture<String> future = future1.thenComposeAsync(task1 -> { return CompletableFuture.supplyAsync(() -> { return task1 + "Task2"; // return "Task1Task2" String }); }, executorService);
CompletableFuture.thenComposeAsync()支持将第一个任务的结果传入第二个任务中。
常用 API 介绍
拿到上一个任务的结果做后续操作,上一个任务完成后的动作
- 拿到上一个任务的结果做后续操作,上一个任务完成后的动作
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action) public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action) public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor) public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)
上面四个方法表示在当前阶段任务完成之后下一步要做什么。whenComplete 表示在当前线程内继续做下一步,带 Async 后缀的表示使用新线程去执行。
- 拿到上一个任务的结果做后续操作,使用 handler 来处理逻辑,可以返回与第一阶段处理的返回类型不一样的返回类型。
public <U> CompletableFuture<U> handle(BiFunction<? super T,Throwable,? extends U> fn) public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn) public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn, Executor executor)
- 拿到上一个任务的结果做后续操作, thenApply方法
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn) public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
- 拿到上一个任务的结果做后续操作,可以不返回任何值,thenAccept方法
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)
看这里的示例:
CompletableFuture.supplyAsync(() -> { return "result"; }).thenAccept(r -> { System.out.println(r); }).thenAccept(r -> { System.out.println(r); });
执行完毕是不会返回任何值的。
CompletableFuture 的特性提现在执行完 runAsync 或者 supplyAsync 之后的操作上。CompletableFuture 能够将回调放到与任务不同的线程中执行,也能将回调作为继续执行的同步函数,在与任务相同的线程中执行。它避免了传统回调最大的问题,那就是能够将控制流分离到不同的事件处理器中。
另外当你依赖 CompletableFuture 的计算结果才能进行下一步的时候,无需手动判断当前计算是否完成,可以通过 CompletableFuture 的事件监听自动去完成。
Netty 中的异步编程
说 Netty 中的异步编程之前先说一个异步编程模型:Future/Promise异步模型。
future和promise起源于函数式编程和相关范例(如逻辑编程 ),目的是将值(future)与其计算方式(promise)分离,从而允许更灵活地进行计算,特别是通过并行化。
Future 表示目标计算的返回值,Promise 表示计算的方式,这个模型将返回结果和计算逻辑分离,目的是为了让计算逻辑不影响返回结果,从而抽象出一套异步编程模型。那计算逻辑如何与结果关联呢?它们之间的纽带就是 callback。
引用自:https://zh.wikipedia.org/wiki/Future%E4%B8%8Epromise
在 Netty 中的异步编程就是基于该模型来实现。Netty 中非常多的异步调用,最简单的例子就是我们 Server 和 Client 端启动的例子:
Server:
Client:
Netty 中使用了一个 ChannelFuture 来实现异步操作,看似与 Java 中的 Future 相似,我们看一下代码:
public interface ChannelFuture extends Future<Void> { }
这里 ChannelFuture 继承了一个 Future,这是 Java 中的 Future 吗?跟下去发现并不是 JDK 的,而是 Netty 自己实现的。该类位于:io.netty.util.concurrent包中:
public interface Future<V> extends java.util.concurrent.Future<V> { // 只有IO操作完成时才返回true boolean isSuccess(); // 只有当cancel(boolean)成功取消时才返回true boolean isCancellable(); // IO操作发生异常时,返回导致IO操作以此的原因,如果没有异常,返回null Throwable cause(); // 向Future添加事件,future完成时,会执行这些事件,如果add时future已经完成,会立即执行监听事件 Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener); Future<V> addListeners(GenericFutureListener<? extends Future<? super V>>... listeners); // 移除监听事件,future完成时,不会触发 Future<V> removeListener(GenericFutureListener<? extends Future<? super V>> listener); Future<V> removeListeners(GenericFutureListener<? extends Future<? super V>>... listeners); // 等待future done Future<V> sync() throws InterruptedException; // 等待future done,不可打断 Future<V> syncUninterruptibly(); // 等待future完成 Future<V> await() throws InterruptedException; // 等待future 完成,不可打断 Future<V> awaitUninterruptibly(); boolean await(long timeout, TimeUnit unit) throws InterruptedException; boolean await(long timeoutMillis) throws InterruptedException; boolean awaitUninterruptibly(long timeout, TimeUnit unit); boolean awaitUninterruptibly(long timeoutMillis); // 立刻获得结果,如果没有完成,返回null V getNow(); // 如果成功取消,future会失败,导致CancellationException @Override boolean cancel(boolean mayInterruptIfRunning); }
Netty 自己实现的 Future 继承了 JDK 的 Future,新增了 sync() 和await() 用于阻塞等待,还加了 Listeners,只要任务结束去回调 Listener 就可以了,那么我们就不一定要主动调用 isDone()来获取状态,或通过 get()阻塞方法来获取值。
Netty的 Future 与 Java 的 Future 虽然类名相同,但功能上略有不同,Netty 中引入了 Promise 机制。在 Java 的 Future 中,业务逻辑为一个 Callable 或 Runnable 实现类,该类的 call()或 run()执行完毕意味着业务逻辑的完结,在 Promise 机制中,可以在业务逻辑中人工设置业务逻辑的成功与失败,这样更加方便的监控自己的业务逻辑。
public interface Promise<V> extends Future<V> { // 设置future执行结果为成功 Promise<V> setSuccess(V result); // 尝试设置future执行结果为成功,返回是否设置成功 boolean trySuccess(V result); // 设置失败 Promise<V> setFailure(Throwable cause); // 尝试设置future执行结果为失败,返回是否设置成功 boolean tryFailure(Throwable cause); // 设置为不能取消 boolean setUncancellable(); // 源码中,以下为覆盖了Future的方法,例如; Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener); @Override Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener); }
Promise 接口继承自 Future 接口,重点添加了上述几个方法,可以人工设置 future 的执行成功与失败,并通知所有监听的 listener。
从 Future 和 Promise 提供的方法来看,Future 都是 get 类型的方法,主要用来判断当前任务的状态。而 Promise 中是 set 类型的方法,主要来对任务的状态来进行操作。这里就体现出来将 结果和操作过程分离的设计。
Promise 实现类是DefaultPromise类,该类十分重要,Future 的 listener 机制也是由它实现的,所以我们先来分析一下该类。先来看一下它的重要属性:
// 可以嵌套的Listener的最大层数,可见最大值为8 private static final int MAX_LISTENER_STACK_DEPTH = Math.min(8, SystemPropertyUtil.getInt("io.netty.defaultPromise.maxListenerStackDepth", 8)); // result字段由使用RESULT_UPDATER更新 @SuppressWarnings("rawtypes") private static final AtomicReferenceFieldUpdater<DefaultPromise, Object> RESULT_UPDATER; private static final Signal SUCCESS = Signal.valueOf(DefaultPromise.class, "SUCCESS"); // 异步操作不可取消 private static final Signal UNCANCELLABLE = Signal.valueOf(DefaultPromise.class, "UNCANCELLABLE"); // 异步操作失败时保存异常原因 private static final CauseHolder CANCELLATION_CAUSE_HOLDER = new CauseHolder(ThrowableUtil.unknownStackTrace( new CancellationException(), DefaultPromise.class, "cancel(...)"));
在解析Netty源码时,在解析NioEventLoop 创建过程中,有一段这样的代码。
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) { if (nThreads <= 0) { throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads)); } if (executor == null) { executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()); } children = new EventExecutor[nThreads]; for (int i = 0; i < nThreads; i ++) { boolean success = false; try { children[i] = newChild(executor, args); success = true; } catch (Exception e) { // TODO: Think about if this is a good exception type throw new IllegalStateException("failed to create a child event loop", e); } finally { if (!success) { for (int j = 0; j < i; j ++) { children[j].shutdownGracefully(); } for (int j = 0; j < i; j ++) { EventExecutor e = children[j]; try { while (!e.isTerminated()) { e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS); } } catch (InterruptedException interrupted) { // Let the caller handle the interruption. Thread.currentThread().interrupt(); break; } } } } } chooser = chooserFactory.newChooser(children); final FutureListener<Object> terminationListener = new FutureListener<Object>() { @Override public void operationComplete(Future<Object> future) throws Exception { if (terminatedChildren.incrementAndGet() == children.length) { terminationFuture.setSuccess(null); } } }; for (EventExecutor e: children) { e.terminationFuture().addListener(terminationListener); } Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length); Collections.addAll(childrenSet, children); readonlyChildren = Collections.unmodifiableSet(childrenSet); }
今天就围绕着加粗代码进行分析 。
首先来看children是什么?从前面的代码children = new EventExecutor[nThreads],可以看出children是EventExecutor数组。
而EventExecutor和NioEventLoop是什么关系呢?请看下图 。
接着分析chooser = chooserFactory.newChooser(children);这一行代码,chooserFactory的默认值为DefaultEventExecutorChooserFactory类,在newChooser中使用不同的策略来获取NioEventLoop。
public final class DefaultEventExecutorChooserFactory implements EventExecutorChooserFactory { public static final DefaultEventExecutorChooserFactory INSTANCE = new DefaultEventExecutorChooserFactory(); private DefaultEventExecutorChooserFactory() { } @Override public EventExecutorChooser newChooser(EventExecutor[] executors) { // 判断数组的长度是否是2的幂次方 if (isPowerOfTwo(executors.length)) { // 如果是2的倍数,则使用PowerOfTwoEventExecutorChooser // 选择器策略 return new PowerOfTwoEventExecutorChooser(executors); } else { // 如果不是2的幂次方,则使用GenericEventExecutorChooser选择器策略 return new GenericEventExecutorChooser(executors); } } private static boolean isPowerOfTwo(int val) { return (val & -val) == val; } private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser { private final AtomicInteger idx = new AtomicInteger(); private final EventExecutor[] executors; PowerOfTwoEventExecutorChooser(EventExecutor[] executors) { this.executors = executors; } @Override public EventExecutor next() { return executors[idx.getAndIncrement() & executors.length - 1]; } } private static final class GenericEventExecutorChooser implements EventExecutorChooser { private final AtomicInteger idx = new AtomicInteger(); private final EventExecutor[] executors; GenericEventExecutorChooser(EventExecutor[] executors) { this.executors = executors; } @Override public EventExecutor next() { return executors[Math.abs(idx.getAndIncrement() % executors.length)]; } } }
DefaultEventExecutorChooserFactory类中两种策略执行结果一样,都是采用轮询方式,如果数组长度是2的幂次方,则用求&的方式来计算要取数组下标的索引值,如果不是2的幂次方,则用求余的方式来计算数组下标的索引值,为什么要区分一下呢? 因为求 & 的方式比求余的方式效率更高,如果数组长度不是2的倍数,则不能使用求 & 的方式 。其实像HashMap,MpscChunkedArrayQueue源码中都用到了求&的方式,如果阅读过相关源码的小伙伴肯定比较熟悉了,但如果还有小伙伴不明白,我们可以来看一个例子,假如数组的长度为14。
当idx.getAndIncrement()的值为16和18时,和17和19时,计算出索引位置为0,1,则存在数组索引冲突,因此不符合轮询的条件。而在DefaultEventExecutorChooserFactory中使用 PowerOfTwoEventExecutorChooser和GenericEventExecutorChooser两种策略,主要是为了提高轮询效率 。
这段代码先放一边。
final FutureListener<Object> terminationListener = new FutureListener<Object>() { @Override public void operationComplete(Future<Object> future) throws Exception { if (terminatedChildren.incrementAndGet() == children.length) { terminationFuture.setSuccess(null); } } };
先看后面的
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
这一段代码 。
因为NioEventLoop继承SingleThreadEventExecutor类,而SingleThreadEventExecutor类中实现了terminationFuture()方法。
private final Promise<?> terminationFuture = new DefaultPromise<Void>(GlobalEventExecutor.INSTANCE); public Future<?> terminationFuture() { return terminationFuture; }
从上面方法中得知,调用e.terminationFuture().addListener(terminationListener);这一行代码,实际上调用的是DefaultPromise的addListener()方法,进入addListener()方法 。
public Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) { checkNotNull(listener, "listener"); // 使用同步的方式,避免并发 synchronized (this) { addListener0(listener); } if (isDone()) { notifyListeners(); } return this; } private void addListener0(GenericFutureListener<? extends Future<? super V>> listener) { if (listeners == null) { listeners = listener; } else if (listeners instanceof DefaultFutureListeners) { ((DefaultFutureListeners) listeners).add(listener); } else { listeners = new DefaultFutureListeners((GenericFutureListener<?>) listeners, listener); } }
光从上面两个方法,看不出什么东西,好像也看不清DefaultPromise的意图。 进入DefaultFutureListeners类中 。
Netty 中 DefalutPromise 是一个非常常用的类,这是 Promise 实现的基础。DefaultChannelPromise DefalutPromise 的子类,加入了 channel 这个属性。
Promise 目前支持两种类型的监听器:
GenericFutureListener:支持泛型的 Future ;
GenericProgressiveFutureListener:它是GenericFutureListener的子类,支持进度表示和支持泛型的Future 监听器(有些场景需要多个步骤实现,类似于进度条那样)。
为了让 Promise 支持多个监听器,Netty 添加了一个默认修饰符修饰的DefaultFutureListeners类用于保存监听器实例数组:
final class DefaultFutureListeners { private GenericFutureListener<? extends Future<?>>[] listeners; private int size; private int progressiveSize; // the number of progressive listeners // 这个构造相对特别,是为了让Promise中的listeners(Object类型) // 实例由单个GenericFutureListener实例转换为DefaultFutureListeners类型 DefaultFutureListeners( GenericFutureListener<? extends Future<?>> first, GenericFutureListener<? extends Future<?>> second) { listeners = new GenericFutureListener[2]; listeners[0] = first; listeners[1] = second; size = 2; if (first instanceof GenericProgressiveFutureListener) { progressiveSize ++; } if (second instanceof GenericProgressiveFutureListener) { progressiveSize ++; } } public void add(GenericFutureListener<? extends Future<?>> l) { GenericFutureListener<? extends Future<?>>[] listeners = this.listeners; final int size = this.size; if (size == listeners.length) { // 注意这里,每次扩容数组长度是原来的2倍 this.listeners = listeners = Arrays.copyOf(listeners, size << 1); } // 把当前的GenericFutureListener加入数组中 listeners[size] = l; // 监听器总数量加1 this.size = size + 1; // 如果为GenericProgressiveFutureListener,则带进度指示的监听器总数量加1 if (l instanceof GenericProgressiveFutureListener) { progressiveSize ++; } } public void remove(GenericFutureListener<? extends Future<?>> l) { final GenericFutureListener<? extends Future<?>>[] listeners = this.listeners; int size = this.size; for (int i = 0; i < size; i ++) { if (listeners[i] == l) { // 计算需要需要移动的监听器的下标 int listenersToMove = size - i - 1; if (listenersToMove > 0) { // listenersToMove后面的元素全部移动到数组的前端 System.arraycopy(listeners, i + 1, listeners, i, listenersToMove); } listeners[-- size] = null; this.size = size; // 如果监听器是GenericProgressiveFutureListener,则带进度指示的监听器总数量减1 if (l instanceof GenericProgressiveFutureListener) { progressiveSize --; } return; } } } // 返回监听器实例数组 public GenericFutureListener<? extends Future<?>>[] listeners() { return listeners; } // 返回监听器总数量 public int size() { return size; } // 返回带进度指示的监听器总数量 public int progressiveSize() { return progressiveSize; } }
从DefaultFutureListeners代码中可以看出DefaultFutureListeners和GenericFutureListener的关系,在DefaultFutureListeners实例中有一个GenericFutureListener数组。从DefaultFutureListeners的构造方法中可以看出, 默认初始化GenericFutureListener数组长度为2,因此再来理解addListener0()方法的原理就很简单了。
private void addListener0(GenericFutureListener<? extends Future<? super V>> listener) { if (listeners == null) { listeners = listener; } else if (listeners instanceof DefaultFutureListeners) { ((DefaultFutureListeners) listeners).add(listener); } else { listeners = new DefaultFutureListeners((GenericFutureListener<?>) listeners, listener); } }
如果向DefaultPromise添加一个listener,则用DefaultPromise的listener属性存储即可,如果向DefaultPromise添加两个listener,则用DefaultFutureListeners对象存储,但DefaultFutureListeners中listeners数组初始化长度为2,刚好存储添加的两个listener,如果向DefaultPromise添加的listener超过两个,则需要扩容DefaultFutureListeners的listener数组来存储了,扩容方式如上加粗代码,listener数组扩容为原来两倍,Netty这样做的原因,一方面是提升性能,另一方面为了节省存储空间吧。
既然listener已经存储好了,什么时候调用operationComplete()方法呢?接下来看DefaultPromise的setSuccess()和setFailure()方法。
当然,我们挑选setSuccess()看即可。 setFailure()方法和setSuccess()方法原理一样。
private boolean setSuccess0(V result) { return setValue0(result == null ? SUCCESS : result); } private boolean setValue0(Object objResult) { if (RESULT_UPDATER.compareAndSet(this, null, objResult) || RESULT_UPDATER.compareAndSet(this, UNCANCELLABLE, objResult)) { if (checkNotifyWaiters()) { notifyListeners(); } return true; } return false; }
看源码多的小伙伴肯定一眼就知道这里又用到了AQS。
private static final AtomicReferenceFieldUpdater<DefaultPromise, Object> RESULT_UPDATER = AtomicReferenceFieldUpdater.newUpdater(DefaultPromise.class, Object.class, "result"); private volatile Object result;
设置result的值时,如果其值当前为为null或UNCANCELLABLE,是可以被设置成功的,默认情况下,result值为null。那什么情况下会为UNCANCELLABLE呢? 请看DefaultPromise中的setUncancellable()方法 。
public boolean setUncancellable() { if (RESULT_UPDATER.compareAndSet(this, null, UNCANCELLABLE)) { return true; } Object result = this.result; return !isDone0(result) || !isCancelled0(result); }
将result的值设置为UNCANCELLABLE后有什么影响呢? 请看cancel()方法 。
public boolean cancel(boolean mayInterruptIfRunning) { if (RESULT_UPDATER.get(this) == null && RESULT_UPDATER.compareAndSet(this, null, new CauseHolder(new CancellationException()))) { if (checkNotifyWaiters()) { notifyListeners(); } return true; } return false; }
当result的值必须为空时,cancel()方法才能调用成功,接着调用notifyListeners()方法,最终调用才Listener的operationComplete()方法 。如果 result的值被设置为UNCANCELLABLE后,cancel()方法将不会调用成功。 言归正传,继续看setValue0()的checkNotifyWaiters()方法 。
private synchronized boolean checkNotifyWaiters() { if (waiters > 0) { notifyAll(); } return listeners != null; }
如果waiters大于0,则调用notifyAll()方法。 notifyAll()方法是Object类中的方法,就是唤醒所有等待线程的意思,接下来看waiters值何时修改的呢? 请看下图。
在DefaultPromise类中有两个方法,incWaiters()和decWaiters()方法,这两个方法对waiters值做了修改,何时调用waiters的值呢?能举个例子不? 当 ChannelFuture cf = bootstrap.bind(9000).sync(); 的sync()方法调用时。
也就是DefaultPromise的sync()方法被调用时。 waiters的值会++,同时会触发当前线程进入wait()。
public Promise<V> sync() throws InterruptedException { await(); rethrowIfFailed(); return this; } public Promise<V> await() throws InterruptedException { // 异步操作已经完成,直接返回 if (isDone()) { return this; } if (Thread.interrupted()) { throw new InterruptedException(toString()); } // 死锁检测 checkDeadLock(); // 同步使修改waiters的线程只有一个 synchronized (this) { while (!isDone()) { // 等待直到异步操作完成 incWaiters(); // ++waiters; try { wait(); // JDK方法 } finally { decWaiters(); // --waiters } } } return this; }
因此在 setValue0() 方法的notifyListeners调用之前,先要唤醒正在等待的线程,接着调用notifyListeners()方法 。接下来进入notifyListeners()方法 。
private void notifyListeners() { EventExecutor executor = executor(); // 当前EventLoop线程需要检查listener嵌套 if (executor.inEventLoop()) { final InternalThreadLocalMap threadLocals = InternalThreadLocalMap.get(); // 这里是当前listener的嵌套层数 final int stackDepth = threadLocals.futureListenerStackDepth(); if (stackDepth < MAX_LISTENER_STACK_DEPTH) { threadLocals.setFutureListenerStackDepth(stackDepth + 1); try { notifyListenersNow(); } finally { threadLocals.setFutureListenerStackDepth(stackDepth); } return; } } // 外部线程直接提交给新线程执行 safeExecute(executor, new Runnable() { @Override public void run() { notifyListenersNow(); } }); }
大家可能会想,上述这段代码什么意思?先来看executor()方法。
protected EventExecutor executor() { return executor; }
executor的值为GlobalEventExecutor。
因此inEventLoop()方法实际上是调用GlobalEventExecutor的inEventLoop()方法 。
public boolean inEventLoop() { return inEventLoop(Thread.currentThread()); } public boolean inEventLoop(Thread thread) { return thread == this.thread; }
判断的依据就是thread是否是当前线程,那GlobalEventExecutor中的thread又在什么时候初始化的呢? 看DefaultPromise的safeExecute()方法 。
final BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>(); private final AtomicBoolean started = new AtomicBoolean(); private static void safeExecute(EventExecutor executor, Runnable task) { try { executor.execute(task); } catch (Throwable t) { rejectedExecutionLogger.error("Failed to submit a listener notification task. Event loop shut down?", t); } }
因为知道executor为GlobalEventExecutor,因此这里的execute方法实际为GlobalEventExecutor中的方法。
public void execute(Runnable task) { if (task == null) { throw new NullPointerException("task"); } addTask(task); if (!inEventLoop()) { startThread(); } } private void addTask(Runnable task) { if (task == null) { throw new NullPointerException("task"); } taskQueue.add(task); } private void startThread() { // 保证同一时间,只有一个线程在执行消费任务操作 if (started.compareAndSet(false, true)) { final Thread t = threadFactory.newThread(taskRunner); // Set to null to ensure we not create classloader leaks by holds a strong reference to the inherited // classloader. // See: // - https://github.com/netty/netty/issues/7290 // - https://bugs.openjdk.java.net/browse/JDK-7008595 AccessController.doPrivileged(new PrivilegedAction<Void>() { @Override public Void run() { t.setContextClassLoader(null); return null; } }); // Set the thread before starting it as otherwise inEventLoop() may return false and so produce // an assert error. // See https://github.com/netty/netty/issues/4357 thread = t; t.start(); } }
大家发现没有,调用GlobalEventExecutor的execute方法,并没有直接执行任务的run()方法,而是将任务添加到taskQueue队列中,接着调用threadFactory的newThread()方法,创建一个新线程,新线程中执行TaskRunner任务,在TaskRunner的run()方法中,从队列中取出任务,并执行其run()方法 ,看 TaskRunner的源码 。
final class TaskRunner implements Runnable { @Override public void run() { for (;;) { Runnable task = takeTask(); if (task != null) { try { // 执行任务的run()方法 task.run(); } catch (Throwable t) { logger.warn("Unexpected exception from the global event executor: ", t); } if (task != quietPeriodTask) { continue; } } Queue<ScheduledFutureTask<?>> scheduledTaskQueue = GlobalEventExecutor.this.scheduledTaskQueue; // Terminate if there is no task in the queue (except the noop task). if (taskQueue.isEmpty() && (scheduledTaskQueue == null || scheduledTaskQueue.size() == 1)) { // Mark the current thread as stopped. // The following CAS must always success and must be uncontended, // because only one thread should be running at the same time. boolean stopped = started.compareAndSet(true, false); assert stopped; // Check if there are pending entries added by execute() or schedule*() while we do CAS above. if (taskQueue.isEmpty() && (scheduledTaskQueue == null || scheduledTaskQueue.size() == 1)) { // A) No new task was added and thus there's nothing to handle // -> safe to terminate because there's nothing left to do // B) A new thread started and handled all the new tasks. // -> safe to terminate the new thread will take care the rest break; } // There are pending tasks added again. if (!started.compareAndSet(false, true)) { // startThread() started a new thread and set 'started' to true. // -> terminate this thread so that the new thread reads from taskQueue exclusively. break; } // New tasks were added, but this worker was faster to set 'started' to true. // i.e. a new worker thread was not started by startThread(). // -> keep this thread alive to handle the newly added entries. } } } }
上面这段代码还是令人费解的,先来看takeTask()方法 。
public Runnable takeTask() { BlockingQueue<Runnable> taskQueue = this.taskQueue; for (;;) { ScheduledFutureTask<?> scheduledTask = peekScheduledTask(); if (scheduledTask == null) { Runnable task = null; try { task = taskQueue.take(); } catch (InterruptedException e) { // Ignore } return task; } else { // 第一种情况,如果ScheduledFutureTask创建到代码执行到这一行为止,时间没有超过deadlineNanos, // 则delayNanos = deadlineNanos - (currentTime - createTime) // 第二种情况,如果ScheduledFutureTask是被移除了,并重新加入到scheduledTaskQueue中 // 则 delayNanos = deadlineNanos - (currentTime - 加入到scheduledTaskQueue中的时间) long delayNanos = scheduledTask.delayNanos(); Runnable task; if (delayNanos > 0) { try { task = taskQueue.poll(delayNanos, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { // Waken up. return null; } } else { task = taskQueue.poll(); } if (task == null) { fetchFromScheduledTaskQueue(); task = taskQueue.poll(); } if (task != null) { return task; } } } } final ScheduledFutureTask<?> peekScheduledTask() { Queue<ScheduledFutureTask<?>> scheduledTaskQueue = this.scheduledTaskQueue; if (scheduledTaskQueue == null) { return null; } return scheduledTaskQueue.peek(); }
上面 long delayNanos = scheduledTask.delayNanos();这一行代码,先看delayNanos()的计算规则。
ScheduledFutureTask{ public long delayNanos() { return Math.max(0, deadlineNanos() - nanoTime()); } public long deadlineNanos() { return deadlineNanos; } // ScheduledFutureTask实例创建时间 private static final long START_TIME = System.nanoTime(); // 当前时间减去创建时间 public static long nanoTime() { return System.nanoTime() - START_TIME; } }
那scheduledTask的值是何时初始化的呢?
从上图中得知, deadlineNanos的值来源于ScheduledFutureTask的deadlineNanos()方法,且在GlobalEventExecutor中传入的值为1秒。 而deadlineNanos的值与ScheduledFutureTask START_TIME静态变量初始化,以及ScheduledFutureTask实例化时间有关。 他们之间的关系为。
假如SCHEDULE_QUIET_PERIOD_INTERVAL 的值为1秒,如果ScheduledFutureTask的START_TIME变量初始化到ScheduledFutureTask实例化花了0.5秒 ,则deadlineNanos的值为1.5秒。
其实 netty 这么做原因就是要保证 delayNanos 有充足的1秒时间,排除掉ScheduledFutureTask初始化的时间,可能是一种更加精准的考虑吧。 在addTask() 方法中将任务加入到taskQueue中。
根据上图第一种情况当执行到long delayNanos = scheduledTask.delayNanos(); 这一行代码的时间小于deadlineNanos时,和第二种情况,当执行到long delayNanos = scheduledTask.delayNanos(); 大于deadlineNanos时。
对应执行代码行如上图所示 。 Netty这么做的目的可能是。
addTask()方法,将任务添加到taskQueue队列中,和takeTask()方法从队列中获取任务,两者之间是异步的。 可能会存在taskQueue.poll()方法比taskQueue.add()方法先执行,因此这里设置一个deadlineNanos参数,来等待任务吧。
接下来看fetchFromScheduledTaskQueue()这个方法 。
public void fetchFromScheduledTaskQueue() { long nanoTime = AbstractScheduledEventExecutor.nanoTime(); Runnable scheduledTask = pollScheduledTask(nanoTime); while (scheduledTask != null) { taskQueue.add(scheduledTask); scheduledTask = pollScheduledTask(nanoTime); } } protected final Runnable pollScheduledTask(long nanoTime) { assert inEventLoop(); Queue<ScheduledFutureTask<?>> scheduledTaskQueue = this.scheduledTaskQueue; ScheduledFutureTask<?> scheduledTask = scheduledTaskQueue == null ? null : scheduledTaskQueue.peek(); if (scheduledTask == null) { return null; } // 如果超过了deadlineNanos时间还没有从taskQueue队列中获取任务 // 则将scheduledTask任务添加到taskQueue中 if (scheduledTask.deadlineNanos() <= nanoTime) { scheduledTaskQueue.remove(); return scheduledTask; } return null; }
如果taskQueue中已经没有元素,则将scheduledTaskQueue队列中的元素添加到taskQueue队列中。
上述 pollScheduledTask()方法有点像优先级队列,因为peek()方法取队头元素,但元素并没有弹出队列,如果时间还未达到,是不能从优先级队列中取出元素的,因此在pollScheduledTask()方法中,加了一行scheduledTask.deadlineNanos() <= nanoTime作为条件,如果时间还未达到,返回的是空,只有时间达到了,才能从scheduledTaskQueue中取到元素。
在fetchFromScheduledTaskQueue()方法中写了一个while()循环,只要scheduledTaskQueue中有元素满足条件,则都加到taskQueue队列中,请看下面例子。
自己写了一个MyGlobalEventExecutor继承GlobalEventExecutor,当然在netty源码中GlobalEventExecutor是final类型的,为了方便测试,我修改了netty源码相关内容,重新编译了netty源码,因此在我的代码中是可以实现的。 在GlobalEventExecutor的构造函数中, 向scheduledTaskQueue中添加了一个quietPeriodTaskNew任务。在GlobalEventExecutor构造函数中也添加了一个quietPeriodTask任务。因此scheduledTaskQueue中已经有两个ScheduledFutureTask任务。
测试例子
public static void main(String[] args) throws Exception { GlobalEventExecutor INSTANCE = new MyGlobalEventExecutor(); Promise<String> promise = new DefaultPromise<>(INSTANCE); promise.addListener(new GenericFutureListener<Future<? super String>>() { @Override public void operationComplete(Future<? super String> future) throws Exception { Object s = future.get(); System.out.println(new Date() + "listner1---promise的future返回值:" + s); } }); promise.setSuccess(Thread.currentThread().getName() + " promise set "); }
打断点查看循环情况
我觉得fetchFromScheduledTaskQueue() 方法的while()循环应该是考虑到这种情况吧,如果获取到正常任务,则调用其run()方法,最终调用的就是notifyListenersNow()方法。
关于notifyListenersNow()方法的实现逻辑,后面来分析。先分析,如果取到的任务是ScheduledFutureTask,其run()方法的实现逻辑。
public void run() { assert executor().inEventLoop(); try { if (periodNanos == 0) { if (setUncancellableInternal()) { V result = task.call(); setSuccessInternal(result); } } else { // check if is done as it may was cancelled if (!isCancelled()) { task.call(); if (!executor().isShutdown()) { long p = periodNanos; if (p > 0) { deadlineNanos += p; } else { deadlineNanos = nanoTime() - p; } if (!isCancelled()) { // scheduledTaskQueue can never be null as we lazy init it before submit the task! Queue<ScheduledFutureTask<?>> scheduledTaskQueue = ((AbstractScheduledEventExecutor) executor()).scheduledTaskQueue; assert scheduledTaskQueue != null; scheduledTaskQueue.add(this); } } } } } catch (Throwable cause) { setFailureInternal(cause); } }
正常情况下,executor()并没有被关闭也没有被取消,因此isCancelled() && executor().isShutdown() 都为false,则只和periodNanos相关了,如果periodNanos = 0 ,则ScheduledFutureTask将从scheduledTaskQueue中移除掉,如果 periodNanos 不等于0 , 则重新计算 deadlineNanos 的值,并将原ScheduledFutureTask从taskQueue中移动到scheduledTaskQueue中。 当然deadlineNanos的计算也分两种情况,
第一种情况,当periodNanos 大于0 ,假设为5秒,ScheduledFutureTask 初始化开始时间为 18:00:00,初始化耗时为1秒,ScheduledFutureTask对象创建时间为18:00:01秒,delay 时间为1秒(也就是GlobalEventExecutor 的 SCHEDULE_QUIET_PERIOD_INTERVAL参数默认为1 秒),则deadlineNanos = 2 秒,如果periodNanos为5秒,则第一次调用ScheduledFutureTask的run方法后,deadlineNanos = 7 秒,如果在18:00:07秒之前调用GlobalEventExecutor的takeTask()方法 。 则会走下图中的1 的情况,如果在18:00:07秒之后调用takeTask()方法,则会走下图中2的情况。
第二种情况,当periodNanos 小于0 ,假设为-5秒,ScheduledFutureTask 初始化开始时间为 18:00:00,初始化耗时为1秒,ScheduledFutureTask对象创建时间为18:00:01秒,delay 时间为1秒(也就是GlobalEventExecutor 的 SCHEDULE_QUIET_PERIOD_INTERVAL参数默认为1 秒),则deadlineNanos = 2 秒,如果执行到下图中的
deadlineNanos = nanoTime() - p;这一行代码的时间为18:00:08秒,则deadlineNanos = 8 + 5 = 13,也就是说在18:00:13秒之前,会执行takeTask方法图的第一种情况,否则会执行takeTask方法图第二种情况。 这可能是代码上的含义,从字面意义上来说呢?periodNanos不为0的情况,如果当前线程从队列中取不到任务,当前线程则会退出循环,当其他线程向taskQueue中添加任务,则又会调用TaskRunner的run()方法,此时又会调用taskTask()方法从taskQueue中取元素,如果从队列中获取元素的时间小于deadlineNanos,则会调用调用taskTask()方法的这一行代码task = taskQueue.poll(delayNanos, TimeUnit.NANOSECONDS); 方法来获取队列中的任务,如果大于deadlineNanos,则会调用taskTask()方法的这一行代码task = taskQueue.poll();来获取队列中的任务 。
第三种情况,如果periodNanos等于0时,会出现什么情况呢?
为什么必须将父类中构造函数GlobalEventExecutor添加的scheduledTaskQueue().add(quietPeriodTask);这一行代码移除掉呢?如果不移除掉。
当scheduledTaskQueue中只有我们手动添加的quietPeriodTaskNew时,而quietPeriodTaskNew的periodNanos为0。
为什么呢? 因为在scheduledTask的run()方法中,走了下面这一行代码。
并没有将ScheduledFutureTask添加到scheduledTaskQueue中,则peekScheduledTask()方法中获取到的scheduledTaskQueue队列中的任务为空,因此进入一直等待。 要测试效果也很容易。
分析完safeExecute()方法后,再来分析notifyListeners()方法 。
在notifyListeners()方法中,上面加红框代码,是什么意思呢? 依然还是来看一个例子。
在GenericFutureListener的operationComplete()方法中,又加入新的Listener,此时新加入的Listener的operationComplete方法依然被调用。
InternalThreadLocalMap在之前的博客 Netty源码性能分析 - ThreadLocal PK FastThreadLocal 分析过,也可以直接将其当成ThreadLocal来看,但有一点还是在疑问? 想了很久没有想明白 。
public class TestPromise2 { public static void main(String[] args) throws Exception { NioEventLoopGroup loopGroup = new NioEventLoopGroup(); EventLoop next = loopGroup.next(); Promise<String> promise = (Promise) next.terminationFuture(); promise.addListener(new GenericFutureListener<Future<? super String>>() { @Override public void operationComplete(Future<? super String> future) throws Exception { Object s = future.get(); System.out.println(new Date() + "listner1---promise的future返回值:" + s); ((DefaultPromise) future).addListener(new GenericFutureListener<Future<? super String>>() { @Override public void operationComplete(Future<? super String> future) throws Exception { Object s = future.get(); final InternalThreadLocalMap threadLocals = InternalThreadLocalMap.get(); final int stackDepth = threadLocals.futureListenerStackDepth(); System.out.println(new Date() + "listner1 inner ---promise的future返回值:" + s + ", stackDepth = " + stackDepth); ((DefaultPromise) future).addListener(new GenericFutureListener<Future<? super String>>() { @Override public void operationComplete(Future<? super String> future) throws Exception { final InternalThreadLocalMap threadLocals = InternalThreadLocalMap.get(); final int stackDepth = threadLocals.futureListenerStackDepth(); Object s = future.get(); System.out.println(new Date() + "listner1 inner inner ---promise的future返回值:" + s + ", stackDepth ="+stackDepth); } }); } }); } }); promise.setSuccess("promise set ");//设置返回结果 并且通知所有Listeners执行回调方法operationComplete方法 } }
从上例子中,我嵌套了两层,但是threadLocals.futureListenerStackDepth()的值始终是0,并没有随着嵌套的层数增加,而增加。为什么源码中stackDepth < MAX_LISTENER_STACK_DEPTH) 要做这个限制呢?
为什么打印两次stackDepth 的值都是0,感兴趣的小伙伴可以去打断点试试 。 还是解释一下吧。
因此在operationComplete()方法的内部调用addListener()并没有我要想像中的,继续调用notifyListenersNow()中的notifyListeners0()方法,而是发现notifyingListeners为true,则直接返回了。 相应的futureListenerStackDepth值也被设置为原来的值, 所以嵌套调用的逻辑就是
我弄了很多的图片,主要想通过打断点的过程来分析notifyListenersNow()方法的执行过程。
private void notifyListenersNow() { Object listeners; synchronized (this) { // 正在通知或已没有监听者(外部线程删除)直接返回 if (notifyingListeners || this.listeners == null) { return; } notifyingListeners = true; listeners = this.listeners; this.listeners = null; } for (;;) { //只有一个listener if (listeners instanceof DefaultFutureListeners) { notifyListeners0((DefaultFutureListeners) listeners); } else { //有多个listener notifyListener0(this, (GenericFutureListener<? extends Future<V>>) listeners); } synchronized (this) { if (this.listeners == null) { // 执行完毕且外部线程没有再添加监听者 notifyingListeners = false; return; } //外部线程添加了新的监听者继续执行 listeners = this.listeners; this.listeners = null; } } }
notifyListenersNow()这个方法看上去简单,但包含的情况还是很多的,因此有兴趣的小伙伴,可以自己写例子去分析为好 。 notifyListener0()方法的内部就是直接调用回调方法operationComplete()了。
private void notifyListeners0(DefaultFutureListeners listeners) { GenericFutureListener<?>[] a = listeners.listeners(); int size = listeners.size(); for (int i = 0; i < size; i ++) { notifyListener0(this, a[i]); } } private static void notifyListener0(Future future, GenericFutureListener l) { try { l.operationComplete(future); } catch (Throwable t) { if (logger.isWarnEnabled()) { logger.warn("An exception was thrown by " + l.getClass().getName() + ".operationComplete()", t); } } }
有了上面的例子,通过上面源码的理解,再来看下面这个例子就简单了。
public class TestPromise { public static void main(String[] args) throws Exception { NioEventLoopGroup loopGroup = new NioEventLoopGroup(); EventLoop next = loopGroup.next(); Promise<String> promise = (Promise) next.terminationFuture(); promise.addListener(new GenericFutureListener<Future<? super String>>() { @Override public void operationComplete(Future<? super String> future) throws Exception { Object s = future.get(); System.out.println(new Date() + "listner1---promise的future返回值:" + s); } }); promise.addListener(new GenericFutureListener<Future<? super String>>() { @Override public void operationComplete(Future<? super String> future) throws Exception { Object s = future.get(); System.out.println(new Date() + "listner2---promise的future返回值:" + s); } }); next.execute(new Runnable() { @Override public void run() { try { System.out.println(new Date() + "execute异步执行开始"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(new Date() + "execute异步执行结束"); promise.setSuccess("promise set ");//设置返回结果 并且通知所有Listeners执行回调方法operationComplete方法 } }); System.out.println(new Date() + "--------主线程的打印"); new Thread(new Runnable() { @Override public void run() { System.out.println(new Date() + "--------多线程的打印"); try { promise.sync(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(new Date() + "--------多线程继续运行"); } }).start(); System.out.println(new Date() + "--------主线程继续运行"); } }
结果输出:
上面标识了打印顺序,当然,1,2 的位置可能交换,3 和4 的位置可能交换。 而相对顺序不会发生改变,7 一定在8的前面执行。因为加入listeners 的顺序是先加入7,再加入8 , 看addListener0()方法 。 按道理6比7和8先执行,promise.sync(); 这一行代码执行,线程将进入wait() , 因为在checkNotifyWaiters()中,如果有其他线程处于等待状态,将调用 notifyAll() 唤醒等待中的线程,接着再去遍历listeners 的 operationComplete()方法,所以按道理 6 比 7 和 8 先执行,因此当你看DefaultPromise源码后再来理解上述例子就很简单了。 其他的就不做过多分析 。
补充
2022-01-30
接下来补充一下之前提出的疑问 。
之前分析过上述代码,但是例子并没有证实嵌套operationComplete()方法会导致futureListenerStackDepth递增,其实之前的例子已经很接近实验效果,感觉与成功失之交臂,但通过网上资料,找到问题所在。
public class TestPromise5 { public static void main(String[] args) throws Exception { NioEventLoopGroup loopGroup = new NioEventLoopGroup(); EventLoop next = loopGroup.next(); Promise<String> promise = (Promise) next.terminationFuture(); promise.addListener(new GenericFutureListener<Future<? super String>>() { @Override public void operationComplete(Future<? super String> future) throws Exception { Object s = future.get(); System.out.println(new Date() + "listner1---promise的future返回值:" + s); NioEventLoopGroup loopGroup = new NioEventLoopGroup(); EventLoop next = loopGroup.next(); Promise<String> promise = (Promise) next.terminationFuture(); promise.addListener(new GenericFutureListener<Future<? super String>>() { @Override public void operationComplete(Future<? super String> future) throws Exception { Object s = future.get(); final InternalThreadLocalMap threadLocals = InternalThreadLocalMap.get(); final int stackDepth = threadLocals.futureListenerStackDepth(); System.out.println(new Date() + "listner1 inner ---promise的future返回值:" + s + ", stackDepth = " + stackDepth); NioEventLoopGroup loopGroup = new NioEventLoopGroup(); EventLoop next = loopGroup.next(); Promise<String> promise = (Promise) next.terminationFuture(); promise.addListener(new GenericFutureListener<Future<? super String>>() { @Override public void operationComplete(Future<? super String> future) throws Exception { final InternalThreadLocalMap threadLocals = InternalThreadLocalMap.get(); final int stackDepth = threadLocals.futureListenerStackDepth(); Object s = future.get(); System.out.println(new Date() + "listner1 inner inner ---promise的future返回值:" + s + ", stackDepth ="+stackDepth); } }); promise.setSuccess("第二层"); } }); promise.setSuccess("第一层"); } }); promise.setSuccess("promise set ");//设置返回结果 并且通知所有Listeners执行回调方法operationComplete方法promise } }
与之前例子之间的区别通过上面红色和蓝色代码标识出了。 之前每次复用之前的,而这个例子中,在operationComplete()方法的内部,都创建一个新的promise,因此在notifyListenersNow方法中。
因为每一次都创建一个新的DefaultPromise, 所以if (notifyingListeners || this.listeners == null) { 这个条件判断始终为false,所以 每一次走的都是notifyListeners0()方法,从而促使futureListenerStackDepth的递增,Netty 这样做的原因,也是限制 Promise 的嵌套层数 ,默认最多为8层。看执行效果 。
总结
这里其实就是一个同步检测当前事件是否完成的过程。
以上就是 Netty 中实现的 Future/Promise 异步回调机制。实现并不是很难懂,代码很值得学习。除了 Netty 中实现了 Future/Promise模型,在Guava中也有相关的实现,大家日常使用可以看习惯引用相关的包。
Guava实现:
关于DefaultPromise 源码解析又告一段落了,在分析DefaultPromise 源码过程中,心情也是一波三折, 有些东西,感觉自己懂了,但是一测试,发现又不是那么回事,可能还是测试用例不全面,随着Netty源码的分析深入,我相信这些问题都会得到解决,这篇博客中也有一些疑问没有解决,可能在后面再遇到类似问题,再来补全博客了吧。 下一篇博客见。
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>21.0</version> </dependency> ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); ListenableFuture<Integer> future = service.submit(new Callable<Integer>() { public Integer call() throws Exception { TimeUnit.SECONDS.sleep(5); return 100; } }); Futures.addCallback(future, new FutureCallback<Integer>() { public void onSuccess(Integer result) { System.out.println("success:" + result); } public void onFailure(Throwable throwable) { System.out.println("fail, e = " + throwable); } }); Thread.currentThread().join();
参考博客
源码地址: