美文网首页
Netty学习笔记(四)NioEventLoopGroup分析

Netty学习笔记(四)NioEventLoopGroup分析

作者: 云师兄 | 来源:发表于2018-11-10 15:00 被阅读20次

    引子

    在上一篇文章中我们已经分析了Netty注册相关的逻辑,接下来再开看下NioEventLoopGroup这个类内部的相关构造。

    NioEventLoopGroup

    在之前的demo中第一行就是:

    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    

    这个NioEventLoopGroup类的构造函数最终实现如下:

    protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                                EventExecutorChooserFactory chooserFactory, Object... args) {
            if (executor == null) {
                executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
            }
            children = new EventExecutor[nThreads];
            for (int i = 0; i < nThreads; i ++) {
                boolean success = false;
                try {
                    children[i] = newChild(executor, args);
                    success = true;
                    ......
                    }
            }
        }
    

    其中传入的参数Executor对象就是多线程中的线程池接口的实现类(关于这块内容我特定又回过头去看多线程相关的知识,具体可以看另外一篇关于java多线程的文章)。接着来看一开始这个线程池对象是空的,就开始创建ThreadPerTaskExecutor实例,
    我们首先就从这个ThreadPerTaskExecutor对象开始分析。

    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();
        }
    }
    

    从代码就可以看出ThreadPerTaskExecutor就是一个一个实现了Executor线程池的实现类,
    execute方法内部开始调用线程工厂的newThread方法创建线程。当后续执行线程工厂的execute方法时就会创建一个新的线程。
    传入的参数是一个newDefaultThreadFactory实例,这个对象实现了ThreadFactory工厂接口,所以就是一个线程工厂类,它的构造函数实现如下:

        public DefaultThreadFactory(String poolName, boolean daemon, int priority, ThreadGroup threadGroup) {
            if (poolName == null) {
                throw new NullPointerException("poolName");
            }
            if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
                throw ......
            }
            prefix = poolName + '-' + poolId.incrementAndGet() + '-';
            this.daemon = daemon;
            this.priority = priority;
            this.threadGroup = threadGroup;
        }
    

    看到这我们先对上面的内容做个小结:

    • 从这两段代码可以看出ThreadPerTaskExecutor()这个方法做的就是创建一个线程池,并且线程池的优先级为Thread.NORM_PRIORITY,即5(优先级越大越优先执行,5是普通的优先级)。
    • 每当new一个NioEventLoopGroup对象的时候,实际上内部就会先创建一个线程池。

    newChild()方法分析

    上面讲到executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());方法创建了一个线程池。接下来就执行了:

            for (int i = 0; i < nThreads; i ++) {
                boolean success = false;
                try {
                    children[i] = newChild(executor, args);
                    success = true;
                ......
    

    这个newChild方法实现如下:

        @Override
        protected EventLoop newChild(Executor executor, Object... args) throws Exception {
            return new NioEventLoop(this, executor, (SelectorProvider) args[0],
                ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
        }
    
        NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                     SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
            super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
            if (selectorProvider == null) {
                throw new NullPointerException("selectorProvider");
            }
            if (strategy == null) {
                throw new NullPointerException("selectStrategy");
            }
            provider = selectorProvider;
            selector = openSelector();
            selectStrategy = strategy;
        }
    

    从源码中可以看出newChild方法创建了一个NioEventLoop对象。构造方法中除了给变量赋值外,又调了父类的构造方法,继续往下看:

        protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                            boolean addTaskWakesUp, int maxPendingTasks,
                                            RejectedExecutionHandler rejectedHandler) {
            super(parent);
            this.addTaskWakesUp = addTaskWakesUp;
            this.maxPendingTasks = Math.max(16, maxPendingTasks);
            this.executor = ObjectUtil.checkNotNull(executor, "executor");
            taskQueue = newTaskQueue(this.maxPendingTasks);
            rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
        }
    

    从这两部分看出newChild方法主要是创建了一个NioEventLoop对象,这个对象内部保存了provider,selector,selectStrategy以及一个taskQueue队列。这里的provider就是Java NIO中的选择器provider,selector就是选择器,至于这些东西具体是怎么用的,这里我还是疑惑的,要后面用到的地方继续分析了。

    newChooser

    接下来看NioEventLoopGroup构造函数的第三部分:

            chooser = chooserFactory.newChooser(children);
    

    这里传入的children数组就是上面讲到的创建的NioEventLoop数组,但是返回的这个chooser是个什么鬼,又不懂了,真是头疼~。所以这里先保留这个疑问吧,只能后续学习明白了再回头补了。

    相关文章

      网友评论

          本文标题:Netty学习笔记(四)NioEventLoopGroup分析

          本文链接:https://www.haomeiwen.com/subject/ghuzxqtx.html