文章目錄
- 前言
- 原了解析
- 總結
前言
在NIO程式設計中,Server 通常會寫一個無限循環的代碼,Selector 持續監聽新的事件:
//NIO
System.out.println("等待連接配接....");
while (true){
selector.select();
//處理 selector.selectedKeys 事件
}
但是Netty程式隻需要啟動 ServerBootstrap,然後就開啟監聽事件了,那它是如何處理的?
//Netty
ChannelFuture future = serverBootstrap.bind(7000).sync();
System.out.println("伺服器已啟動,端口:7000");
原了解析
上一篇文章我們介紹了 ServerBootstrap啟動過程全解析 ,其中講到說 ServerBootstrap 啟動的時候會将 NioServerSocketChannel 注冊到 BossGroup 内部的 NioEventLoop,這個步驟是由 NioEventLoop 内部開啟的線程異步執行,如下圖紅框部分所示:

答案也就在這裡,外部調用 eventLoop.execute(new Runnable(){ … }) 在EventLoop 獨立線程執行任務。EventLoop 是一個單線程的線程池,内部維護一個 Thread 對象,優先判斷線程是否啟動了,如果沒有就建立新線程,反之不處理。
//SingleThreadEventExecutor.java
private void execute(Runnable task, boolean immediate) {
boolean inEventLoop = inEventLoop();
addTask(task);
//這裡判斷目前線程是否EventLoop開啟的線程
if (!inEventLoop) {
//這句是關鍵
startThread();
//以下省略...
}
}
在 startThread() 内部有一句 SingleThreadEventExecutor.this.run(),該方法就是無限循環監聽的實作(這裡删掉了部分無關代碼):
protected void run() {
int selectCnt = 0;
for (;;) {
strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
switch (strategy) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.BUSY_WAIT:
// fall-through to SELECT since the busy-wait is not supported with NIO
case SelectStrategy.SELECT:
long curDeadlineNanos = nextScheduledTaskDeadlineNanos();
if (curDeadlineNanos == -1L) {
curDeadlineNanos = NONE; // nothing on the calendar
}
nextWakeupNanos.set(curDeadlineNanos);
try {
if (!hasTasks()) {
strategy = select(curDeadlineNanos);
}
} finally {
nextWakeupNanos.lazySet(AWAKE);
}
selectCnt++;
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio;
boolean ranTasks;
if (ioRatio == 100) {
try {
if (strategy > 0) {
processSelectedKeys();
}
} finally {
// Ensure we always run tasks.
ranTasks = runAllTasks();
}
} else if (strategy > 0) {
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
} else {
ranTasks = runAllTasks(0); // This will run the minimum number of tasks
}
if (ranTasks || strategy > 0) {
if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS && logger.isDebugEnabled()) {
logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
selectCnt - 1, selector);
}
selectCnt = 0;
} else if (unexpectedSelectorWakeup(selectCnt)) { // Unexpected wakeup (unusual case)
selectCnt = 0;
}
} finally {
// Always handle shutdown even if the loop processing threw an exception.
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
}
}
}
上面這段代碼比較長,用一個簡單的流程圖表示就是這樣:
總結
NioEventLoop 在第一次建立線程的時候,會調用其父類 SingleThreadEventExecutor 的 run 方法,在這個方法内部啟動無限循環,監聽并處理事件。