您好,登錄后才能下訂單哦!
今天小編給大家分享一下ScheduledThreadPoolExecutor的坑如何解決的相關(guān)知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
先看下面案例代碼
public class ScheduledThreadPoolExecutorTest { public static ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2); public static AtomicInteger atomicInteger = new AtomicInteger(1); public static void main(String[] args) { scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> { // 模擬業(yè)務邏輯 int num = atomicInteger.getAndIncrement(); // 模擬出現(xiàn)異常 if (num > 3) { throw new RuntimeException("定時任務執(zhí)行異常"); } System.out.println("別坑我!"); }, 0, 1, TimeUnit.SECONDS); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } scheduledThreadPoolExecutor.shutdown(); } }
案例代碼邏輯很簡單,主線程等待5秒后關(guān)閉線程池,定時任務執(zhí)行三次后模擬拋出RuntimeException
但是我們看看執(zhí)行結(jié)果,只執(zhí)行了三次!
因為某種情況下,定時任務在執(zhí)行第四次時出現(xiàn)異常,從而導致任務調(diào)度被取消,不會繼續(xù)執(zhí)行
而且,異常信息也沒有對外拋出!
那么咋解決嘞?try-catch就行了唄~
可以看到執(zhí)行結(jié)果,雖然執(zhí)行異常,但是任務卻還是一直在調(diào)度~
代碼里使用工具類對Runnable任務包了一層,就是加了try-catch
public class RunnableDecoratorUtil { public static Runnable runnableDecorator(Runnable runnable) { return () -> { try { runnable.run(); } catch (Exception e) { e.printStackTrace(); } }; } }
直接進入scheduleAtFixedRate
源碼查看
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { // 參數(shù)校驗 if (command == null || unit == null) throw new NullPointerException(); if (period <= 0L) throw new IllegalArgumentException(); // 將任務、執(zhí)行時間、周期等封裝到ScheduledFutureTask內(nèi) ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command, null, triggerTime(initialDelay, unit), unit.toNanos(period), sequencer.getAndIncrement()); RunnableScheduledFuture<Void> t = decorateTask(command, sft); sft.outerTask = t; // 延時執(zhí)行 delayedExecute(t); return t; }
因為我們提交的任務被封裝在ScheduledFutureTask
,所以我們直接來看ScheduledFutureTask
的run
方法
public void run() { // 校驗當前狀態(tài)是否還能執(zhí)行任務,不能執(zhí)行直接cancel取消 if (!canRunInCurrentRunState(this)) cancel(false); else if (!isPeriodic()) // 如果不是周期性的,直接調(diào)用父類run方法執(zhí)行一次即可 super.run(); else if (super.runAndReset()) { // 周期性任務,調(diào)用runAndReset運行并重置 // 設置下一次的執(zhí)行時間 setNextRunTime(); // 將任務重新加入隊列,進行調(diào)度 reExecutePeriodic(outerTask); } } public boolean isPeriodic() { return period != 0; }
我們是周期性任務,所以直接看runAndReset
源碼
protected boolean runAndReset() { // 檢查任務狀態(tài),cas機制防止并發(fā)執(zhí)行任務 if (state != NEW || !RUNNER.compareAndSet(this, null, Thread.currentThread())) return false; // 默認不周期執(zhí)行任務 boolean ran = false; // state為NEW狀態(tài) int s = state; try { Callable<V> c = callable; if (c != null && s == NEW) { try { // 執(zhí)行任務 c.call(); // 正常執(zhí)行成功,設置為true代表周期執(zhí)行 ran = true; } catch (Throwable ex) { // 但是,如果執(zhí)行異常!則不會將ran = true,所以最終返回false setException(ex); } } } finally { runner = null; // 設置為NEW狀態(tài) s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } // 正常執(zhí)行完之后,結(jié)果為true,能夠周期執(zhí)行 // 但如果執(zhí)行異常,ran為false,返回結(jié)果為false return ran && s == NEW; }
通過上面源碼,我們可以很清楚的了解到,就是因為任務執(zhí)行異常,且沒有被try-catch,所以導致任務沒有被再次加入到隊列中進行調(diào)度。
并且通過文章開頭,我們還能看到任務執(zhí)行異常,但是卻沒有拋出異常信息
那是因為異常被封裝了,只有調(diào)用get方法時,才會拋出異常
/** The result to return or exception to throw from get() */ private Object outcome; private volatile int state; private static final int NEW = 0; private static final int COMPLETING = 1; private static final int NORMAL = 2; private static final int EXCEPTIONAL = 3; private static final int CANCELLED = 4; protected void setException(Throwable t) { if (STATE.compareAndSet(this, NEW, COMPLETING)) { // 將異常信息賦值給outcome // outcome既可以為任務執(zhí)行結(jié)果也可以為異常信息 outcome = t; // 將state設置為異常狀態(tài),state=3 STATE.setRelease(this, EXCEPTIONAL); // final state finishCompletion(); } } // 調(diào)用get方法阻塞獲取結(jié)果 public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); } private V report(int s) throws ExecutionException { Object x = outcome; // 此時s = EXCEPTIONAL = 3 if (s == NORMAL) return (V)x; if (s >= CANCELLED) throw new CancellationException(); // 所以會走到這里,對外拋出了任務執(zhí)行的異常 throw new ExecutionException((Throwable)x); }
以上就是“ScheduledThreadPoolExecutor的坑如何解決”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關(guān)注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。