目錄
2.創建EventLoop,並存儲到EventExecutor類型的數組中
1、NioEventLoopGroup
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
這裏創建了兩個group, 我們提過這是boss線程組和worker線程組, 其實這兩個線程組就相當於兩個NioEventLoop的集合, 默認每個NioEventLoopGroup創建時, 如果不傳入線程數, 會創建cpu核數*2個NioEventLoop線程, 其中boss線程通過輪詢處理Server的accept事件, 而完成accept事件之後, 就會創建客戶端channel, 通過一定的策略, 分發到worker線程進行處理, 而worker線程, 則主要用於處理客戶端的讀寫事件。
1.1 類圖
NioEventLoopGroup 是一個線程池,繼承了 MultithreadEventLoopGroup
1.2 初始化
從構造函數入手
跟進super, 進入了其父類MultithreadEventExecutorGroup的構造方法中:這裏我們看到, 如果傳入的線程數量參數爲0, 則會給一個默認值, 這個默認值就是兩倍的CPU核數。
public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {
private static final int DEFAULT_EVENT_LOOP_THREADS;
static {
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
}
protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args);
}
}
繼續跟代碼之後, 我們就看到了創建NioEventLoop的真正邏輯, 在MultithreadEventExecutorGroup類的構造方法中,創建了 nThreads 個子線程池。
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) {
if (executor == null) {
//創建一個新的線程執行器(1)
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
// 創建子線程池數組,構造NioEventLoop(2)
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
// 創建子線程池
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
if (!success) {
// 某一個線程池創建失敗,則關閉之前創建成功的線程池
for (int j = 0; j < i; j ++) {
children[j].shutdownGracefully();
}
for (int j = 0; j < i; j ++) {
EventExecutor e = children[j];
try {
while (!e.isTerminated()) {
e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
} catch (InterruptedException interrupted) {
// Let the caller handle the interruption.
Thread.currentThread().interrupt();
break;
}
}
}
}
}
//創建線程選擇器(3)
chooser = chooserFactory.newChooser(children);
final FutureListener<Object> terminationListener = new FutureListener<Object>() {
@Override
public void operationComplete(Future<Object> future) throws Exception {
if (terminatedChildren.incrementAndGet() == children.length) {
terminationFuture.setSuccess(null);
}
}
};
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
這邊將代碼主要分爲三個步驟:
1.創建線程執行器
這裏有個new DefaultThreadFactory()創建一個DefaultThreadFactory對象, 這個對象作爲參數傳入ThreadPerTaskExecutor的構造函數, DefaultThreadFactory顧名思義, 是一個線程工廠, 用於創建線程的, 簡單看下這個類的繼承關係:
public class DefaultThreadFactory implements ThreadFactory{//類體}
這裏繼承了jdk底層ThreadFactory類, 用於創建線程
我們繼續跟進ThreadPerTaskExecutor的類中:
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) {
//起一個線程
threadFactory.newThread(command).start();
}
}
這個類非常簡單, 繼承了jdk的Executor類, 從繼承關係中我就能猜想到, 而這個類就是用於開啓線程的線程執行器,再看重寫的 execute(Runnable command) 方法, 傳入一個任務, 然後由threadFactory對象創建一個線程執行該任務。這個execute(Runnable command)方法, 其實就是用開開啓NioEventLoop線程用的。
這樣, 通過 executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()) 這種方式就返回了一個線程執行器Executor, 用於開啓NioEventLoop線程
2.創建EventLoop,並存儲到EventExecutor類型的數組中
這裏通過 children = new EventExecutor[nThreads] 初始化了children屬性, 看下這個屬性的定義:
private final EventExecutor[] children
這裏的children是EventExecutor類型的數組, 其實就是NioEventLoop的集合, 因爲NioEventLoop也是EventExecutor的子類
所以這裏初始化了children數組, 大小爲參數nThreads傳入的線程數量, 默認爲cpu核數的兩倍
後面就是通過for循環來創建NioEventLoop線程,在循環體裏通過 newChild() 創建NioEventLoop, 我們跟newChild(executor, args)方法
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new NioEventLoop(this, executor, (SelectorProvider) args[0], ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}
this是NioEventLoopGroup自身, executor就是上一小節講到的線程執行器
跟一下NioEventLoop的構造方法,跟到父類SingleThreadEventExecutor構造方法:
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks,
RejectedExecutionHandler rejectedHandler) {
super(parent);
this.addTaskWakesUp = addTaskWakesUp;
this.maxPendingTasks = Math.max(16, maxPendingTasks);
//初始化了線程執行器
this.executor = ObjectUtil.checkNotNull(executor, "executor");
//創建一個任務隊列, 這個任務隊列可以將不屬於NioEventLoop線程的任務放到這個任務隊列中, 通過NioEventLoop線程執行
taskQueue = newTaskQueue(this.maxPendingTasks);
rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}
3.創建線程選擇器
chooser = chooserFactory.newChooser(children);
NioEventLoopGroup都綁定一個chooser對象, 作爲線程選擇器, 通過這個線程選擇器, 爲每一個channel分配不同的線程
我們看到newChooser(children)傳入了NioEventLoop數組
我們跟到DefaultEventExecutorChooserFactory類中的newChooser方法:
public EventExecutorChooser newChooser(EventExecutor[] executors) {
if (isPowerOfTwo(executors.length)) {
return new PowerOfTowEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
}
這裏通過 isPowerOfTwo(executors.length) 判斷NioEventLoop數組的長度是不是2的倍數, 然後根據判斷結果返回兩種選擇器對象, 這裏使用到java設計模式的策略模式
根據這兩個類的名字不難看出, 如果是2的倍數, 使用的是一種高性能的方式選擇線程, 如果不是2的倍數, 則使用一種比較普通的線程選擇方式。
private static final class PowerOfTowEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
PowerOfTowEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
}
這個類實現了線程選擇器的接口EventExecutorChooser, 構造方法中初始化了NioEventLoop線程數組
重點關注下next()方法, next()方法就是選擇下一個線程的方法, 如果線程數是2的倍數, 這裏通過按位與進行計算, 所以效率極高
private static final class GenericEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
GenericEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
}
這個類同樣實現了線程選擇器的接口EventExecutorChooser, 並在造方法中初始化了NioEventLoop線程數組
再看這個類的next()方法, 如果線程數不是2的倍數, 則用絕對值和取模的這種效率一般的方式進行線程選擇
2、NioEventLoop
2.1 類圖
NioEventLoop 是隻有單個線程的線程池,但並不是一個純粹的I/O線程,它除了負責I/O的讀寫之外,還兼顧處理以下兩類任務:
-
系統Task:通過調用NioEventLoop的execute(Runnable task)方法實現,Netty有很多系統Task,創建它們的主要原因是:當I/O線程和用戶線程同時操作網絡資源時,爲了防止併發操作導致的鎖競爭,將用戶線程的操作封裝成Task放入消息隊列中,由I/O線程負責執行,這樣就實現了局部無鎖化。
-
定時任務:通過調用NioEventLoop的schedule(Runnable command, long delay, TimeUnit unit)方法實現。
2.2 selector
作爲NIO框架的Reactor線程,NioEventLoop需要處理網絡I/O讀寫事件,因此它必須聚合一個多路複用器對象。Selector的初始化非常簡單,直接調用Selector.open()方法就能創建並打開一個新的Selector。Netty對Selector的selectedKeys進行了優化,用戶可以通過io.netty.noKeySetOptimization開關決定是否啓用該優化項。默認不打開selectedKeys優化功能。
Selector selector;
private SelectedSelectionKeySet selectedKeys;
private final SelectorProvider provider;
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider) {
super(parent, executor, false);
if (selectorProvider == null) {
throw new NullPointerException("selectorProvider");
}
provider = selectorProvider;
//初始化selector
selector = openSelector();
}
private Selector openSelector() {
final Selector selector;
try {
////調用jdk底層的api,通過provider.openSelector()創建並打開多路複用器
selector = provider.openSelector();
} catch (IOException e) {
throw new ChannelException("failed to open a new selector", e);
}
//如果沒有開啓selectedKeys優化開關,就立即返回。
if (DISABLE_KEYSET_OPTIMIZATION) {
return selector;
}
//如果開啓了優化開關,需要通過反射的方式從Selector實例中獲取selectedKeys和publicSelectedKeys,
//將上述兩個成員變量設置爲可寫,通過反射的方式使用Netty構造的selectedKeys包裝類selectedKeySet將原JDK的selectedKeys替換掉。
try {
//用這個數據結構替換原生的SelectionKeySet
SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
//通過反射拿到sun.nio.ch.SelectorImpl這個類的class對象
Class<?> selectorImplClass = Class.forName("sun.nio.ch.SelectorImpl", false, ClassLoader.getSystemClassLoader());
//判斷拿到的是不是class對象並且是不是Selector的實現類,如果不是他的實現, 就直接返回原生select
if (!selectorImplClass.isAssignableFrom(selector.getClass())) {
return selector;
}
//通過反射拿到selectedKeys和publicSelectedKeys兩個屬性, 默認這兩個屬性底層都是hashSet方式實現的
Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
//設置成可修改的
selectedKeysField.setAccessible(true);
publicSelectedKeysField.setAccessible(true);
//將selector的這兩個屬性替換成Netty的selectedKeySet
selectedKeysField.set(selector, selectedKeySet);
publicSelectedKeysField.set(selector, selectedKeySet);
selectedKeys = selectedKeySet;
logger.trace("Instrumented an optimized java.util.Set into: {}", selector);
} catch (Throwable t) {
selectedKeys = null;
logger.trace("Failed to instrument an optimized java.util.Set into: {}", selector, t);
}
return selector;
}
selector在select()操作時候, 會通過selector.selectedKeys()操作返回一個Set<SelectionKey>, 這個是Set類型。
netty對這個set進行了處理, 使用SelectedSelectionKeySet的數據結構進行替換, 當在select()操作時將key存入一個SelectedSelectionKeySet的數據結構中。
簡單跟一下SelectedSelectionKeySet這個類的構造方法:
SelectedSelectionKeySet() {
keys = new SelectionKey[1024];
}
說明這類其實底層是通過數組實現的, 通過操作數組下標會有更高的效率。看下其中重要的方法,add表示添加一個SelectionKey。remove()方法, contains()方法都返回了false, 說明其不支持刪除方法和包含方法。
@Override
public boolean add(SelectionKey o) {
if (o == null) {
return false;
}
keys[size++] = o;
if (size == keys.length) {
increaseCapacity();
}
return true;
}
@Override
public boolean remove(Object o) {
return false;
}
@Override
public boolean contains(Object o) {
return false;
}
2.3 run
NioEventLoop 的核心方法是 run 方法
protected void run() {
for (;;) {
try {
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.BUSY_WAIT:
case SelectStrategy.SELECT:
//輪詢io事件(1)
select(wakenUp.getAndSet(false));
if (wakenUp.get()) {
selector.wakeup();
}
// fall through
default:
}
cancelledKeys = 0;
needsToSelectAgain = false;
//ioRatio主要是用來控制processSelectedKeys()方法執行時間和任務隊列執行時間的比例, 其中ioRatio默認是50, 所以會走到下一步else
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
runAllTasks();
}
} else {
//記錄下開始時間
final long ioStartTime = System.nanoTime();
try {
//處理輪詢到的key(2)
processSelectedKeys();
} finally {
//計算耗時
final long ioTime = System.nanoTime() - ioStartTime;
//執行task(3)
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
} catch (Throwable t) {
handleLoopException(t);
}
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}
分爲以下3步
1. 輪詢io事件
2. 處理輪詢到的key
3. 執行任務隊列中的task
1.輪詢io事件
首先switch塊中默認會走到SelectStrategy.SELECT中, 執行select(wakenUp.getAndSet(false))方法
參數wakenUp.getAndSet(false)代表當前select操作是未喚醒狀態
進入到select(wakenUp.getAndSet(false))方法中:
private void select(boolean oldWakenUp) throws IOException {
Selector selector = this.selector;
try {
int selectCnt = 0;
//當前系統的納秒數
long currentTimeNanos = System.nanoTime();
//截止時間=當前時間+隊列第一個任務剩餘執行時間,因爲超過之後定時任務不能嚴格按照預定時間執行,
//delayNanos(currentTimeNanos)代表距定時任務中第一個任務剩餘多長時間,
//其中定時任務隊列是已經按照執行時間有小到大排列好的隊列, 所以第一個任務則是最近需要執行的任務, selectDeadLineNanos就代表了當前操作不能超過的時間。
long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);
for (;;) {
//阻塞時間(毫秒)=(截止時間-當前時間+0.5毫秒),除以1000000表示將計算的時間轉化爲毫秒數
//最後算出的時間就是selector操作的阻塞時間, 並賦值到局部變量的timeoutMillis中
long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
if (timeoutMillis <= 0) {
if (selectCnt == 0) {
//時間已經超過了最後截止時間+0.5毫秒, selectCnt == 0 代表沒有進行select操作, 滿足這兩個條件, 則執行selectNow()之後, 將selectCnt賦值爲1之後跳出循環
selector.selectNow();
selectCnt = 1;
}
break;
}
//沒超過截止時間
if (hasTasks() && wakenUp.compareAndSet(false, true)) {
//hasTasks()這裏是判斷當前NioEventLoop所綁定的taskQueue是否有任務,
//如果有任務, sa則執行selectNow()之後, 將selectCnt賦值爲1之後跳出循環(跳出循環之後去執行任務隊列中的任務)
selector.selectNow();
selectCnt = 1;
break;
}
//如果沒有滿足上述條件, 就會執行如下,進行阻塞式輪詢, 並且自增輪詢次數
int selectedKeys = selector.select(timeoutMillis);
//輪詢次數
selectCnt ++;
//如果輪詢到一個事件(selectedKeys != 0), 或者當前select操作需要喚醒(oldWakenUp),
//或者在執行select操作時已經被外部線程喚醒(wakenUp.get()),
//或者任務隊列已經有任務(hasTask), 或者定時任務隊列中有任務(hasScheduledTasks())
//如果滿足以上,就跳出循環
if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
break;
}
//省略
//記錄下當前時間
long time = System.nanoTime();
//當前時間-開始時間>=超時時間(條件成立, 說明已經完整的執行完成了一個阻塞的select()操作, 條件不成立, 有可能發生空輪詢)
if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) {
//代表已經進行了一次阻塞式select操作, 操作次數重置爲1
selectCnt = 1;
} else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
//省略日誌代碼
//如果空輪詢的次數大於一個閾值(512), 解決空輪詢的bug
rebuildSelector();
selector = this.selector;
selector.selectNow();
selectCnt = 1;
break;
}
currentTimeNanos = time;
}
//代碼省略
} catch (CancelledKeyException e) {
//省略
}
}
如果此條件不成立, 說明沒有完整執行select()操作, 可能觸發了一次空輪詢, 根據前一個selectCnt++這步我們知道, 每觸發一次空輪詢selectCnt都會自增
之後會進入第二個判斷 SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD
其中SELECTOR_AUTO_REBUILD_THRESHOLD默認是512, 這個判斷意思就是空輪詢的次數如果超過512次, 則會認爲是發生了epoll bug, 這樣會通過rebuildSelector()方法重新構建selector, 然後將重新構建的selector賦值到局部變量selector, 執行一次selectNow(), 將selectCnt初始化1, 跳出循環
2.處理輪詢到的key
ioRatio主要是用來控制processSelectedKeys()方法執行時間和任務隊列執行時間的比例, 其中ioRatio默認是50, 所以會走到下一步else,首先通過 final long ioStartTime = System.nanoTime() 記錄下開始時間, 再通過processSelectedKeys()方法處理輪詢到的key。
我們跟到processSelectedKeys()方法中:
private void processSelectedKeys() {
if (selectedKeys != null) {
//如果selectedKeys被netty優化過,走這裏
processSelectedKeysOptimized();
} else {
//如果selectedKeys沒有被優化過,到這裏
processSelectedKeysPlain(selector.selectedKeys());
}
}
private void processSelectedKeysOptimized() {
//通過for循環遍歷數組
for (int i = 0; i < selectedKeys.size; ++i) {
//拿到當前的selectionKey
final SelectionKey k = selectedKeys.keys[i];
//將當前引用設置爲null
selectedKeys.keys[i] = null;
//獲取channel(NioSeverSocketChannel)
final Object a = k.attachment();
//判斷channel是不是AbstractNioChannel, 通常情況都是AbstractNioChannel。
//如果是AbstractNioChannel, 則調用processSelectedKey()方法處理io事件
if (a instanceof AbstractNioChannel) {
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
processSelectedKey(k, task);
}
if (needsToSelectAgain) {
// null out entries in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.reset(i + 1);
selectAgain();
i = -1;
}
}
}
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
//獲取到channel中的unsafe
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
//驗證key是否合法的
if (!k.isValid()) {
//如果這個key不是合法的, 說明這個channel可能有問題
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
return;
}
if (eventLoop != this || eventLoop == null) {
return;
}
unsafe.close(unsafe.voidPromise());
return;
}
try {
//如果是合法的, 拿到key的io事件
int readyOps = k.readyOps();
//鏈接事件
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
//寫事件
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
ch.unsafe().forceFlush();
}
//讀事件和接受鏈接事件
//如果當前NioEventLoop是work線程的話, 這裏就是op_read事件
//如果是當前NioEventLoop是boss線程的話, 這裏就是op_accept事件
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
//然後會通過channel綁定的unsafe對象執行read()方法用於處理鏈接或者讀寫事件
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
3.執行任務隊列中的task
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
protected boolean runAllTasks(long timeoutNanos) {
//從定時任務隊列中聚合任務,也就是將定時任務中找到可以執行的任務添加到taskQueue中
fetchFromScheduledTaskQueue();
//從普通taskQ裏面拿一個任務
Runnable task = pollTask();
//task爲空, 則直接返回
if (task == null) {
//跑完所有的任務執行收尾的操作
afterRunningAllTasks();
return false;
}
//如果隊列不爲空
//首先算一個截止時間(+50毫秒, 因爲執行任務, 不要超過這個時間)
final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos;
long runTasks = 0;
long lastExecutionTime;
//執行每一個任務
for (;;) {
safeExecute(task);
//標記當前跑完的任務
runTasks ++;
//當跑完64個任務的時候, 會計算一下當前時間
if ((runTasks & 0x3F) == 0) {
//定時任務初始化到當前的時間
lastExecutionTime = ScheduledFutureTask.nanoTime();
//如果超過截止時間則不執行(nanoTime()是耗時的)
if (lastExecutionTime >= deadline) {
break;
}
}
//如果沒有超過這個時間, 則繼續從普通任務隊列拿任務
task = pollTask();
//直到沒有任務執行
if (task == null) {
//記錄下最後執行時間
lastExecutionTime = ScheduledFutureTask.nanoTime();
break;
}
}
//收尾工作
afterRunningAllTasks();
this.lastExecutionTime = lastExecutionTime;
return true;
}
private boolean fetchFromScheduledTaskQueue() {
long nanoTime = AbstractScheduledEventExecutor.nanoTime();
//從定時任務隊列中抓取第一個定時任務
//尋找截止時間爲nanoTime的任務
Runnable scheduledTask = pollScheduledTask(nanoTime);
//如果該定時任務隊列不爲空, 則塞到普通任務隊列裏面
while (scheduledTask != null) {
//如果添加到普通任務隊列過程中失敗
if (!taskQueue.offer(scheduledTask)) {
//則重新添加到定時任務隊列中
scheduledTaskQueue().add((ScheduledFutureTask<?>) scheduledTask);
return false;
}
//繼續從定時任務隊列中拉取任務
//方法執行完成之後, 所有符合運行條件的定時任務隊列, 都添加到了普通任務隊列中
scheduledTask = pollScheduledTask(nanoTime);
}
return true;
}
參考文獻: