高併發系統中,線程池配合 Future.get(timeout) 實現異步任務超時控制是常見模式。但若超時任務未正確取消,會佔用線程池槽位,導致後續任務排隊甚至服務不可用。本文通過復現代碼揭示問題根因,並給出正確處理姿勢。
public class FutureTest {
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newFixedThreadPool(1); // 單線程池
Callable<String> slowTask = () -> {
Thread.sleep(2000); // 耗時 2 秒
return "慢任務完成";
};
Callable<String> fastTask1 = () -> "快任務1";
Callable<String> fastTask2 = () -> "快任務2";
Future<String> f1 = exec.submit(slowTask);
Future<String> f2 = exec.submit(fastTask1);
Future<String> f3 = exec.submit(fastTask2);
// 設置 500ms 超時
try { String r = f1.get(500, TimeUnit.MILLISECONDS); }
catch (TimeoutException e) { System.out.println("任務1超時"); }
// 後續任務雖快,但需等待線程釋放
try { String r = f2.get(3000, TimeUnit.MILLISECONDS); }
catch (Exception e) { e.printStackTrace(); }
try { String r = f3.get(3000, TimeUnit.MILLISECONDS); }
catch (Exception e) { e.printStackTrace(); }
exec.shutdown();
}
}
TimeoutException。Future.get(timeout) 僅限制調用線程的等待時間,不會中斷正在執行的任務線程。Callable<String> interruptibleTask = () -> {
try {
for (int i = 0; i < 10; i++) {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException("任務被取消");
}
Thread.sleep(200); // 可中斷睡眠
}
return "完成";
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 保持中斷狀態
return "已取消";
}
};
// 提交後超時取消
Future<String> future = exec.submit(interruptibleTask);
try {
String result = future.get(500, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
future.cancel(true); // 關鍵:true 表示中斷正在執行的任務
}
CompletableFuture.supplyAsync(() -> {
// 耗時操作
return "結果";
}, exec).orTimeout(500, TimeUnit.MILLISECONDS)
.whenComplete((result, ex) -> {
if (ex != null) System.out.println("超時或異常: " + ex);
else System.out.println("結果: " + result);
});
orTimeout 內部自動調用 cancel(true) 中斷任務線程。
ThreadPoolExecutor 顯式設置核心/最大線程數、有界隊列。CallerRunsPolicy 或自定義拒絕策略記錄日誌、報警。threadpool.active, threadpool.queue.size。Thread.interrupted() 或捕獲 InterruptedException。future.cancel(true) 釋放線程資源。Object.wait() 無超時、某些 JDBC 驅動、舊版 HTTP 客戶端)。Executors 默認無界隊列工廠。Future.get(timeout) 不會自動殺死任務線程,超時處理的關鍵在於 future.cancel(true) 配合任務代碼對中斷的響應。構建健壯的異步系統,需從任務編寫、線程池配置、超時取消、監控告警全鏈路閉環。