本篇文章是对《Netty In Action》一书第七章"EventLoop和线程模型"的学习摘记,主要内容为线程模型的概述、事件循环的概念和实现、任务调度和实现细节
线程模型指定了操作系统、编程语言、框架或者应用程序的上下文中的线程管理的关键方面。显而易见地,线程模型确定了代码的执行方式,如何以及何时创建线程将对应用程序代码的执行产生显著的影响,因此开发人员需要理解与不同模型相关的权衡
因为具有多核心或多个 CPU 的计算机现在已经司空见惯,大多数的现代应用程序都利用了复杂的多线程处理技术以有效地利用系统资源,所以我们必须规避并发执行可能会带来的副作用
在早期的 Java 语言中,我们使用多线程处理的主要方式无非是按需创建和启动新的 Thread 来执行并发的任务单元 —— 一种在高负载下工作得很差的原始方式。Java 5 随后引入了 Executor API(Java 的并发 API —— java.util.concurrent),其线程池通过缓存和重用 Thread极大地提高了性能,基本的线程池化模式可以描述为:
从池的空闲线程列表中选择一个 Thread,并且指派它去运行一个已提交的任务(一个 Runnable的实现);
当任务完成时,将该Thread返回给该列表,使其可被重用
java.util.concurrent
public interface Executor
An object that executes submitted Runnable tasks. This interface provides a way of decoupling task submission from the mechanics of how each task will be run, including details of thread use, scheduling, etc. An Executor is normally used instead of explicitly creating threads.
虽然池化和重用线程相对于简单地为每个任务都创建和销毁线程是一种进步,但是它并不能消除由上下文切换所带来的开销,它的性能瓶颈随着线程数量的增加和负载的增大很快变得明显
运行任务来处理在连接的生命周期内发生的事件是任何网络框架的基本功能。与之相应的编程上的构造通常被称为事件循环 —— 一个 Netty 使用了 interface io.netty.channel. EventLoop来适配的术语
在前面的"深入理解Netty核心组件"的文章中,我们提供了下面这张图:
图中标注的④、⑤、⑥和⑦便体现了事件循环的基本思想,在Netty中使用了AtomicBoolean类型的变量wakenUp作为超时标记
Boolean that controls determines if a blocked Selector.select should break out of its selection process. In our case we use a timeout for the select method and the select method will block for that time unless waken up.
事件循环中执行任务的一个极简化的代码如下:
boolean terminated = true;
//...
while (!terminated) {
//阻塞,直到有事件已经就绪可被运行
List<Runnable> readyEvents = blockUntilEventsReady();
for (Runnable ev: readyEvents) {
//循环遍历,并处理所有的事件
ev.run();
}
}
Netty 的 EventLoop采用了两个基本的 API:并发和网络编程
首先,io.netty.util.concurrent 包构建在 JDK 的 java.util.concurrent 包上,用来提供线程执行器
其次,io.netty.channel包中的类,为了与Channel的事件进行交互, 扩展了这些接口/类
一个 EventLoop 将由一个永远都不会改变的 Thread 驱动,同时任务 (Runnable 或者 Callable)可以直接提交给 EventLoop 实现,以立即执行或者调度执行。
根据配置和可用核心的不同,可能会创建多个EventLoop实例用以优化资源的使用,并且单个 EventLoop可能会被指派用于服务多个Channel
事件和任务是以先进先出(FIFO)的顺序执行的。这样可以通过保证字 节内容总是按正确的顺序被处理,消除潜在的数据损坏的可能性,例如在SingleThreadEventLoop中定义了Queue<Runnable>类型的tailTasks变量来添加需要执行的任务
?
由 I/O 操作触发的事件将流经安装了一个或者多个 ChannelHandler的ChannelPipeline。传播这些事件的方法调用可以随后被ChannelHandler所拦截,并且可以按需地处理事件
并且所有的I/O操作和事件都由已经被分配给了 EventLoop的那个Thread来处理
而在以前的版本中所使用的线程模型,只保证了入站(之前称为上游)事件会在所谓的 I/O 线程(对应于 Netty 4 中的EventLoop)中执行。所有的出站(下游)事件都由调用线程处理,其可能是 I/O 线程也可能是别的线程
?
?
?
?
?
?
?
?
?
?
?
?
原文:https://www.cnblogs.com/kuluo/p/12653731.html