前面幾章,在我們分析的時候註冊Selector相關代碼的時候,提到過一部分NioEventLoop
的創建過程。接下來詳細分析。new NioEventLoopGroup();
public NioEventLoopGroup(int nThreads, Executor executor) {
//從jdk地層中獲取了一個SelectorProvider
this(nThreads, executor, SelectorProvider.provider());
}
public NioEventLoopGroup(
int nThreads, Executor executor, final SelectorProvider selectorProvider) {
//接着提供了一種默認的SelectStrategy工廠
this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory) {
//提供了一種拒絕執行的異常處理器
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
//如果沒有指定線程數,那麼就使用默認的值,通過斷點發現DEFAULT_EVENT_LOOP_THREADS的值是8
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
// 提供了默認的EventExecutor選擇器工廠
//該值爲:new DefaultEventExecutorChooserFactory();
this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}
最終走到了MultithreadEventExecutorGroup
的構造方法。主要做了幾件事
new ThreadPerTaskExecutor(newDefaultThreadFactory())
- 根據線程數初始化
EventExecutor
數組,並提供足夠的具體EventLoop
,這裏是NioEventLoop
- 初始化
chooser
,提供不同的事件執行的選擇器,已選擇EventExecutor數組種的EventLoop
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
//省略代碼
if (executor == null) {
//初始化executor : 每個任務的執行器
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
//實例化具體的NioEventLoop
children[i] = newChild(executor, args);
}
//初始化 EventExecutorChooserFactory.EventExecutorChooser chooser
chooser = chooserFactory.newChooser(children);
//省略相關代碼
}
-
new ThreadPerTaskExecutor(newDefaultThreadFactory())
任務執行器給了一個默認的工廠,定義了該具體每個線程的名字,類似nioEventLoopGroup-2-1
這樣的線程名字,第一個數字在初始化的時候便定義了爲poolId
,第二個數字這是在執行newThread()
的時候定義的nextId
。ThreadPerTaskExecutor
還定義了execute
方法。
public final class ThreadPerTaskExecutor implements Executor {
private final ThreadFactory threadFactory;
public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
if (threadFactory == null) {
throw new NullPointerException("threadFactory");
}
this.threadFactory = threadFactory;
}
@Override
public void execute(Runnable command) {
//調用工廠具體的執行方法執行 該task: command
threadFactory.newThread(command).start();
}
}
關於poolId
和nextId
我們發現前者是靜態的,因此,每次new NioEventLoopGroup()
的時候遞增,因此代表線程池,而後者並非靜態的,因此僅僅是調用遞增而已。
private static final AtomicInteger poolId = new AtomicInteger();
private final AtomicInteger nextId = new AtomicInteger();
跟進最終調用的是DefaultThreadFactory#newThread()
,並且發現,結果是返回了FastThreadLocalThread
,該線程爲netty自定義線程。執行的也是這類型的線程。因此們該對象每次執行任務(調用ThreadPerTaskExecutor#execute()
),其實都創建了一個線程實體。
@Override
public Thread newThread(Runnable r) {
Thread t = newThread(new DefaultRunnableDecorator(r), prefix + nextId.incrementAndGet());
//省略代碼
return t;
}
protected Thread newThread(Runnable r, String name) {
return new FastThreadLocalThread(threadGroup, r, name);
}
至於初始化
chooser
,線程選擇器,前面章節《Selector註冊》有提到過,其實就是分開兩種策略去循環使用EventExecutor
數組。調用
newChild()
初始化NioEventLoop
的時候,這裏創建並綁定了selector
用於去輪詢註冊到它上面的一些連接。
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
if (selectorProvider == null) {
throw new NullPointerException("selectorProvider");
}
if (strategy == null) {
throw new NullPointerException("selectStrategy");
}
provider = selectorProvider;
final SelectorTuple selectorTuple = openSelector();
selector = selectorTuple.selector;
unwrappedSelector = selectorTuple.unwrappedSelector;
selectStrategy = strategy;
}
值得注意的是,父類構造方法SingleThreadEventLoop
初始化了tailTasks
。該隊列用於在外部線程執行一些netty任務的時候,判斷是否在NioEventLoop
中,如果不在的話,則放到該隊列中去執行。講這些任務放到一個線程去執行。
private final Queue<Runnable> tailTasks;
protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks,
RejectedExecutionHandler rejectedExecutionHandler) {
super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
tailTasks = newTaskQueue(maxPendingTasks);
}
//由於本例NioEventLoop重寫了該方法,因此調用的子類
protected Queue<Runnable> newTaskQueue(int maxPendingTasks) {
return new LinkedBlockingQueue<Runnable>(maxPendingTasks);
}
@Override
protected Queue<Runnable> newTaskQueue(int maxPendingTasks) {
// This event loop never calls takeTask()
return PlatformDependent.newMpscQueue(maxPendingTasks);
}