回顾
在上一节,我尝试追踪NioEventLoopGroup的构造函数,然后经历了一层层套娃,最终到达了MultithreadEventExecutorGroup的一个构造函数中,下面再贴一次代码+坐标:
此处【坐标1】
这节要讲的就是new ThreadPerTaskExecutor这个构造函数。
- new ThreadPerTaskExecutor - 创建NioEventLoop执行任务线程的线程执行器。
Netty Version: 4.1.6
ThreadPerTaskExcutor
首先起点就是上面的【坐标1】构造方法,然后先进入ThreadPerTaskExecutor的构造方法看看:
- 将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)
- 可以证实,之前的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都会自增。