SingleThreadEventExecutor.execute() 方法完整解析
方法源码
@Override
public void execute(Runnable task) {
// 1. 参数校验
if (task == null) {
throw new NullPointerException("task");
}
// 2. 判断当前线程是否是 EventLoop 的线程
boolean inEventLoop = inEventLoop();
// 3. 将任务添加到任务队列
addTask(task);
// 4. 如果不是 EventLoop 线程提交的任务
if (!inEventLoop) {
// 4.1 启动 EventLoop 线程(如果还没启动)
startThread();
// 4.2 如果 EventLoop 正在关闭
if (isShutdown()) {
boolean reject = false;
try {
// 尝试从队列中移除刚才添加的任务
if (removeTask(task)) {
reject = true;
}
} catch (UnsupportedOperationException e) {
// 队列不支持移除操作,忽略
}
if (reject) {
// 拒绝执行该任务
reject();
}
}
}
// 5. 唤醒机制(某些特殊情况需要唤醒 EventLoop)
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
逐步详解
步骤 1:参数校验
if (task == null) {
throw new NullPointerException("task");
}
作用: 防止空指针异常,确保传入的任务不为 null。
步骤 2:判断当前线程
boolean inEventLoop = inEventLoop();
作用: 判断当前执行 execute() 方法的线程是否是 EventLoop 内部的线程。
实现:
@Override
public boolean inEventLoop(Thread thread) {
return thread == this.thread;
}
两种情况:
inEventLoop = true:当前线程就是 EventLoop 的线程inEventLoop = false:当前线程是外部线程(如 main 线程)
步骤 3:添加任务到队列
addTask(task);
源码:
protected void addTask(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
if (!offerTask(task)) {
reject(task); // 队列满了,拒绝任务
}
}
final boolean offerTask(Runnable task) {
if (isShutdown()) {
reject(); // 如果已关闭,拒绝任务
}
return taskQueue.offer(task); // 添加到队列
}
关键点:
taskQueue是一个LinkedBlockingQueue,默认容量是 16- 如果队列满了,会调用
reject(task)拒绝任务 - 无论是哪个线程调用 execute(),都会先把任务加入队列
步骤 4:启动线程(关键步骤)
if (!inEventLoop) {
startThread();
// ... 后续处理
}
4.1 startThread() 方法
private void startThread() {
// 只有在未启动状态才会启动
if (state == ST_NOT_STARTED) {
// CAS 操作,确保只有一个线程能启动成功
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
try {
doStartThread();
} catch (Throwable cause) {
// 启动失败,恢复状态
STATE_UPDATER.set(this, ST_NOT_STARTED);
PlatformDependent.throwException(cause);
}
}
}
}
关键点:
- 使用 CAS 保证线程只启动一次
- 状态从
ST_NOT_STARTED变为ST_STARTED - 如果已经启动过,直接返回(什么都不做)
4.2 doStartThread() 方法(核心)
private void doStartThread() {
assert thread == null;
// executor 是 ThreadPerTaskExecutor,每次创建一个新线程
executor.execute(new Runnable() {
@Override
public void run() {
// ===== 关键:将新创建的线程设置为 EventLoop 的线程 =====
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
updateLastExecutionTime();
try {
// ===== 执行 EventLoop 的 run() 方法(死循环)=====
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
// ... 清理工作
}
}
});
}
关键点:
executor.execute()会创建一个新的 Thread- 将这个新线程赋值给
thread变量 - 调用
SingleThreadEventExecutor.this.run(),这是一个抽象方法 - 在
NioEventLoop中,run()方法是一个死循环,不断处理 IO 事件和任务
4.3 NioEventLoop 的 run() 方法
@Override
protected void run() {
for (;;) { // 死循环
try {
// 1. select 操作(可能阻塞)
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.SELECT:
select(wakenUp.getAndSet(false));
if (wakenUp.get()) {
selector.wakeup();
}
default:
}
// 2. 处理 IO 事件
processSelectedKeys();
// 3. 处理任务队列中的任务
runAllTasks();
} catch (Throwable t) {
handleLoopException(t);
}
}
}
这就是 EventLoop 的核心工作循环!
步骤 4.4:关闭状态处理
if (isShutdown()) {
boolean reject = false;
try {
if (removeTask(task)) {
reject = true;
}
} catch (UnsupportedOperationException e) {
// 队列不支持移除操作
}
if (reject) {
reject(); // 抛出 RejectedExecutionException
}
}
作用: 如果 EventLoop 正在关闭,尝试移除刚才添加的任务并拒绝执行。
步骤 5:唤醒机制
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
作用: 在某些情况下,需要唤醒可能正在阻塞的 EventLoop 线程。
wakeup() 方法:
protected void wakeup(boolean inEventLoop) {
if (!inEventLoop || state == ST_SHUTTING_DOWN) {
// 向队列中添加一个唤醒任务
taskQueue.offer(WAKEUP_TASK);
}
}
为什么需要唤醒?
- EventLoop 可能正在
select()操作中阻塞 - 添加了新任务后,需要唤醒它来处理任务
- 通过添加
WAKEUP_TASK或调用selector.wakeup()来唤醒
完整执行流程图
场景 A:main 线程首次提交任务
main 线程
|
|--eventLoop.execute(register0 任务)
|
|--inEventLoop() = false
| (main 线程 != EventLoop 线程)
|
|--addTask(register0)
| [taskQueue: register0]
|
|--startThread()
| |
| |--state == ST_NOT_STARTED
| |
| |--CAS(ST_NOT_STARTED -> ST_STARTED)
| |
| |--doStartThread()
| |
| |--executor.execute(new Runnable)
| |
| |--创建新线程
| |
| +--> EventLoop 线程
| |
| |--thread = Thread.currentThread()
| |
| |--run() 死循环开始
| |
| |--for (;;) {
| | select();
| | processSelectedKeys();
| | runAllTasks(); // 执行 register0
| | }
|
|--返回(不等待任务执行完成)
场景 B:EventLoop 线程自己提交任务
EventLoop 线程
|
|--正在执行某个 Handler
|
|--eventLoop.execute(新任务)
|
|--inEventLoop() = true
| (当前线程 == EventLoop 线程)
|
|--addTask(新任务)
| [taskQueue: 新任务]
|
|--不调用 startThread()
| (因为自己已经在运行)
|
|--继续执行当前代码
|
|--当前任务完成
|
|--回到 run() 循环
|
|--runAllTasks()
| |
| |--从 taskQueue 取出任务
| |
| |--执行新任务
关键属性说明
1. taskQueue
private final Queue<Runnable> taskQueue;
// 初始化
protected Queue<Runnable> newTaskQueue(int maxPendingTasks) {
return new LinkedBlockingQueue<Runnable>(maxPendingTasks);
}
作用: 存放待执行的任务,默认容量 16。
2. thread
private volatile Thread thread;
作用: EventLoop 内部的线程,第一次调用 execute() 时创建。
3. executor
private final Executor executor;
// 初始化时传入 ThreadPerTaskExecutor
public SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor, ...) {
this.executor = ThreadExecutorMap.apply(executor, this);
}
ThreadPerTaskExecutor 的实现:
public final class ThreadPerTaskExecutor implements Executor {
private final ThreadFactory threadFactory;
@Override
public void execute(Runnable command) {
// 每次创建一个新线程
threadFactory.newThread(command).start();
}
}
作用: 用于创建 EventLoop 的线程。
4. state
private volatile int state = ST_NOT_STARTED;
// 状态常量
private static final int ST_NOT_STARTED = 1;
private static final int ST_STARTED = 2;
private static final int ST_SHUTTING_DOWN = 3;
private static final int ST_SHUTDOWN = 4;
private static final int ST_TERMINATED = 5;
作用: 记录 EventLoop 的状态。
为什么这样设计?
1. 懒加载线程
// 不在构造方法中创建线程
public NioEventLoop(...) {
// 只初始化属性,不创建线程
}
// 在第一次提交任务时才创建线程
eventLoop.execute(task); // 这时才创建线程
好处:
- 节省资源:如果 EventLoop 没有被使用,就不会创建线程
- 延迟初始化:只在需要时才创建
2. 单线程模型
// 一个 EventLoop 只有一个线程
private volatile Thread thread;
// 所有任务都在这个线程中执行
protected void run() {
for (;;) {
runAllTasks(); // 串行执行
}
}
好处:
- 线程安全:避免并发问题
- 无锁设计:不需要加锁
- 高性能:减少线程切换
3. 任务队列化
// 所有任务先进入队列
addTask(task);
// 然后由 EventLoop 线程统一处理
runAllTasks();
好处:
- 解耦:提交任务和执行任务分离
- 异步:提交任务立即返回,不阻塞
- 有序:保证任务按顺序执行
实际应用示例
示例 1:服务端启动
// main 线程
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
ServerBootstrap b = new ServerBootstrap();
ChannelFuture f = b.bind(port).sync();
// 执行流程:
// 1. main 线程调用 bind()
// 2. 内部调用 eventLoop.execute(register0)
// 3. 创建 EventLoop 线程
// 4. EventLoop 线程执行 register0
// 5. 完成注册
示例 2:Handler 中提交任务
public class MyHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 当前线程:EventLoop 线程
// 提交一个延迟任务
ctx.channel().eventLoop().execute(() -> {
// 这个任务会在当前 EventLoop 线程中执行
System.out.println("延迟任务");
});
// 或者提交一个定时任务
ctx.channel().eventLoop().schedule(() -> {
System.out.println("定时任务");
}, 5, TimeUnit.SECONDS);
}
}
示例 3:业务线程提交任务
// 业务线程池
ExecutorService businessPool = Executors.newFixedThreadPool(10);
businessPool.submit(() -> {
// 当前线程:业务线程
// 向 Channel 写数据
channel.writeAndFlush(msg);
// 内部会调用 eventLoop.execute()
// 确保写操作在 EventLoop 线程中执行
});
总结
execute() 方法的核心逻辑
- 判断线程:当前线程是否是 EventLoop 线程
- 加入队列:将任务添加到 taskQueue
- 启动线程:如果是外部线程调用,启动 EventLoop 线程
- 异步执行:EventLoop 线程从队列中取任务执行
关键设计
- 懒加载:第一次提交任务时才创建线程
- 单线程:一个 EventLoop 只有一个线程
- 队列化:所有任务进入队列,串行执行
- 线程安全:通过单线程避免并发问题
1722



被折叠的 条评论
为什么被折叠?



