通过源码分析-Netty默认起多少线程?何时启动?

    如果你在简历上写了Netty,那么面试官百分之九十的可能会问你Netty默认其多少线程?在什么时候启动的问题。面试官一方面是想考验你对Netty有没有最基本的知识点掌握,一方面是想试探你有没有深入了解过Netty的源码和启动流程。

    你在编写Netty服务端的时候经常会编写下面的代码:

EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();

    这里构建了两个事件循环组,循环组具体是干什么的我就暂时不细说了。我们拿其中的一个来进行分析底层的源码即可。这里调用了无参的构造方法,也就使用了默认的线程数,那么默认线程数是多少呢?慢慢进行分析:

public NioEventLoopGroup() {
    this(0);
}

    调用了“this(0)”,也就是调用了单个参数的构造方法,然后调用的传递线程数是0。

public NioEventLoopGroup(int nThreads) {
    this(nThreads, (Executor) null);
}

    紧接着调用 线程数和线程池接口的方法,需要注意的是,这里的“executor”是null。


public NioEventLoopGroup(int nThreads, Executor executor) {
    this(nThreads, executor, SelectorProvider.provider());
}

    因为Netty底层封装了NIO所以调用了传递“provider”的构造参数。


public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider) {
    this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}

    紧接着比之前的多了一个默认选择策略工厂,传递了一个单例对象。


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) {
    super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}

    紧接着又调用了父类的“MultithreadEventLoopGroup”构造方法,下面这个就是默认初始化的线程了:


nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads

    这里是一个三元表达式,判断传递过来的参数“nThreads”是否等于0,如果是的话就赋值“DEFAULT_EVENT_LOOP_THREADS”,如果不是的话就赋值为传递过来的值。

    那么“DEFAULT_EVENT_LOOP_THREADS”是在什么时候初始化的呢?是在静态代码块中初始化。

private static final int DEFAULT_EVENT_LOOP_THREADS;
static {
    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);
    }
}

    它获取的值也就是“NettyRuntime.availableProcessors() * 2”。然后调用了“availableProcessors”的方法:

public static int availableProcessors()
{
    return holder.availableProcessors();
}
synchronized int availableProcessors() {
    if (this.availableProcessors == 0) {
        final int availableProcessors =SystemPropertyUtil.getInt(
                        "io.netty.availableProcessors",
                        Runtime.getRuntime().availableProcessors());
        setAvailableProcessors(availableProcessors);
    }
    return this.availableProcessors;
}

    需要注意的就是“Runtime.getRuntime().availableProcessors()”这个方法,这个方法的意思是获取运行时可用线程数。接下来下来写个方法测试一下:

public class NettyServer {
    public static void main(String[] args) {
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

    输出的值是:

12

    打开我的电脑的配置,可以看到我的处理器一共12线程:
在这里插入图片描述

    因此,Netty默认构建的线程数的是电脑线程数的2倍。那它是在什么时候启动的呢?是在“bind”的时候启动的!


server.bind(9999).sync();

    我们继续跟进“bind”方法,查看底层做了些什么。

public ChannelFuture bind(int inetPort) {
    return bind(new InetSocketAddress(inetPort));
}

    “validate()”是一些简单的校验,不是很重要,可以略过。直接看“doBind”。

public ChannelFuture bind(SocketAddress localAddress) {
    validate();
    return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));
}

    “doBind”里面方法比较多,重要的是“doBind0”
在这里插入图片描述
    继续跟进“doBind0”可以发现调用了“channel.eventLoop.execute”。看到这里是不是感觉很亲切,这不就是线程池构建线程的方法吗?
在这里插入图片描述

    由此可见,Netty的默认启动了电脑可用线程数的两倍,在调用了bind方法的时候执行。

    那么反过来思考下!我们真的需要每个EventLoopGroup都默认启用线程吗?答案当然不是!我们具体的线程数应该交由业务来决定而不是使用默认的参数。有的时候我们只需要使用一个线程来监听注册事件。这个在写客户端的时候比较突出明显。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值