Netty 第八篇:doBind0() 与 EventLoop 的线程启动
📅 2026-05-22 ✍️ 原创 🏷️ Java / NIO / Netty / 源码分析 / doBind0
在上一篇文章中,我们详细分析了 Channel 是如何注册到 Selector 上的。注册完成后,服务端还不能接收连接,因为我们还没有真正监听端口。
这一篇,我们来看看 bind(8080) 的最后一步:doBind0()。这一步不仅会真正监听端口,还会揭开 EventLoop 线程是如何启动的这个谜题。
一、doBind0() 的入口
在 AbstractBootstrap 中,注册完成后会调用 doBind0:
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
二、SingleThreadEventExecutor 的核心逻辑
当我们调用 channel.eventLoop().execute() 时,实际上是调用了 SingleThreadEventExecutor.execute()。
@Override
public void execute(Runnable task) {
boolean inEventLoop = inEventLoop();
addTask(task);
if (!inEventLoop) {
startThread();
}
}
三、doStartThread():线程的诞生
这是 Netty 线程模型的灵魂所在。在 doStartThread() 中,Netty 并没有直接 new Thread(),而是使用了 executor。
private void doStartThread() {
executor.execute(new Runnable() {
@Override
public void run() {
SingleThreadEventExecutor.this.thread = Thread.currentThread();
SingleThreadEventExecutor.this.run();
}
});
}
这里的 executor 通常是 ThreadPerTaskExecutor,它的作用就是每次执行任务时创建一个新的线程。SingleThreadEventExecutor.this.thread = Thread.currentThread();这里完成了EventLoop与线程的绑定。
而SingleThreadEventExecutor.this.run();会进入死循环,用于处理当前EventLoop中的IO事件以及任务。这一点在下一章说明。
四、bind0 任务的执行
当 EventLoop 线程启动后,它就会从任务队列里取出我们在 doBind0 中提交的那个任务并执行:
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise);
}
五、Pipeline 的 bind 传播
channel.bind() 会调用 pipeline.bind()。Pipeline 的传播顺序是从尾到头,最终传播到HeadContext:
@Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
tail.bind(localAddress, promise);
return promise;
}
六、HeadContext 与 Unsafe
在 HeadContext 中,真正的绑定逻辑交给了 unsafe:
@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
unsafe.bind(localAddress, promise);
}
七、NioServerSocketChannel.doBind()
终于,我们来到了最底层。在 AbstractUnsafe.bind() 中,会调用 AbstractChannel的doBind()方法,这是一个抽象方法,最终执行类为NioServerSocketChannel。
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
javaChannel().bind(localAddress, config.getBacklog());
}
八、总结
| 阶段 | 核心动作 |
|---|---|
| 提交任务 | 主线程向 EventLoop 提交 bind 任务 |
| 启动线程 | SingleThreadEventExecutor.startThread() |
| 绑定线程 | 将ThreadPerTaskExecutor生成的线程赋值给EventLoop.thread并启动run方法处理IO事件与任务 |
| Pipeline 传播 | 调用bind方法,传播路径:Channel→Pipeline→Tail → Head→Unsafe→AbstractChannel→NioServerSocketChannel |
| JDK 绑定 | ServerSocketChannel.bind() |
— 原创文章,转载请注明出处 —

浙公网安备 33010602011771号