回顾

在上一节,我尝试追踪NioEventLoopGroup的构造函数,然后经历了一层层套娃,最终到达了MultithreadEventExecutorGroup的一个构造函数中,下面再贴一次代码+坐标:
MultithreadEventExecutorGroup.png

此处【坐标1】
MultithreadEventExecutorGroup构造函数.png

这节要讲的就是new ThreadPerTaskExecutor这个构造函数。

  • new ThreadPerTaskExecutor - 创建NioEventLoop执行任务线程的线程执行器。

Netty Version: 4.1.6


ThreadPerTaskExcutor

首先起点就是上面的【坐标1】构造方法,然后先进入ThreadPerTaskExecutor的构造方法看看:

ThreadPerTaskExecutor.png

  • 将threadFactory实例保存下来了。
  • execute方法每接到一个任务都会开辟新的线程去执行

那么这个threadFactory到底是啥呢?它的newThread返回的又是什么线程?线程的命名规则又是如何?不急,接着走下去就知道答案了。

先将视角切回本篇【坐标1】,我们进入newDefaultThreadFactory(),此处【坐标3】:
io.netty.util.concurrent.MultithreadEventExecutorGroup#newDefaultThreadFactory

    protected ThreadFactory newDefaultThreadFactory() {
        return new DefaultThreadFactory(getClass());
    }
  • 这里的getClass()就是NioEventLoopGroup,等下去到命名规则就揭晓了。

继续,进入new DefaultThreadFactory:
io.netty.util.concurrent.DefaultThreadFactory#DefaultThreadFactory(java.lang.Class<?>)

    public DefaultThreadFactory(Class<?> poolType) {
        this(poolType, false, Thread.NORM_PRIORITY);
    }

继续,进入this,此处【坐标2】:
io.netty.util.concurrent.DefaultThreadFactory#DefaultThreadFactory(java.lang.Class<?>, boolean, int)

    public DefaultThreadFactory(Class<?> poolType, boolean daemon, int priority) {
        this(toPoolName(poolType), daemon, priority);
    }

来到这里我们先不急着继续进入this,可以看看toPoolName方法,它就是给线程起名字前缀的:
io.netty.util.concurrent.DefaultThreadFactory#DefaultThreadFactory(java.lang.Class<?>, boolean, int)

toPoolName.png

  • 可以证实,之前的getClass()就是NioEventLoopGroup。
  • 转换之后返回nioEventLoopGroup(其实就是头字母变小写)

现在,视角再切回【坐标2】,继续进入this:
io.netty.util.concurrent.DefaultThreadFactory#DefaultThreadFactory(java.lang.String, boolean, int)

    public DefaultThreadFactory(String poolName, boolean daemon, int priority) {
        this(poolName, daemon, priority, System.getSecurityManager() == null ?
                Thread.currentThread().getThreadGroup() : System.getSecurityManager().getThreadGroup());
    }

继续,进入this:
io.netty.util.concurrent.DefaultThreadFactory#DefaultThreadFactory(java.lang.String, boolean, int, java.lang.ThreadGroup)

    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 new IllegalArgumentException(
                    "priority: " + priority + " (expected: Thread.MIN_PRIORITY <= priority <= Thread.MAX_PRIORITY)");
        }

        prefix = poolName + '-' + poolId.incrementAndGet() + '-';
        this.daemon = daemon;
        this.priority = priority;
        this.threadGroup = threadGroup;
    }
  • 到此完成构造。
  • 线程命名前缀为nioEventLoopGroup-poolId-,poolId类型是AtomicInteger(提供线程安全操作),每次设置前缀时都会自增1。


上面看完构造函数之后不用急着离开,因为当前类还有个核心的方法,即上面【坐标3】的newThred方法就在这个类中,我们来看一下:

io.netty.util.concurrent.DefaultThreadFactory#newThread(java.lang.Runnable)

    public Thread newThread(Runnable r) {
        Thread t = newThread(new DefaultRunnableDecorator(r), prefix + nextId.incrementAndGet());
        try {
            if (t.isDaemon()) {
                if (!daemon) {
                    t.setDaemon(false);
                }
            } else {
                if (daemon) {
                    t.setDaemon(true);
                }
            }

            if (t.getPriority() != priority) {
                t.setPriority(priority);
            }
        } catch (Exception ignored) {
            // Doesn't matter even if failed to set.
        }
        return t;
    }
  • 这个newThread会在之后的这篇中用到
  • 这个newThread中的核心方法也叫newThread。
  • 并且这里能得到完整的线程名称,即nioEventLoopGroup-prefix-nextId。

继续,进入newThread方法:
io.netty.util.concurrent.DefaultThreadFactory#newThread(java.lang.Runnable, java.lang.String)

    protected Thread newThread(Runnable r, String name) {
        return new FastThreadLocalThread(threadGroup, r, name);
    }
  • 可以发现这里其实new了一个FastThreadLocalThread,它是经过Netty封装的一个类。

关于FastThreadLocalThread据说是做了一些优化,至于是怎么优化的先不探究,这里就简单看下类关系图:



小结

  • new ThreadPerTaskExecutor就是给返回一个线程的创建器,NioEventLoop可以把接受到的任务通过创建器的execute方法(execute又调用new Thread)创建FastThreadLocalThread线程执行任务。
  • 另外,我们还知道了NioEventLoopGroup线程的命名规则:nioEventLoopGroup-prefix-nextId。其中prefix、nextId都会自增。