先從EventExecutor開始,因爲它是一個很基礎的工具類,是對I/O線程的包裝。先了解下它的源碼會對後面的分析有更好的理解。
先看下EventExecutor的類關係圖,這裏只是簡單的畫出了類和接口的繼承和實現關係,還有其他的聚合關係沒有畫出來,爲的是便於分析思路的清晰。
說到Executor,很容易聯想到jdk中 java.util.concurrent.Executor 接口,這個接口非常簡單,就一個方法
void execute(Runnable command);
從方法簽名上就能看出這個是爲了支持異步模式的。command表示一個命令。當前線程就是命令者角色,Executor內部的去運行Runnable的線程就是執行者。這裏沒有提供明確的地方供命令者去取得命令的執行結果。
ExecutorService 繼承了Executor 接口,增加了對自身生命週期管理的方法,同時提供了一個Future給命令者去獲取命令的執行結果。
ScheduledExecutorService 繼承了ExecutorService接口,增加了對定時任務的支持。
EventExecutorGroup 繼承了ScheduledExecutorService接口,對原來的ExecutorService的關閉接口提供了增強,提供了優雅的關閉接口。從接口名稱上可以看出它是對多個EventExecutor的集合,提供了對多個EventExecutor的迭代訪問接口。
EventExecutor 繼承EventExecutorGroup 看着這個關係真心有些糾結啊。不過細想下還是能理解的。A是B中的一員,但是A也能迭代訪問B中的其他成員。這個繼承關係支持了迭代訪問這個行爲。自然的他提供了一個parent接口,來獲取所屬的EventExecutorGroup 。另外提供了inEventLoop 方法支持查詢某個線程是否在EventExecutor所管理的線程中。還有其他一些創建Promise和Future的方法。
AbstractEventExecutor 只是對EventExecutor中某些方法的簡單實現
下面重點分析下非常有意思SingleThreadEventExecutor,它也是個抽象類,但是提供了很多重要方法的實現。弄清楚了這個對整個EventExecutor體系都非常有幫助。從類名上可知裏面只有一個線程,先搞清楚一個線程的處理過程再理解多線程的就輕鬆些了。
先從execute方法入口分析
@Override
public void execute(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
addTask(task);
} else {
startThread();
addTask(task);
if (isShutdown() && removeTask(task)) {
reject();
}
}
if (!addTaskWakesUp) {
wakeup(inEventLoop);
}
}
先不看這個源碼,我們分析下,作爲一個Executor,A讓你執行命令A,B讓你執行命令B。。。命令不是說執行就能執行的吧。總得有個地方保存還沒來得及執行的命令吧,總得有個先來後到吧。而這個地方又會被多線程訪問,得保證多線程訪問可見性,操作的原子性。jdk中提供的BlockingQueue就是爲此而生的。BlockingQueue是一個接口,jdk中有很多他的實現。SingleThreadEventExcutor中提供的這個地方叫tasksQueue,類型是jdk中的LinkedBlockingQueue。上面代碼中的addTask就是往tasksQueue中添加。 另外SingleThreadEventExcutor實現了ScheduledExecutorService 接口,支持執行定時任務。得有個地方存放定時任務信息。類中的實現是delayedTaskQueue,它是一個PriorityQueue ,也是一個BlockingQueue。不過它裏面的元素不是按照先來後到的順序存取的,而是按照各個元素的優先級判斷的。
因爲這個execute方法,是可以在外部線程調用,也可以在內部線程調用。也就是說外部成員可以給你下命令,內部成員也可以給你下命令。所以在上面的代碼中先調用inEventLoop判斷當前下命令的是外部的還是內部的。
如果是外部的,先確定內部線程是否啓動,沒啓動就先啓動內部線程同時給自己加一個定時清理的定時任務。這個從下面的代碼中可以看出
private void startThread() {
synchronized (stateLock) {
if (state == ST_NOT_STARTED) {
state = ST_STARTED;
delayedTaskQueue.add(new ScheduledFutureTask<Void>(
this, delayedTaskQueue, Executors.<Void>callable(new PurgeTask(), null),
ScheduledFutureTask.deadlineNanos(SCHEDULE_PURGE_INTERVAL), -SCHEDULE_PURGE_INTERVAL));
thread.start();
}
}
}
private final class PurgeTask implements Runnable {
@Override
public void run() {
Iterator<ScheduledFutureTask<?>> i = delayedTaskQueue.iterator();
while (i.hasNext()) {
ScheduledFutureTask<?> task = i.next();
if (task.isCancelled()) {
i.remove();
}
}
}
}
添加命令完成了,下面就看如何去執行命令了,這個就需要分析下內部線程的執行邏輯了。SingleThreadEventExecutor類中有一個實力變量Thread,它引用的就是當前Executor所擁有的那個thread對象。
thread = threadFactory.newThread(new Runnable() {
@Override
public void run() {
boolean success = false;
updateLastExecutionTime();
try {
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
// 更改狀態
if (state < ST_SHUTTING_DOWN) {
state = ST_SHUTTING_DOWN;
}
// Check if confirmShutdown() was called at the end of the loop.
// 這裏說明在try塊中調用的SingleThreadEventExecutor.this.run();中在方法結束之前必須調用confirmShutdown方法,這個在其之類實現的run方法中得到驗證
if (success && gracefulShutdownStartTime == 0) {
logger.error(
"Buggy " + EventExecutor.class.getSimpleName() + " implementation; " +
SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called " +
"before run() implementation terminates.");
}
try {
// Run all remaining tasks and shutdown hooks.
// 確保tasksQueue和shutDownHooks中的runable都處理完成了,這裏的處理完成有可能是超時了
for (;;) {
if (confirmShutdown()) {
break;
}
}
} finally {
try {
cleanup();
} finally {
synchronized (stateLock) {
state = ST_TERMINATED;
}
//釋放信號量,使用Semaphore(0)來讓另一個線程一直等待,知道內部線程調用了release()
threadLock.release();
if (!taskQueue.isEmpty()) {
logger.warn(
"An event executor terminated with " +
"non-empty task queue (" + taskQueue.size() + ')');
}
terminationFuture.setSuccess(null);
}
}
}
}
});
上面Thread內部run方法執行的是SingleThreadEventExecutor.this.run(),而這個run方法是一個抽象方法,留給了子類去實現了。不過可以肯定的是子類的run方法是不斷的去tasksQueue中取出task去執行。現在重點分析下finally塊中的代碼。
1、首先更改狀態爲正在關閉狀態。
2、如果子類中的run方法中的loop執行成功了,就得先調用confirmShutdown,確認任務隊列中的任務是否都已經被執行了。
3、然後還得再次確認下任務隊列中是否已被執行完畢,因爲在關閉的過程中外部也是能添加任務的。
4、最終執行清理工作,更改狀態爲已關閉,釋放信號量。
5、如果這個時候還是有任務沒執行完,那也只能是無奈了,記個log吧
6、更新整個關閉過程爲success
再分析下confirmShutdown,看看是如何保證所有的task執行完成的呢
protected boolean confirmShutdown() {
// 如果state狀態 state < ST_SHUTTING_DOWN則直接return false
if (!isShuttingDown()) {
return false;
}
// 這個方法必須從內部調用,從修飾符 protected也可以看出
if (!inEventLoop()) {
throw new IllegalStateException("must be invoked from an event loop");
}
// 取消所有的定時任務
cancelDelayedTasks();
if (gracefulShutdownStartTime == 0) {
// 標記shutdown處理的開始時間
gracefulShutdownStartTime = ScheduledFutureTask.nanoTime();
}
// 運行tasksQueue或者shutdownHooks中的所有Runnable都處理完成
if (runAllTasks() || runShutdownHooks()) {
//分析了下源碼,isShutdown()這個只能是在外部線程調用了shutdown()接口的時候纔會有可能成爲true
//但是現在這個方法已經@Deprecated,所以這個if塊是不會進入的
if (isShutdown()) {
// shutdown 成功,沒有更多的runnable需要執行
return true;
}
// There were tasks in the queue. Wait a little bit more until no tasks are queued for the quiet period.
wakeup(true);
return false;
}
final long nanoTime = ScheduledFutureTask.nanoTime();
// runAllTasks() 或者runAllTasks() + runShutdownHooks()方法執行時間操作了最大限制
if (isShutdown() || nanoTime - gracefulShutdownStartTime > gracefulShutdownTimeout) {
return true;
}
// 現在時間與上個任務執行完成的時間差小於quietPeriod時間,繼續檢測
if (nanoTime - lastExecutionTime <= gracefulShutdownQuietPeriod) {
// Check if any tasks were added to the queue every 100ms.
// TODO: Change the behavior of takeTask() so that it returns on timeout.
wakeup(true);
try {
//內部線程sleep 100ms
Thread.sleep(100);
} catch (InterruptedException e) {
// Ignore
}
return false;
}
// No tasks were added for last quiet period - hopefully safe to shut down.
// (Hopefully because we really cannot make a guarantee that there will be no execute() calls by a user.)
return true;
}
後面再看看這個類中的一些內部變量 private static final Runnable WAKEUP_TASK = new Runnable() {
@Override
public void run() {
// Do nothing.
}
};
這個WAKEUP_TASK什麼也不做,爲啥取名wakeup呢?我看源碼也沒太明白。有人理解的給解釋下吧
private final Semaphore threadLock = new Semaphore(0);
threadLock的內部permits設置爲0,也就是說acquire()永遠獲取不到permit,會一直被阻塞着。那有什麼用呢?另一種實現wait()/notify()吧 值得注意的是類中是如何來控制定時任務的呢?祕密在這個方法中
private void fetchFromDelayedQueue() {
long nanoTime = 0L;
for (;;) {
ScheduledFutureTask<?> delayedTask = delayedTaskQueue.peek();
if (delayedTask == null) {
break;
}
if (nanoTime == 0L) {
nanoTime = ScheduledFutureTask.nanoTime();
}
if (delayedTask.deadlineNanos() <= nanoTime) {
delayedTaskQueue.remove();
taskQueue.add(delayedTask);
} else {
break;
}
}
}
ScheduledFutureTask類中有個變量記錄這個類被加載進內存中的時間
private static final long START_TIME = System.nanoTime();
static long nanoTime() {
return System.nanoTime() - START_TIME;
}
// 返回到期時間,到期時間在構造函數中指定了
public long deadlineNanos() {
return deadlineNanos;
}
// 這個方法決定了排序的優先級
@Override
public int compareTo(Delayed o) {
if (this == o) {
return 0;
}
ScheduledFutureTask<?> that = (ScheduledFutureTask<?>) o;
long d = deadlineNanos() - that.deadlineNanos();
if (d < 0) {
return -1;
} else if (d > 0) {
return 1;
} else if (id < that.id) {
return -1;
} else if (id == that.id) {
throw new Error();
} else {
return 1;
}
}
所以fetchFromDelayedQueue()方法的邏輯就是先取出即將到期的task,判斷是否已經到期,若已經到期就加入到tasksQueue中,等到被執行。 先分析到這裏,後面在補上。
有什麼不正確的,也請大家指正。