您好,登錄后才能下訂單哦!
這篇文章給大家介紹如何進(jìn)行ScheduledThreadPoolExecutor分析與線程池防坑,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
線程池默認(rèn)使用無界隊(duì)列,任務(wù)過多時(shí),JVM OOM
不設(shè)置最大線程數(shù),線程數(shù)目暴漲
定時(shí)執(zhí)行,執(zhí)行一次任務(wù)耗時(shí)太長甚至一直阻塞,達(dá)不到定時(shí)執(zhí)行的目的
線程池內(nèi)的線程是非守護(hù)線程,停止JVM時(shí)出問題
使用submit方法,沒有調(diào)用future.get()導(dǎo)致異常被吞,execute不會有異常被吞的問題
死鎖問題,并不常見,但是有時(shí)候?qū)憦?fù)雜了可能會出現(xiàn)。一句話,使用線程池和諸如阻塞隊(duì)列這種抽象層次比較高的工具時(shí),盡量不要再用低層次的類,如lock,wait(),notify()等,這樣混用會導(dǎo)致難以排查的問題。
ScheduledExecutorService scheduledService = Executors.newScheduledThreadPool(4); scheduledService.scheduleAtFixedRate(() -> { try { System.out.println("pool thread:" + Thread.currentThread().isDaemon()); System.out.println("start at:" + new Date()); Thread.sleep(10000); System.out.println("end at:" + new Date()); } catch (InterruptedException e) { e.printStackTrace(); } }, 0, 5, TimeUnit.SECONDS);
上面的代碼塊意圖每5秒執(zhí)行一次任務(wù),但是執(zhí)行這個(gè)任務(wù)需要耗費(fèi)10秒(sleep),那么肯定不能達(dá)到5秒一次的效果。分析一下原因。
定時(shí)執(zhí)行功能的實(shí)現(xiàn)類是ScheduledThreadPoolExecutor
,它是一個(gè)線程池加延遲隊(duì)列來實(shí)現(xiàn)的,延遲隊(duì)列是使用堆來實(shí)現(xiàn)的,也就是根元素值最大或者值最小,在定時(shí)任務(wù)執(zhí)行這個(gè)場景下,根元素就是下一個(gè)要執(zhí)行的任務(wù),然后有一個(gè)等待時(shí)間。
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); if (period <= 0) throw new IllegalArgumentException(); ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command, null, triggerTime(initialDelay, unit), unit.toNanos(period)); RunnableScheduledFuture<Void> t = decorateTask(command, sft); sft.outerTask = t; delayedExecute(t); return t; }
然后看ScheduledFutureTask
這個(gè)類的run()
方法
public void run() { boolean periodic = isPeriodic();//是否周期執(zhí)行 if (!canRunInCurrentRunState(periodic)) cancel(false); else if (!periodic) ScheduledFutureTask.super.run();//不是執(zhí)行一次就完了 else if (ScheduledFutureTask.super.runAndReset()) {//執(zhí)行 setNextRunTime();//設(shè)置下次時(shí)間 reExecutePeriodic(outerTask);//加入隊(duì)列 } }
void reExecutePeriodic(RunnableScheduledFuture<?> task) { if (canRunInCurrentRunState(true)) { super.getQueue().add(task); if (!canRunInCurrentRunState(true) && remove(task)) task.cancel(false); else ensurePrestart();//決定線程池是否新增線程,未達(dá)到核心線程數(shù),有新任務(wù)則加線程 } }
可以看到如果一次執(zhí)行時(shí)間很長,是達(dá)不到定時(shí)執(zhí)行的目的,最終的表現(xiàn)就是一次連著一次執(zhí)行,如果我們確實(shí)需要精準(zhǔn)的定期執(zhí)行,不關(guān)注每次多長時(shí)間,該怎么辦?很簡單,再弄一個(gè)線程池專門用來干活
關(guān)于如何進(jìn)行ScheduledThreadPoolExecutor分析與線程池防坑就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。