接上一篇,我们继续看

不知道大家第一次看这段代码的时候有没有一脸懵逼,反正我是一脸懵,为什么这个if else 最终都是调用的register0方法,都是一样的。

其实这里就是为什么Netty是线程安全的根本原因。

我们先看下 eventLoop.inEventLoop() 方法

第一张图传入了 当前的 线程, 第二个图 判断了 当前这个NioEventLoop中的Thread 是不是和当前线程相等, 如果相等返回true, 相反就是false.

我们debug 看一下

发现NioEventLoop中的Thread 当前并没有赋值, 值是null,所以返回false.

那么代码也就进入到了

这里其实也容易漏看,其实这里不只是启动一个子线程来执行register0, 其实在这之前还做了好多时间。

我们进入eventLoop的execute()方法,惊喜不。。。

inEventLoop的值肯定是false,  然后执行addTask(task),把当前这个任务(register0)加入到队列中,看下这个队列

这个队列是一个LinkedBlockingQueue.

继续

这就是举世闻名的CAS无锁技术,当然不了解CAS的自行百度。这里我想说的是,大家可以学习一下Netty这种写法。

CAS方式原子性更新state字段的值,这里的state一定要使用volatile修饰,这个关键字不太了解的,也自行百度。

回到  startThread() 方法, 先检查一下Thread 是否已经启动, 如果没有启动,就把state原子性改成 启动状态 ,如果在启动过程中出现异常,则再次把state原子性改成 未启动状态。

继续进入 doStartThread() 方法

先是一个断言来保证thread一定是null, 然后启动一个子线程,并把当前这个子线程 赋值给了当前的 这个NioEventLoop 中的 thread 成员变量。 ok ,到现在为止,NioEventLoop 中的唯一线程确定。

从这里我们进入run() 方法

我们发现进入到了一个死循环, 然后里面有一个switch分支,我们来看下里面的策略计算方法。

在说这个之前我们再来一起看一个NIO中多路复用器的API

不会阻塞,不管什么通道就绪都立刻返回(译者注:此方法执行非阻塞的选择操作。如果自从前一次选择操作后,没有通道变成可选择的,则此方法直接返回零。

同时这个方法会清除wakeup()方法的效果。

此方法执行阻塞的 selection operation 。 只有在选择了至少一个通道之后,才会返回此选择器的wakeup方法,当前线程被中断,或给定的超时期限到期,以先到者为准。

此方法不提供实时保证:它调用超时,就像调用Object.wait(long)方法一样。

如果另一个线程在调用select()select(long)方法时被阻止,则该调用将立即返回。 如果当前没有选择操作,则下一次调用这些方法之一将立即返回,除非在此期间调用selectNow()方法。 无论如何,该调用返回的值可能不为零。 的后续调用select()select(long)除非此方法在此期间再次调用的方法将阻塞如常。 在两次连续的选择操作之间多次调用此方法与调用它一样具有相同的效果

好了,了解了这些我们继续看,

先检查是否有待处理的task,如果有那么就非阻塞的检查一下是否有新的channel被注册,然后返回channel注册的数量,可能是0, 如果没有task,则返回 - 1

我们发现如果有task,那么这么switch就直接跳出了。如果返回 - 1 ,就执行 select(wakenUp.getAndSet(false))

我们先看下没有task的情况吧。先大概读一下这一大段注释

大概的意思是说:

在调用选择器唤醒方法,之前,先确定wakenUp的值,以减少唤醒负载,因为唤醒选择器是一个耗时的操作。  但是不能把warkup设置true太早,将会触发竞争。

1、选择器在wakenUp属性更新为false和选择操作之间被唤醒

2、选择器在选择操作和获取wakenUp属性之间

在第一种情况下,当wakenUp属性更新为true,接下来的选择操作就会立刻被唤醒, 直到在下一次循环中wakenUp属性更新为false,wakenUp.compareAndSet(false, true)  ,将会失败,同时引起下一次不必要的选择操作阻塞, 怎么这句话呢(自己的理解)。

我们看一下这个方法 select(wakenUp.getAndSet(false))

首先我们假如入参当前是false, 也就是 oldWakenUp = false

那么再假如当前是有task待处理的,那么也就是说  hasTasks() && wakenUp.compareAndSet(false, true)  == true , 那么将执行selectNow(), 也就是当前时间到上一次select操作的期间内是否有channel注册进来。

然后break,接下来

wakeUp 刚刚被CAS 成 true ,所以这里会执行wakeup操作,也就意味着下一次select操作将会被立即返回。

接下来就是去处理task 和 新接入的客户端或者读写操作了(一会再说这个)。

因为是死循环,我们继续回来,又到了

这次的wakeUp 变成了true, 并且把状态置为false, 那么也就是说  oldWakenUp = true

这里不管有没有任务,都会立即返回,因为我们之前执行了selector.wakeup(),这里我自己猜测可能是因为处理读写和任务用掉了很长时间,所以这里直接就检查当前是会有channel已经注册进来已经在等待了。

如果有的话,直接break.去执行。

当然如果之前没有 selector.wakeup() 过,那么将会执行 1s 的时间,看着1s 内是否有新的channel进来。

继续看,通过这两段我们发现如果循环超时了,那么将会break掉。

通过这两段我们发现,当循环512次之后,那么将会重建Selector

这里其实是因为JDK的BUG导致的,会把CPU飚到100%

整个重建的过程其实就是,创建新的selector,把老的上面的 SelectionKey 都注册到新的selector上,然后将老的selector关闭掉,具体的内容就不一起看了。

Netty源码分析--Channel注册(中)(六)的更多相关文章

  1. Netty源码分析--Channel注册(上)(五)

    其实在将这一节之前,我们来分析一个东西,方便下面的工作好开展. 打开启动类,最开始的时候创建了一个NioEventLoopGroup 事件循环组,我们来跟一下这个. 这里bossGroup, 我传入了 ...

  2. Netty源码分析--Channel注册&绑定端口(下)(七)

    接下来,我们看到的就是两个非常重要的方法 就是 processSelectedKeys() 和  runAllTasks() 方法了. selectionKey中ready的事件,如accept.co ...

  3. Netty源码分析-- 处理客户端接入请求(八)

    这一节我们来一起看下,一个客户端接入进来是什么情况.首先我们根据之前的分析,先启动服务端,然后打一个断点. 这个断点打在哪里呢?就是NioEventLoop上的select方法上. 然后我们启动一个客 ...

  4. Netty源码分析 (七)----- read过程 源码分析

    在上一篇文章中,我们分析了processSelectedKey这个方法中的accept过程,本文将分析一下work线程中的read过程. private static void processSele ...

  5. Netty源码分析第1章(Netty启动流程)---->第3节: 服务端channel初始化

    Netty源码分析第一章:Netty启动流程   第三节:服务端channel初始化 回顾上一小节的initAndRegister()方法: final ChannelFuture initAndRe ...

  6. Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用

    Netty源码分析第一章:Netty启动流程   第四节:注册多路复用 回顾下以上的小节, 我们知道了channel的的创建和初始化过程, 那么channel是如何注册到selector中的呢?我们继 ...

  7. Netty源码分析第3章(客户端接入流程)---->第4节: NioSocketChannel注册到selector

    Netty源码分析第三章: 客户端接入流程 第四节: NioSocketChannel注册到selector 我们回到最初的NioMessageUnsafe的read()方法: public void ...

  8. Netty源码分析第8章(高性能工具类FastThreadLocal和Recycler)---->第4节: recycler中获取对象

    Netty源码分析第八章: 高性能工具类FastThreadLocal和Recycler 第四节: recycler中获取对象 这一小节剖析如何从对象回收站中获取对象: 我们回顾上一小节demo的ma ...

  9. Netty源码分析(前言, 概述及目录)

    Netty源码分析(完整版) 前言 前段时间公司准备改造redis的客户端, 原生的客户端是阻塞式链接, 并且链接池初始化的链接数并不高, 高并发场景会有获取不到连接的尴尬, 所以考虑了用netty长 ...

随机推荐

  1. CodeForces Round#229 DIV2 C 递归DP

    这条路是只说哦话题,没有注意到k只有最大射程10,所以昨天晚上,一个很长的纠结.没有好的办法来处理,后来不情愿地去寻找解决问题的办法,研究发现,人们对开始到句子,由于k的范围比较小 所以....... ...

  2. 2 WCF里面配置的含义

    1 首先介绍所谓的a,b,c. a就是address 地址: b binding 绑定的协议 譬如http  tcp udp 利用这些协议方式请求address: c contract  代表请求的规 ...

  3. Windows中点击“关闭”button发生了什么?

    对于Windows操作,当用户点击"关闭"button时,窗体函数就会收到一个WM_DESTROY消息. 窗体函数应该调用PostQuitMessage(0) 向消息队列插入一个W ...

  4. 手把手教你开发Nginx模块

    前面的哪些话 关于Nginx模块开发的博客资料,网上很多,很多.但是,每篇博客都只提要点,无法"step by step"照着做,对于初次接触Nginx开发的同学,只能像只盲目的蚂 ...

  5. C#控制台关闭之前做一些操作

    using System; using System.Runtime.InteropServices; class Program { static void Main(string[] args) ...

  6. WPF 拖动多个文件到窗体 添加文件信息

    将Window的AllowDrop属性设置为true window添加Drop事件 private void Window_Drop(object sender, DragEventArgs e) { ...

  7. 解压压缩文件报错gzip: stdin: not in gzip format tar: Child returned status 1 tar: Error is not recoverable: exiting now

    压缩包是直接weget 后面加官网上的tar包地址获取的  [root@xuegod43 ~]# tar -zxvf /home/hadoop/hadoop-2.6.5-src.tar.gz gzip ...

  8. ASP .NET Response类型

    .ContentType .htm,.html Response.ContentType = "text/HTML"; .txt Response.ContentType= &qu ...

  9. WPF Path.Data 后台代码赋值

    Path path = new Path(); string sData = "M 250,40 L200,20 L200,60 Z"; var converter = TypeD ...

  10. LeetCode - 4 - Longest Substring Without Repeating Characters

    题目 URL:https://leetcode.com/problems/median-of-two-sorted-arrays/ 解法 二分法. 总的思想是将 2 个数组用 2 个指针“整体”二分. ...