i++和++i的区别_Netty有几类线程池,它们的区别,以及和JDK线程池区别分别是什么?...

93d7337c4c0a703ece075a540df103d5.png

第一次尝试在知乎写技术文章,略紧张,本文大概1000字,阅读时间大约5分钟,前面使用了多篇文章,透彻总结拆解了Netty的线程图像,或者说线程模型,下面对Netty的线程池做一个小的总结。不妨带着问题阅读:

1、Netty有多少类线程池,它们的区别和使用场景都是什么?

2、Netty的线程池和JDK的线程池有什么区别?


一般的,Netty里常用的线程池有如下几种:

74553fe9a346413274f862c255d75f75.png

经过前面的分析,知道Netty实现了自己的线程池——EventLoopGroup。其中最最常用的就是大家都知道的NioEventLoopGroup,在非阻塞模式的编码中,基本都是用它来实现Reactor模型。其实更清晰的说法是Netty在自己的线程封装类——NioEventLoop中封装了JDK的线程实例Thread,I/O多路复用器以及异步任务队列,实现了高性能的线程模型。使用如下:

EventLoopGroup workerGroup = new NioEventLoopGroup();

另外,上一篇文章刚刚总结了GlobalEventExecutor.INSTANCE,我想就不必多说,严格来说它不算线程池,但是理解为只有一个线程的特殊的线程池也未尝不可。

除了以上线程池,Netty还提供了一种非I/O线程池——DefaultEventExecutorGroup,它的用法和I/O线程池一样,如下:

EventExecutorGroup businessGroup = 
              new DefaultEventExecutorGroup(16); // 业务线程池

即:

1、NioEventLoopGroup线程池内部是NioEventLoop,它可以理解为是I/O线程,它内部聚合了Java的Thread,以及JDK的I/O多路复用器,实现了事件循环机制,侧重于处理网络I/O相关的各种操作

2、DefaultEventExecutorGroup线程池内部是DefaultEventExecutor,它可以理解为是非I/O线程,内部聚合了Java的Thread,但没有I/O多路复用器,侧重于处理耗时业务逻辑(非I/O操作)

下面重点看这个DefaultEventExecutor的实现,如下类定义:

9f0f88a745e63f72c4bdf6c16a994f55.png

注释写到:默认的Netty线程执行器会串行的执行提交到它的异步任务。

下面是Netty的线程模型完整类图,包括了线程和线程池,最上面是继承的JDK的线程池相关接口,包括定时任务执行器,左下角是Netty的线程池,右下角是线程:

d74865c73a4a1ca519efe4e6aa2620a3.png

作为对比,回忆NioEventLoop的创建过程,它会在I/O线程池NioEventLoopGroup中,使用newChild方法创建:

2e09b06a73f11885d94bcd3e490fb123.png

同理,DefaultEventExecutor的创建过程如出一辙,也会在非I/O线程池DefaultEventExecutorGroup中,使用newChild方法创建:

7194cabfa2bc8a1cd233e784ac1695b0.png

两类线程(池)的区别:

1、I/O线程池里的线程封装实例Thread会绑定I/O多路复用器,以及配套的NIO的属性,非I/O线程池的线程只封装Thread,类似于JDK原生的线程,很干净。

2、非I/O线程池的线程执行器的执行原理和I/O线程池的线程执行器没什么区别,都可以统称为Netty线程,而且它们都消除了锁竞争。即每个Channel只会绑定一个不变的Netty线程,而一个Netty线程可以绑定多个Channel,期间每个Channel上的各种handler的逻辑执行都是串行无锁的。如下是DefaultEventExecutor的run:

a7c2cfba394c3b9cdbba23d6605cdc8d.png

逻辑就是当前一个非I/O线程在不断的执行其异步任务队列——MPSCQ里的任务。即不论是I/O线程还是非I/O线程,在执行任务时,都是下面这样的图像:

a2b6eb71162229c9c115f7ccaf1a2ba8.png

以上,总结完了Netty的几类线程池,对于非I/O线程池来说,最常见的用法可以参考文章:Netty耗时的业务逻辑应该写在哪儿,有什么注意事项?

下面看看它们和JDK线程池的区别

两者比起来,Netty线程池里的线程全部消除了对锁的竞争,而JDK的线程池没有这种设计,JDK线程池的线程会处理同一个阻塞队列。比如LinkedBlockingQueue,可能产生锁竞争。如下(图来源于网络,侵删):

6b61de443e1048d7e12b5a6a2b00fcf1.png

虽然LinkedBlockingQueue通过读写锁来提升性能,但是当业务线程数和写操作比较多时,锁竞争对性能的影响还是比较大的。如果采用自定义线程池,那么优化方向就是锁消除,也可以使用Disruptor或者使用Channel Id与业务线程池中的某个业务进行绑定。当然最好是直接使用Netty默认实现的业务线程池。并且还可以搭配操作系统层面的CPU亲和特性,来给某个线程指定CPU内核,使其尽可能不发生切换。

小结:

以上简单总结了Netty的几大类常用,常见的线程池,并且分析了它们在不同场景下的用法,还对比了它们和JDK线程池的区别,需要掌握用法,更重要的是掌握线程池的多种设计思路,和多线程代码的高性能的编写技巧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值