使用 ExecutorService 線程池時,主線程提交任務後呼叫 shutdown() 與 awaitTermination(),理應等待所有任務完成並正常退出。但若主線程邏輯錯誤,導致 shutdown() 未被呼叫或提前結束,JVM 會因非守護執行緒仍在運作而拒絕退出(Eclipse 控制台紅點不變灰)。
public static void main(String[] args) {
int thread = 0;
ExecutorService pool = Executors.newFixedThreadPool(15);
for (int i = 666; i <= 888; i++) {
for (int j = 0; j < 19; j++) {
pool.execute(new MyClass(i, j));
}
}
thread++; // 只執行一次!
if (thread >= 15) { // 永遠不會成立
pool.shutdown();
// ... awaitTermination ...
System.exit(0);
}
}
核心錯誤:thread++ 與 if 判斷在迴圈外部,且只執行一次,thread 永遠為 1,永不滿足 >=15,導致 shutdown() 從未被呼叫,線程池持續運行,JVM 不退出。
ExecutorService pool = Executors.newFixedThreadPool(15);
// 提交所有任務
for (int i = 666; i <= 888; i++) {
for (int j = 0; j < 19; j++) {
pool.execute(new MyClass(i, j));
}
}
// 1. 關閉線程池,不再接受新任務
pool.shutdown();
try {
// 2. 等待現有任務完成,設定超時時間
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
// 3. 超時則強制關閉正在執行的任務
pool.shutdownNow();
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt(); // 保留中斷狀態
}
// 4. 正常退出(非必要,main 結束即退出)
// System.exit(0);
shutdown() 必須在提交完所有任務後呼叫一次,而非在迴圈或條件判斷中。awaitTermination() 會阻塞主線程,直到所有任務完成或超時。Thread.interrupted() 並拋出 InterruptedException,配合 shutdownNow() 的中斷機制實現真正停止。System.exit(0) 通常多餘。生產環境建議使用 ThreadPoolExecutor 直接構建,自訂 ThreadFactory 設定執行緒名稱、RejectedExecutionHandler 處理拒絕策略,並搭配監控指標(佇列長度、活躍執行緒數)觀察線程池健康狀態。
正確管理線程池生命週期,是 Java 併發程式穩定運行的基礎。