Java定時執行緒池停止超時任務
阿新 • • 發佈:2019-02-08
一、背景
題主最近遇到一個問題,本來通過ScheduledExecutorService
執行緒池定時排程一個任務。奈何不知道為啥跑了2個多月,其中一個任務Hang住了,原本定時的任務則出現了問題。
關於定時執行緒池,好多人認為設定好頻率(比如1Min),它會按照這個間隔按部就班的工作。但是,如果其中一次排程任務卡住的話,不僅這次排程失敗,而且整個執行緒池也會停在這次排程上。
我們先從一個例子試著復現下問題:
public class pool {
private static class Runner implements Runnable {
@Override
public void run() {
try {
Thread.sleep(10000);
System.out.println(new Date());
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ScheduledExecutorService service
= Executors.newScheduledThreadPool(1 );
service.scheduleAtFixedRate(
new Runner(), 0, 1, TimeUnit.SECONDS);
}
}
先從Main
看,啟動一個定時執行緒池,每隔1S排程一次Runner
。看上去,應該是1S排程一次,但是Runner
的實際執行時間為10S,那多久會排程一次?答案是10S。
所以說,這個Runner
不管什麼原因掛掉了或者Hang住了,那這個定時排程執行緒池基本就廢了。
二、解決方法
那我們應該怎麼解決這個問題?如果說定時執行緒池有任務排程的超時策略就完美了,很可惜並沒有。
我們想下在併發程式設計中,哪種方式有超時策略?
對,Future
Future
,提供一種自動停止超時任務的方式,來解決某個任務Hang住的問題。
我們簡單修改下,把sleep
邏輯移動到Callable
中,並在Runner
中使用Future
來控制超時。
public class pool {
private static class Caller implements Callable<Boolean> {
@Override
public Boolean call() {
try {
Thread.sleep(10000);
System.out.println(new Date());
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
private static class Runner implements Runnable {
@Override
public void run() {
ExecutorService excutor = Executors.newSingleThreadExecutor();
Future<Boolean> future = excutor.submit(new Caller());
try {
future.get(1, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.out.println("timeout");
} catch (Exception e) {
e.printStackTrace();
} finally {
excutor.shutdownNow(); // 強制終止任務
}
}
}
public static void main(String[] args) {
ScheduledExecutorService service
= Executors.newScheduledThreadPool(1);
service.scheduleAtFixedRate(
new Runner(), 0, 1, TimeUnit.SECONDS);
}
}
備註:
- 實現邏輯相當於轉移了,把本來應該排程的任務交給了另外一個Future
單執行緒去執行。因為存在超時邏輯,不會影響原有定時執行緒池的執行。
- finally是否需要殺死執行緒池,因人而異。如果不殺死的話,那超時的任務會繼續執行。
題外話:如果你有好的解決方式,歡迎和題主探討。謝謝。