全网最通俗易懂的netty源码--EventLoopGroup的核心构造参数
上一节内容在分析NioEventLoopGroup的初始化过程时遇到了一些参数,这几个参数非常重要。但是为了突出初始化的整体逻辑,没有对这几个参数具体介绍。本节内容就是在上节对初始化过程有个宏观把握的基础,进一步深入的分析这几个参数。
1、nThreads:
线程数,也就是NioEventLoop的实例数量。如果采用无参构造函数,默认nThreads会被设置为CPU核心数 * 2,可以看下DEFAULT_EVENT_LOOP_THREADS的赋值。
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
2、executor
从名字上看executor是线程池。但我们要构造的EventLoopGroup本身就是一个线程池,为什么还传一个
executor对象呢?这个其实是给NioEventLoop用的。
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
点击ThreadPerTaskExecutor进入如下代码
public final class ThreadPerTaskExecutor implements Executor {
private final ThreadFactory threadFactory;
public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
if (threadFactory == null) {
throw new NullPointerException("threadFactory");
}
this.threadFactory = threadFactory;
}
@Override
public void execute(Runnable command) {
threadFactory.newThread(command).start(); //为每个任务新建一个线程
}
}
Executor 作为线程池的最顶层接口, 只有一个 execute(runnable) 方法。ThreadPerTaskExecutor是netty封装的实现类, 从上可知,每来一个任务,新建一个线程。
3、chooserFactory
当提交一个任务到线程池时,线程池会选择一个线程来执行,怎么选择呢?chooserFactory就是用来实现选择策略。默认值为
chooserFactory=DefaultEventExecutorChooserFactory.INSTANCE
进一步追踪如下:
public final class DefaultEventExecutorChooserFactory
implements EventExecutorChooserFactory {
//饿汉式单例
public static final DefaultEventExecutorChooserFactory INSTANCE
= new DefaultEventExecutorChooserFactory();
//构造函数私有
private DefaultEventExecutorChooserFactory() { }
//如果线程数是2^n,则返回PowerOfTwoEventExecutorChooser内部类;否则,返回GenericEventExecutorChooser内部类。
public EventExecutorChooser newChooser(EventExecutor[] executors) {
if (isPowerOfTwo(executors.length)) {
return new PowerOfTwoEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
}
。。。。。。。
private static final class PowerOfTwoEventExecutorChooser
implements EventExecutorChooser {
@Override
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
}
private static final class GenericEventExecutorChooser
implements EventExecutorChooser {
@Override
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
}
}
总结如下
1)如果线程池的线程数量是 2^n,则采用位运算
2)如果不是,用取模的方式
4、selectorProvider:
默认值如下:
selectorProvider = SelectorProvider.provider()
调用JDK提供的方法来实例化Selector,每个NioEventLoopGroup持有一个selectorProvider实例。
5、selectStrategyFactory
默认值如下:
selectStrategyFactory = DefaultSelectStrategyFactory.INSTANCE
这个参数主要用于线程在select操作和执行任务过程中的策略选择问题。在NioEventLoop的run()方法中有如下这段代码
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.SELECT:
// 如果 !hasTasks(),那么进到这个 select 分支,这里 select 带阻塞的
select(wakenUp.getAndSet(false));
if (wakenUp.get()) {
selector.wakeup();
}
default:
}
总结:
selectStrategy有两个值:CONTINUE和SELECT。
1.如果taskQueue不为空,也就是hasTasks()返回true,那么执行selectNow(),该方法不会阻塞。
2.如果taskQueue为空,即hasTasks()返回false,则执行SelectStrategy.SELECT 分支,进行select(…),是阻塞的。
其实就是按照是否有任务在排队来决定是否可以进行阻塞。
6、rejectedExecutionHandler
拒绝策略,JDK原生的线程池也有这个参数。在Netty中稍微有点不一样。默认值为:
rejectedExecutionHandler = RejectedExecutionHandlers.reject()
点进去看下reject()方法,可看到Netty的默认拒绝策略是:抛出异常。
private static final RejectedExecutionHandler REJECT = new RejectedExecutionHandler() {
@Override
public void rejected(Runnable task, SingleThreadEventExecutor executor) {
throw new RejectedExecutionException();
}
};