vlambda博客
学习文章列表

全网最通俗易懂的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();
    }
};