• 处理器ChannelHandler的线程安全问题


    这里主要讨论ChannelHandler的 @ S harable注解的作用。
    ChannelHandler 的一端是Netty NIO线程,另一端则是业务线程池,在多线程并发场景下理解ChannelHandler的并发安全性很重要,如果使用不当,会产生性能和并发安全问题。

    【非共享ChannelHandler的线程安全问题】

    如果每个channel创建某个ChannelHandler时都new一个自己的处理器。
    假设有一个非线程安全的类 ThreadUnsafeClass 在 ChannelHandler 的 channelRead 中调用,请问调用是否会有问题。 先说结论:没有问题
    如果ChannelHandler是非共享的,则它就是线程安全的, 原因为:当链路完成初始化时会创建ChannelPipeline,每个Channel对应一个ChannelPipeline实例,业务的ChannelHandler会被实例化并加入ChannelPipeline执行。由于 某个Channel只能被特定的NioEventLoop线程执行,因此ChannelHandler不会被并发调用,不用考虑线程安全问题
    这里说的线程安全问题,指的是同一个channel流水线中的所有ChannelHandler,会严格按照顺序由NioEventLoop组中某一个线程去执行
    业务可能会多线程调用ChannelHandlerContext或者Channel的write方法,这会不会导致多个业务线程并发调用ChannelHandler 呢?通过源码分析发现,不会产生并发安全问题。在执行write操作时,判断是否是下一个要执行write操作的AbstractChannelHandlerContext的EventExecutor线程,如果不是则将write操作封装成AbstractWriteTask放入线程任务队列异步执行,原调用线程返回。如果业务的ChannelHandler没有指定(通常不需要指定)EventExecutor线程,则使用的就是消息读写对应的 NioEventLoop线程。由此看见,即便多个业务线程并发调用某个Channel,也不会产生多个线程并发访问业务ChannelHandler的问题。源码如下(AbstractChannelHandlerContext类):

    【共享ChannelHandler的线程安全问题】

    如果ChannelHandler不是共享的,重复向ChannelPipeline添加时就会抛出ChannelPipelineException异常,添加失败。所以非共享的同一个ChannelHandler实例不能被重复加入多个ChannelPipeline或者被多次加入某一个ChannelPipeline。通过 @ S harable注解可以让不同的channel添加同一个ChannelHandler实例到自己的流水线中。
    首先,netty的处理模型是存在一组IO线程,去处理IO事件,如read,connect,write等等,对于服务端接收到的每个channel,都会将该channel映射到一条IO线程。
    当一个channel被建立之后,需要将其初始化,包含给他创建pipleline并填充channelhandler;给channel附以channelOptions和channelAttrs等,其中填充channelhandler可能会导致问题
    如某个channelHandler是共享的,却被添加到多个channel上,而每个channel对应一个线程,必然会导致线程并发问题。
    当ChannelHandler被添加到多个ChannelPipeline,就会面临多线程并发访问问题,需要ChannelHandler保证自身的线程安全,例如通过原子类、读写锁等方式对数据做并发保护。如果加锁,可能会阻塞NioEventLoop线程,所以Sharable注解的ChannelHandler要慎用

    【ChannelHandler并发总结】

    用户自定义的ChannelHandler有两种场景需要考虑并发安全。
    (1)通过Sharable注解,多个ChannelPipeline 共享的ChannelHandler, 它将被多个NioEventLoop线程( 通常用户创建的NioEventLoopGroup线程数>1)并发访问,如图7-5所示。在这种场景下,用户需要 保证ChannelHandler共享的合理性,同时需要自己保证它的 并发安全性 ,尽量通过原子类等方式降低锁的开销,防止阻塞NioEventLoop线程。 这里的并发安全在于,假如该共享ChannelHandler有某个标值类成员变量,不用的Channel可能会同时修改该变量,则会出现线程安全问题
    (2)ChannelHandler没有共享,但是在用户的ChannelPipeline中的一些ChannelHandler绑定了新的线程池,这样ChannelPipeline的ChannelHandler就会被异步执行,如图7-6所示。在多线程异步执行过程中,如果某ChannelHandler的成员变量共享给其他ChannelHandler,那么被多个线程并发访问和修改就存在并发安全问题,如图7-7所示。
  • 相关阅读:
    1480. Running Sum of 1d Array (Java)
    【OpenCV 例程200篇】208. Photoshop 对比度自动调整算法
    2022 全球 AI 模型周报
    考研时间规划和激励语句
    java项目-第128期ssm+oracle的宿舍管理平台-java毕业设计_计算机毕业设计
    BIO、NIO、IO多路复用(select/poll/epoll)、信号驱动IO、异步IO
    大数据分析&数据仓库关于数据库选型方面的感触
    Android入门第36天-以一个小动画说一下Android里的Handler的使用
    知识讲解类短视频如何定位?分享三种类型,帮你选择短视频方向
    ORM之查询常见的关键字,神奇的双下滑线查询,外键字段数据操作,正反向概念,基于对象的跨表查询,基于双下滑线的跨表查询
  • 原文地址:https://blog.csdn.net/qq_34448345/article/details/127439899