导读
原创文章,转载请注明出处。
本文源码地址:netty-source-code-analysis
本文所使用的netty版本4.1.6.Final:带注释的netty源码
EventLoop
在netty中发挥着驱动引擎的作用,本文我们以NioEventLoop
为例分析一下EventLoop
的工作原理。
1 EventLoop线程的创建时机
还记得我们在“服务端启动”和“客户端启动”这两篇文章中都有一个重要操作吗?就是将Channel
注册到EventLoop
上。我们看AbstractChannel
的register
方法,其中有eventLoop.execute
调用,是不是很熟悉。大多数情况下(并非绝对),这里就是EventLoop
线程开始的地方。
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
}
}
}
咱们跟进去execute
方法,这个方法的实现在SingleThreadEventExecutor
中,我们看到这里有一个startThread
方法,看名字就知道,这里是线程真正开始的地方,一起来看看吧。
后面的!addTaskWakesUp && wakesUpForTask(task)
是怎么回事呢?
EventLoop
的实现中,有的EventLoop
实现会阻塞在任务队列上,对于这样的EventLoop
唤醒方法是向任务队列中添加一个比较特殊的任务,这样的EventLoop
中addTaskWakesUp
为ture
。
而有的EventLoop
比如NioEventLoop
不会阻塞在任务队列上,但是会阻塞在selector
上,对于这样的EventLoop
通过调用wakeup
方法唤醒,这样的EventLoop
中addTaskWakesUp
为false
。
public void execute(Runnable task) {
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
addTask(task);
} else {
startThread();
addTask(task);
if (isShutdown() && removeTask(task)) {
reject();
}
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
我们看看SingleThreadEventExecutor
中的wakeup
方法,这里通过向队列中添加一个特殊的task
来唤醒EventLoop
线程。
protected void wakeup(boolean inEventLoop) {
if (!inEventLoop || STATE_UPDATER.get(this) == ST_SHUTTING_DOWN) {
taskQueue.offer(WAKEUP_TASK);
}
}
而NioEventLoop
中覆盖了这个wakeup
方法,通过调用selector.wakeup
方法来唤醒EventLoop
线程,因为wakeup
是个重量级操作,所以netty用了一个AtomicBoolean
类型的wakenUp
变量来减少wakeup
的次数,如果已经被wakeup
了,就不再调用selector.wakeup
。
protected void wakeup(boolean inEventLoop) {
if (!inEventLoop && wakenUp.compareAndSet(false, true)) {
selector.wakeup();
}
}
startThread
方法中首先对EventLoop
的状态做了判断,如果为ST_NOT_STARTED
(未开始)状态,才调用doStartThread
方法,接着跟下去看doStartThread
方法。
private void startThread() {
if (STATE_UPDATER.get(this) == ST_NOT_STARTED) {
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
doStartThread();
}
}
}
doStartThread
接着调用了SingleThreadEventExecutor.this.run()
方法,这个run
方法是抽象的,在这里没有实现。我们重点关注NioEventLoop
,所以我们去看NioEventLoop
中run
方法的实现。
我们前面已经讲过了这个executor
是ThreadPerTaskExecutor
,所以这里调用execute
方法会创建出一个新的线程,这个线程就是EventLoop
线程。
private void doStartThread() {
assert thread == null;
executor.execute(new Runnable() {
@Override
public void run() {
try {