首先回顾下, Netty中的IO线程主要完成三件事
1.轮询IO事件
2.处理IO事件
3.执行任务

在轮询IO事件的过程中,在Linux系统下, 使用epoll实现.
涉及的Netty代码如下

private void select() {// ...int selectedKeys = selector.select(timeoutMillis);// ...}具体源码位置:
io.netty.channel.nio.NioEventLoop#select

当IO线程执行以上代码的时候, 如果超时时间timeoutMillis还没有到达的情况下, IO线程就会处于阻塞状态. 这个时候如果非IO线程需要向对端写数据, 由于Netty是异步的框架, 它的实现是非IO线程将写数据封装成一个任务提交到IO线程的任务队列里.

当任务提交到任务队列后, 那么就会面临一个问题.此时的IO线程处于阻塞状态, 是否需要唤醒它呢?
答案是需要唤醒, 之所以要把它唤醒, 是需要让IO线程可以及时的处理刚刚非IO线程提交的任务.

@Override
protected void wakeup(boolean inEventLoop) {if (!inEventLoop && wakenUp.compareAndSet(false, true)) {// 唤醒IO线程selector.wakeup();}
}源码位置: io.netty.channel.nio.NioEventLoop#wakeup

以上代码, 就是唤醒的代码, 主要调用的方法就是wakeup.

接下来通过查看它的系统调用, 弄清楚它到底是如何实现的.
代码如下

// WakeUp.java
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;public class WakeUp {public static void main(String[] args) throws Exception {ServerSocketChannel serverSocketChannel;Selector selector = Selector.open();serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1", 8080), 64);serverSocketChannel.configureBlocking(false);serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);new Thread() {@Overridepublic void run() {try {System.out.print("Thread[" + Thread.currentThread().getName() + "]invoke select\r\n");// 底层调用epoll_wait而阻塞int readyChannels = selector.select();} catch (Exception x) {x.printStackTrace();}System.out.print("Success...\r\n");}}.start();// 之所以设置的时间比较久, 是为了让程序暂时不结束Thread.sleep(5_60_000);System.out.print("Thread[" + Thread.currentThread().getName() + "]invoke wakeup\r\n");// 唤醒阻塞线程selector.wakeup();}
}

以上代码的逻辑比较简单, 一个线程调用select()方法阻塞, 另一个线程唤醒它.
首先javac编译以上代码, 然后使用一个查看系统调用的命令strace.
strace -ff -o strace java WakeUp

具体如何使用strace请童鞋自行Google

执行以后, 通过以下步骤进行分析

使用jps查看进程ID号
获得PID=1141
进入 /proc/1141/fd目录下, 就可以查看到当前进程(PID=1141)打开的文件描述符

0,1,2这三个文件描述符是标准输入,标准输出和错误输出.
4号文件描述符是在使用epoll实现的多路复用IO创建的一个文件描述符.
5,6这两个文件描述符是一对管道.
7是select创建的套接字

在上面执行strace命令的时候, 在它的同目录下会生成如下文件

通过搜索strace命令打印的文件内容, 查看具体的系统调用方法.

使用grep命令搜索关键字pipe

程序调用pipe这个系统调用创建管道.
其中的5和6是两个文件描述符,也就是在/proc/1141/fd目录下的那两个5和6文件描述符.
5这个描述符用来读取数据, 6这个描述符用来写入数据, 这样就实现了两个进程之间的通信.

epoll三个关键的方法: epoll_create,epoll_ctl,epoll_wait.
epoll_create用于创建epoll文件描述符
epoll_ctl用于管理其他文件描述符
epoll_wait用于阻塞等待其他文件描述符就绪.

使用grep命令搜索关键字epoll

通过epoll_create创建4号文件描述符.
5和7这两个文件描述符添加到epoll上(底层是添加到内核的红黑树).

在上面的Java代码中, 当调用int readyChannels = selector.select()方法的时候, 底层就会调用epoll_wait方法, 那么线程就会阻塞在此.
当另一个线程调用selector.wakeup()的时候, 它就会向6号文件描述符写入数据, 通过pipe通信的方式, 唤醒另一个阻塞的线程.
可以通过grep搜索关键字write验证结论.

通过write系统调用向6号文件描述符写入数据, 具体数据没有任何含义, 它就是想唤醒阻塞的线程. 与6号文件描述符对应的是5号文件描述符. 由于epoll管理着5号文件描述符, 这样epoll发现有文件描述符就绪(5号文件描述符就绪), 被阻塞的线程也就会被操作系统重新调度.

以上简单介绍了Netty中IO线程如何阻塞和被唤醒的底层系统调用.


个人站点
语雀

公众号

Netty之线程唤醒wakeup相关推荐

  1. 【Netty】Netty 简介 ( 原生 NIO 弊端 | Netty 框架 | Netty 版本 | 线程模型 | 线程 阻塞 IO 模型 | Reactor 模式引入 )

    文章目录 一. NIO 原生 API 弊端 二. Netty 简介 三. Netty 架构 四. Netty 版本 五. Netty 线程模型 六. 阻塞 IO 线程模型 七. 反应器 ( React ...

  2. netty worker线程数量_Dubbo线程模型

    Dubbo中线程池的应用还是比较广泛的,按照consumer端到provider的RPC的方向来看,consumer端的应用业务线程到netty线程.consuemr端dubbo业务线程池,到prov ...

  3. 面试官:Netty的线程模型可不是Reactor这么简单

    笔者看来Netty的内核主要包括如下图三个部分: 其各个核心模块主要的职责如下: 内存管理 主要提高高效的内存管理,包含内存分配,内存回收. 网通通道 复制网络通信,例如实现对NIO.OIO等底层JA ...

  4. Netty Reactor线程模型与EventLoop详解

    本文来说下Netty Reactor线程模型与EventLoop 文章目录 EventLoop事件循环 任务调度 线程管理 线程分配 非阻塞传输 阻塞传输 Netty线程模型 单Reactor单线程模 ...

  5. Netty之线程模型

    Reactor 线程模型: Reactor 是反应堆的意思,Reactor 模型是指通过一个或多个输入同时传递给服务处理器的服务请求的事件驱动处理模式.服务端程序处理传入多路请求,并将它们同步分派给请 ...

  6. python线程唤醒_python 多线程

    python 多线程 真正的多线程吗? 对于多核处理器,在同一时间确实可以多个线程独立运行,但在Python中确不是这样的了.原因在于,python虚拟机中引入了GIL这一概念.GIL(Global ...

  7. linux阻塞线程等待唤醒,linux – POSIX消息队列 – mq_send线程唤醒命令

    有人可以向我解释消息队列如何处理唤醒多个 线程在单个消息队列中被阻止? 我的情况是我有多个作者阻止完整的消息 队列,每个发布优先级等于线程的消息 优先.我想确保他们按优先顺序叫醒和发布, 但是我的应用 ...

  8. netty reactor线程模型分析

    netty4线程模型 ServerBootstrap http示例 // Configure the server.EventLoopGroup bossGroup = new EpollEventL ...

  9. C++多线程之间,线程函数启动之后,多线程依赖的启动和线程唤醒操作。

    C++多线程之间,线程函数启动之后,线程间依赖的启动和唤醒操作 一.原理分析 1. 线程依赖关系 二. 实例分析 2.1 多线程启动 2.2 多线程模式讲解 (1) 多线程开启与主线程唤醒 (2)单线 ...

  10. java重新执行_(转载)java线程 - 线程唤醒后并被执行时,是在上次阻塞的代码行重新往下执行,而不是从头开始执行...

    今天重新把昨晚的线程同步面试题做一遍时,发现实际情况运行下来时,线程一直不同步.后来经过不断测试,发现自己的一个误区. 之前一直以为,线程如果被唤醒后再次执行时,会从头开始运行这个线程,也就是重新运行 ...

最新文章

  1. @RequestBody如何使用
  2. linux c socket通信
  3. phpize增加php模块
  4. Harris及Shi-Tomasi原理及源码【转载】
  5. Nginx学习总结(15)—— 提升 Web 应用性能的十个步骤
  6. 高校后勤管理系统java代码_java毕业设计_springboot框架的高校后勤信息管理系统...
  7. bing的翻译API 国际化
  8. [转]VC常用小知识
  9. 自学python能干什么知乎_自学python能干什么
  10. 2022CCPC广州 CM
  11. 职称计算机ppt教程,职称计算机:Word文档转为PPT的两种方法
  12. C/C++新手学习项目(三) 魔兽世界之三:开战
  13. Java线程池几个参数的理解
  14. 美国的工会制度——Google成立工会背后
  15. Redis典型应用场景实战之抢红包系统
  16. 案例精选 | 志翔科技:安全与高效并重 构筑芯片行业数据安全堡垒
  17. 自由Android安全研究员陈愉鑫:移动App灰色产业案例分析与防范
  18. Matlab绘制垂直的直线图
  19. python中explode是什么意思_python中explode有什么用
  20. springboot大杂烩

热门文章

  1. 计算机网络对等网实验报告,计算机网络实验报告_双机互联
  2. IT业台风警报(一)——望天
  3. 2019蓝桥杯国赛c++ A组
  4. NC打印模板汇总技巧
  5. 百度搜索引擎关键字URL采集爬虫优化行业定投方案高效获得行业流量-代码篇
  6. python中列表中字符串按数字排列
  7. 怎么彻底删除users下的文件夹_什么工具可以有效清理C:\Users\用户名\AppData目录下的文件?...
  8. 服务端渲染技术之Nuxt.js的详细使用
  9. 对于幸福不是悖论的证明,在现代对于幸福探寻
  10. 关于transition过渡的详解