简介在我的印象中,JDK有两个经典版本。第一个是JDK8,现在大部分公司都在用。更流畅,减少了很多冗余代码。另一个版本更早一些,还是JAVA1.X时代,我们称之为JDK1.5,这个版本引入了java.util.concurrent并发包,从此可以愉快的使用JAVA异步编程了。JDK虽然已经发展到17版本,但是并发度并没有太大变化。受限于JDK维护稳定性的要求,并发发包提供的功能不能完全满足某些业务场景。所以依赖JDK的包都开发了自己的并发包。当然netty也不例外。下面我们来看看netty的并发合约交付的优势。JDK异步的由来如何在java中创建一个异步任务,或者启动一个异步线程,每个人可能都有自己的答案。你可能首先想到的是创建一个实现了Runnable接口的类,然后封装成Thread来运行,如下所示:newThread(new(RunnableTask())).start()requiresanewThreadevery时间是JDK高手们无法接受的,于是想出了一个思路,封装线程调用,这个封装类就叫做Executor。执行器是一个接口。首先看这个接口的定义:publicinterfaceExecutor{voidexecute(Runnablecommand);}接口很简单,它定义了一个execute方法来执行传入的Runnable命令。所以我们可以像这样异步启动任务:Executorexecutor=anExecutor;executor.execute(newRunnableTask1());executor.execute(newRunnableTask2());看到这里,聪明的朋友可能要问了,好像不对呀,Executor自定义了execute接口,好像和异步多线程关系不大吧?不用担心,因为Executor是一个接口,我们可以有很多实现。比如下面直接执行Runnable,这样Runnable就可以在当前线程中执行了:}}另一个例子是在一个新的线程中执行Runnable:}}另一个例子是队列中存储多个任务的顺序执行,先执行一个任务再执行下一个任务:finalExecutor执行者;可运行活动;SerialExecutor(Executor执行器){this.executor=executor;{tasks.offer(newRunnable(){publicvoidrun(){try{r.run();}finally{scheduleNext();}}});if(active==null){scheduleNext();}}protectedsynchronizedvoidscheduleNext(){if((active=tasks.poll())!=null){执行者。执行(主动);}}}这些Executor是完美的,但是他们只能提交任务,提交任务之后什么都不知道。这对于好奇的宝宝来说是无法忍受的,因为我们需要知道执行的结果,或者控制任务的执行。于是就有了ExecutorService。ExecutorService也是一个接口,但是它提供了一个shutdown方法来停止接受新的任务,isShutdown来判断关闭状态。此外,它还提供了单独调用任务的submit方法和批量调用任务的invokeAll和invokeAny方法。既然有了execute方法,submit虽然和execute方法基本执行相同的操作,但是方法参数和返回值略有不同。首先是返回值,submit返回Future,Future代表异步计算的结果。它提供了检查计算是否完成、等待计算完成以及检索计算结果的方法。Future提供了get方法获取计算结果。但是如果调用get方法时计算结果还没有准备好,就会发生阻塞。第二个是提交参数。一般来说只有Callable才会有返回值,所以我们常见的调用方式是这样的:Futuresubmit(Callabletask);如果我们传入Runnable,那么虽然也是返回一个Future,但是返回值为null:Future>submit(Runnabletask);如果我想传入Runnable并希望Future有返回值怎么办?古人告诉我们,鱼和熊掌不可兼得!但现在是2021年,有些事情可以改变:Futuresubmit(Runnabletask,Tresult);上面我们可以传入一个结果,当Future中的任务执行时,直接返回结果。既然ExecutorService这么强大,那么如何创建ExecutorService呢?最简单的方法是使用new创建对应的实例。但是这样还不够优雅,所以JDK提供了一个Executors工具类,它提供了多种创建不同ExecutorServices的静态方法,非常好用。netty中的Executor兼容JDK并发框架。netty中虽然也有Executors,但是netty中的Executors都是从JDK的并发包派生出来的。具体来说,netty中的Executor叫做EventExecutor,继承自EventExecutorGroup:publicinterfaceEventExecutorextendsEventExecutorGroupEventExecutorGroup继承自JDK的ScheduledExecutorService:publicinterfaceEventExecutorGroupextendsScheduledExecutorService,Iterable为什么叫Group呢?这个Group意味着它包含一个EventExecutors的集合。这些组合中的EventExecutor都是通过Iterable的next方法来遍历的。这就是为什么EventExecutorGroup也继承了Iterable类。然后在EventExecutor的基础上扩展了netty中其他具体Executor的实现。这样就得到了netty自己的EventExecutor实现。Future的困境与netty的实现那么Future在JDK中会出现什么问题呢?前面我们也提到了,虽然JDK中的Future保存了计算结果,但是我们想要获取的时候还是需要通过调用get方法来获取。但是如果当前的计算结果还没有出来,get方法就会导致当前线程被阻塞。不用担心,这个问题在netty中已经解决了。首先看netty中Future的定义:publicinterfaceFutureextendsjava.util.concurrent.Future可以看到netty中的Future是继承自JDK的Future。还添加了addListener和removeListener,以及sync和await方法。先说sync和await方法,都是等待Future执行结束。不同之处在于,如果未来在执行过程中失败,则会抛出异常。await方法没有。所以如果不想同步调用Future的get方法获取计算结果。然后你可以给Future添加一个监听器。这样当Future执行结束时,会自动通知监听器中的方法,从而达到异步通知的效果。使用代码如下:EventExecutorGroupgroup=newDefaultEventExecutorGroup(4);//4threadsFuture>f=group.submit(newRunnable(){...});f.addListener(newFutureListener>{publicvoidoperationComplete(Future>f){..}});还有一个问题,我们每次提交一个任务,都需要创建一个EventExecutorGroup。有没有一种方法可以在不创建任务的情况下提交任务?一些!对于没时间新建EventExecutorGroup的同志,netty特意新建了一个全局的GlobalEventExecutor,可以直接使用:GlobalEventExecutor.INSTANCE.execute(newRunnable(){...});GlobalEventExecutor是一个单线程的任务执行器,每秒钟回去检查是否有新的任务,如果有则提交给执行器执行。总结Netty为JDK的并发包提供了非常有用的扩展。大家可以直接使用。本文已收录于http://www.flydean.com/46-netty-future-executor/最流行的解读,最深刻的干货,最简洁的教程,很多你不知道的小技巧等着你等你发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!