Netty源码分析--Channel注册(中)(六)
接上一篇,我们继续看

不知道大家第一次看这段代码的时候有没有一脸懵逼,反正我是一脸懵,为什么这个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注册(中)(六)的更多相关文章
- Netty源码分析--Channel注册(上)(五)
其实在将这一节之前,我们来分析一个东西,方便下面的工作好开展. 打开启动类,最开始的时候创建了一个NioEventLoopGroup 事件循环组,我们来跟一下这个. 这里bossGroup, 我传入了 ...
- Netty源码分析--Channel注册&绑定端口(下)(七)
接下来,我们看到的就是两个非常重要的方法 就是 processSelectedKeys() 和 runAllTasks() 方法了. selectionKey中ready的事件,如accept.co ...
- Netty源码分析-- 处理客户端接入请求(八)
这一节我们来一起看下,一个客户端接入进来是什么情况.首先我们根据之前的分析,先启动服务端,然后打一个断点. 这个断点打在哪里呢?就是NioEventLoop上的select方法上. 然后我们启动一个客 ...
- Netty源码分析 (七)----- read过程 源码分析
在上一篇文章中,我们分析了processSelectedKey这个方法中的accept过程,本文将分析一下work线程中的read过程. private static void processSele ...
- Netty源码分析第1章(Netty启动流程)---->第3节: 服务端channel初始化
Netty源码分析第一章:Netty启动流程 第三节:服务端channel初始化 回顾上一小节的initAndRegister()方法: final ChannelFuture initAndRe ...
- Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用
Netty源码分析第一章:Netty启动流程 第四节:注册多路复用 回顾下以上的小节, 我们知道了channel的的创建和初始化过程, 那么channel是如何注册到selector中的呢?我们继 ...
- Netty源码分析第3章(客户端接入流程)---->第4节: NioSocketChannel注册到selector
Netty源码分析第三章: 客户端接入流程 第四节: NioSocketChannel注册到selector 我们回到最初的NioMessageUnsafe的read()方法: public void ...
- Netty源码分析第8章(高性能工具类FastThreadLocal和Recycler)---->第4节: recycler中获取对象
Netty源码分析第八章: 高性能工具类FastThreadLocal和Recycler 第四节: recycler中获取对象 这一小节剖析如何从对象回收站中获取对象: 我们回顾上一小节demo的ma ...
- Netty源码分析(前言, 概述及目录)
Netty源码分析(完整版) 前言 前段时间公司准备改造redis的客户端, 原生的客户端是阻塞式链接, 并且链接池初始化的链接数并不高, 高并发场景会有获取不到连接的尴尬, 所以考虑了用netty长 ...
随机推荐
- lua转换etcd应答
local function decodeNodes(nodes) local table = {} for _, value in ipairs(nodes) do if value.nodes t ...
- openresty: nginx worker不同请求之间共享数据
To globally share data among all the requests handled by the same nginx worker process, encapsulate ...
- Jquery对象和dom对象获取html的方法
1)DOM对象 var domObj = document.getElementById("id"); //DOM对象 domObj.innerHTML;// domObj.out ...
- Linux性能测试 sar命令
sar命令包含在sysstat工具包中,提供系统的众多统计数据.其在不同的系统上命令有些差异 sar 命令行的常用格式: [root@C44 ~]# sar sysstat version (C) S ...
- Scatter matrix(散布矩阵)
n 个 m 维的样本,Xm×n=[x1,x2,-,xn],样本均值定义为: x¯=1n∑i=1nxi 散列矩阵定义为如下的半正定矩阵: S=∑j=1n(xj−x¯)(xj−x¯)T=∑j=1n(xj− ...
- 机器学习: Tensor Flow with CNN 做表情识别
我们利用 TensorFlow 构造 CNN 做表情识别,我们用的是FER-2013 这个数据库, 这个数据库一共有 35887 张人脸图像,这里只是做一个简单到仿真实验,为了计算方便,我们用其中到 ...
- 食谱API自由和开放接口-为了发展自己的健康厨房APP应用
什么时候健康厨房 (cook.yi18.net)上线后,基于接口的须要,就非常快就完毕的食谱API接口的开发 文档地址:http://doc.yi18.net/cookapidoc 菜谱食谱API , ...
- WPF BorderBrush BorderThickness
基本上所有的控件都可以设置BorderBrush BorderThickness 例如TextBox,Button
- linux 修改 ssh 的端口号,启动hg服务
修改 ssh 的端口号 # vim /etc/ssh/sshd_config 将其中的 Port 改为 想要修改的端口号. # service sshd restart 启动hg服务 # hg ...
- SQL Server分页存储过程笔记
USE [database] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER PROCEDURE [dbo].[ProcedureN ...