netty教程(三)——NioEventLoopGroup创建

https://juejin.cn/user/1451011081254792/posts

以netty的一个小demo为例(使用的源码版本为4.1.50.Final)

demo里使用到了NioEventLoopGroup ,一路跟进会调用到这个类一个比较复杂的构函数

public NioEventLoopGroup(int nThreads, 
                         Executor executor, 
                         final SelectorProvider selectorProvider,
                         final SelectStrategyFactory selectStrategyFactory) {
    super(nThreads, 
          executor, 
          selectorProvider, 
          selectStrategyFactory, 
          RejectedExecutionHandlers.reject());
}

这里调用到了父类的一个构造函数

protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
    //DEFAULT_EVENT_LOOP_THREADS为两倍cpu核数
    super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}

最终会调用到的MultithreadEventExecutorGroup的一个构造函数

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
    if (nThreads <= 0) {
        //省略
    }

    //executor是线程执行器
    if (executor == null) {
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }

    //nThreads等于2倍cpu核数
    children = new EventExecutor[nThreads];

    for (int i = 0; i < nThreads; i ++) {
        boolean success = false;
        try {
            //创建NioEventLoop
            children[i] = newChild(executor, args);
            success = true;
        } catch (Exception e) {
            //省略
        } finally {
            if (!success) {
                //省略
            }
        }
    }
    //创建线程选择器
    chooser = chooserFactory.newChooser(children);

    //线程终止监听等其他代码,省略
}

可以看到,主要做了三件事

  • 创建线程执行器ThreadPerTaskExecutor
  • 创建2倍cpu核数的NioEventLoop
  • 创建线程选择器

创建ThreadPerTaskExecutor

这个类很简单

public final class ThreadPerTaskExecutor implements Executor {
    private final ThreadFactory threadFactory;

    public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
        this.threadFactory = ObjectUtil.checkNotNull(threadFactory, "threadFactory");
    }

    @Override
    public void execute(Runnable command) {
        threadFactory.newThread(command).start();
    }
}

可以看到,这个类就是通过传入的threadFactory,每次执行任务时调用threadFactory创建线程执行

而这个threadFactory是通过newDefaultThreadFactory这个方法创建的

protected ThreadFactory newDefaultThreadFactory() {
    return new DefaultThreadFactory(getClass());
}

这里会创建一个DefaultThreadFactory类,最终调用到这个类的一个构造函数

public DefaultThreadFactory(String poolName, boolean daemon, int priority, ThreadGroup threadGroup) {
    ObjectUtil.checkNotNull(poolName, "poolName");

    if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
        //省略
    }

    //poolName的值为nioEventLoop
    prefix = poolName + '-' + poolId.incrementAndGet() + '-';
    //daemon为false
    this.daemon = daemon;
    //priority的值为Thread.NORM_PRIORITY
    this.priority = priority;
    this.threadGroup = threadGroup;
}

DefaultThreadFactory#newThread方法如下

public Thread newThread(Runnable r) {
    Thread t = newThread(FastThreadLocalRunnable.wrap(r), prefix + nextId.incrementAndGet());
    try {
        if (t.isDaemon() != daemon) {
            t.setDaemon(daemon);
        }

        if (t.getPriority() != priority) {
            t.setPriority(priority);
        }
    } catch (Exception ignored) {
        // Doesn't matter even if failed to set.
    }
    return t;
}

所以,NioEventLoop创建的线程的命名规则为nioEventLoop-1-xx形式

并且使用的Thread也不是原生的Thread二是FastThreadLocalThread(主要是对ThreadLocal做了优化)

创建NioEventLoop

上面newChild方法最终会调用NioEventLoop的一个构造函数

NioEventLoop(NioEventLoopGroup parent, 
             Executor executor, 
             SelectorProvider selectorProvider,
             SelectStrategy strategy, 
             RejectedExecutionHandler rejectedExecutionHandler,
             EventLoopTaskQueueFactory queueFactory) {
    //调用父类构造函数设置一些成员变量
    super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory),
          rejectedExecutionHandler);
    this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
    this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
    //openSelector就是调用了provider.openSelector()方法
    final SelectorTuple selectorTuple = openSelector();
    this.selector = selectorTuple.selector;
    this.unwrappedSelector = selectorTuple.unwrappedSelector;
}

注意到,这里

  • 一个selector和一个NioEventLoop做唯一绑定
  • newTaskQueue方法创建一个MpscQueue,用于将一些外部任务放到NioEventLoop中执行
  • 保存了executor,便于后续执行任务

创建线程选择器

线程选择器的选择NioEventLoop的逻辑很简单,例如,假设有n个NioEventLoop,则会从第1个开始选择直到第n个,然后再回到第一个继续选择

不过这样一个简单的过程netty也做了优化,

  • 若线程数为2的指数幂,则创建线程选择器PowerOfTwoEventExecutorChooser
  • 否则,创建GenericEventExecutorChooser

这两种线程选择器的主要区别在获取下一个NioEventLoop的方式不同

PowerOfTwoEventExecutorChooser使用了位与操作,即在从NioEventLoop数组中取出一个时,索引计算的方式为 (索引+1)&(数组长度-1)

GenericEventExecutorChooser计算索引的方式则是 Math.abs((索引+1)%数组长度)

所以如果线程数是2的倍数,索引计算速度会更快

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值