長耗時操作(資料庫連線、網絡下載)常需超時控制,超時後需退出線程或重試。本文介紹兩種實現思路。
核心思路:啟動一個守護線程計時,超時拋出未檢查異常(RuntimeException 子類),主邏輯捕獲處理。
public class TimeoutException extends RuntimeException {
private static final long serialVersionUID = -8078853655388692688L;
public TimeoutException(String msg) { super(msg); }
}
public class TimeoutThread extends Thread {
private final long timeout;
private volatile boolean canceled = false;
private final TimeoutException exception;
public TimeoutThread(long timeout, TimeoutException ex) {
this.timeout = timeout;
this.exception = ex;
setDaemon(true); // 守護線程,不阻塞 JVM 退出
}
public synchronized void cancel() { canceled = true; }
public void run() {
try {
Thread.sleep(timeout);
if (!canceled) throw exception;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
TimeoutThread timer = new TimeoutThread(5000, new TimeoutException("超時"));
try {
timer.start();
// ... 要監控的業務代碼 ...
timer.cancel(); // 正常完成取消計時
} catch (TimeoutException e) {
// 超時處理邏輯
}
注意:此方案計時從 start() 開始,非精確執行時間;異常拋出在計時器線程中,需確保業務代碼能感知(如共享變量或中斷)。
主線程調用 thread.join(timeout) 等待子線程結束;超時則中斷子線程:
Thread worker = new Thread(() -> { /* 長任務 */ });
worker.start();
worker.join(5000);
if (worker.isAlive()) {
worker.interrupt(); // 請求中斷
// 超時處理
}
優點:超時定義為線程實際運行時間。缺點:只能控制整個 run(),無法精確到代碼片段;異常處理邏輯在外部,耦合度較高。
ExecutorService + Future.get(timeout) 或 CompletableFuture,更標準健壯。