在Netty网络编程中,不管是编写客户端代码,还是编写服务端代码,我们都写过这段代码
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
在Netty的线程模型中,NioEventLoopGroup扮演着线程池角色,一说到线程池,不由自主地地会想到ThreadPoolExecutor,NioEventLoopGroup是不是对ThreadPoolExecutor的二次封装了下,我们从下面几个方向来分析。
NioEventLoopGroup源码分析思维导图
NioEventLoopGroup层次结构图
从层次结构图上可以看出,NioEventLoopGroup实现了线程池模型的ScheduledExecutorService,ExecutorService和Executor接口,因此可知NioEventLoopGroup提供了线程池相关功能。
NioEventLoopGroup继承了MultithreadEventLoopGroup,父类MultithreadEventLoopGroup继承了抽象类
MultithreadEventExecutorGroup,其抽象方法newChild由子类实现,使用了模板设计模式,一步一步跟踪,最终定位到
MultithreadEventExecutorGroup的构造函数,初始化时做了3件事:
创建线程工厂并构建线程执行器Executor
创建指定数量nThreads的children线程组并初始化
创建线程选择器chooser
创建线程工厂并构建线程执行器Executor:如果我们没有传executor,会帮忙创建一个线程工厂DefaultThreadFactory来构建线程执行器ThreadPerTaskExecutor。DefaultThreadFactory会创建FastThreadLocalThread线程来执行任务。
if (executor == null) { executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()); }
创建指定数量nThreads的children线程组并初始化:第一步创建指定数量nThreads的线程组。nThreads没有传或者传0,会默认指定2*cpu线程数。
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) { //nThreads为0时,会设置2*cpu线程数 super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args); }static { //默认线程数:2*cpu DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt( "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); if (logger.isDebugEnabled()) { logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS); } }
第二步通过newChild()方法初始化children线程组,newChild()是个抽象方法,具体实现由子类NioEventLoopGroup完成。在newChild()方法中,NioEventLoop的初始化参数有6个:
参数parent:NioEventLoopGroup线程组本身;
参数executor:线程执行器,用于启动线程,在SingleThreadEventExecutor的doStartThread()方法中被调用;
参数selectorProvider:NIO的Selector选择器的提供者;
参数strategy:主要在NioEventLoop的run()方法中用于控制选择循环;
参数rejectedExecutionHandler:非I/O任务提交被拒绝时的处理Handler;
参数queueFactory:队列工厂,在NioEventLoop中,队列读是单线程操作,而队列写则可能是多线程操作,使用支持多生产者、单消费者的队列比较合适,默认为MpscChunkedArrayQueue队列。
//指定数量nThreads的children线程组children = new EventExecutor[nThreads];for (int i = 0; i < nThreads; i ++) { boolean success = false; try { // 初始化线程组children,调用子类NioEventLoopGroup的newChild() children[i] = newChild(executor, args); success = true; } catch (Exception e) { // TODO: Think about if this is a good exception type throw new IllegalStateException("failed to create a child event loop", e); } ...... }
创建线程选择器chooser:默认用的是
DefaultEventExecutorChooserFactory.INSTANCE,根据children线程组数量决定使用不同的选择器,使用了单例模式和工厂模式。
chooser = chooserFactory.newChooser(children);
java线程池的队列是所有线程共享的,NioEventLoopGroup线程组的队列是每个线程独享的。
NioEventLoopGroup线程组没有最大线程数的概念。
NioEventLoopGroup线程组有线程选择器chooser的概念。
NioEventLoopGroup线程组图
模板方法模式:MultithreadEventExecutorGroup类中抽象方法newChild()
工厂模式:DefaultSelectStrategyFactory,DefaultEventExecutorChooserFactory
单例模式:DefaultSelectStrategyFactory,DefaultSelectStrategy,DefaultEventExecutorChooserFactory
策略模式:DefaultSelectStrategy
用上面的设计模式,可以把变化的地方变得容易扩展
Java定义好的线程池模型接口,我们可以重用,还可以根据自己的业务特征实现自己的线程池
接口EventExecutorGroup重写父类接口ExecutorService,为了提示作用
NioEventLoopGroup中for (EventExecutor e: this),for-each只能适用于数组或者实现Iterable接口。