摘要:的類圖如下實(shí)現(xiàn)了接口,繼承了接口,所以首先是一個(gè)線程池,然后除了具有線程池的功能,它還有定時(shí)和周期性執(zhí)行任務(wù)的功能。
在現(xiàn)實(shí)世界里,我們總是免不了要定期去做一件事情(比如上課)—— 在計(jì)算機(jī)的世界里,更是如此。比如我們手機(jī)每天叫我們起床的電子鬧鐘,某些網(wǎng)站會(huì)定期向我們發(fā)送一些推薦相關(guān)的郵件,集群中我們需要每隔一定時(shí)間檢查是否有機(jī)器宕機(jī)等。
在 使用線程池 中已經(jīng)介紹,JDK 1.5 時(shí),標(biāo)準(zhǔn)類庫(kù)添加了對(duì)線程池的支持,然后在線程池核心實(shí)現(xiàn) ThreadPoolExecutor 的基礎(chǔ)上,實(shí)現(xiàn)了 ScheduledThreadPoolExecutor,作為可以 定時(shí)和周期性執(zhí)行任務(wù) 的線程池。ScheduledThreadPoolExecutor 的類圖如下:
ScheduledThreadPoolExecutor 實(shí)現(xiàn)了 ScheduledExecutorService 接口,ScheduledExecutorService 繼承了 ExecutorService 接口,所以首先 ScheduledThreadPoolExecutor 是一個(gè) ExecutorService (線程池),然后除了具有線程池的功能,它還有定時(shí)和周期性執(zhí)行任務(wù)的功能。ScheduledExecutorService 除了從 ExecutorService 繼承的方法外,還包括如下四個(gè)方法:
第一個(gè) Schedule 方法:
delay 指定的時(shí)間后,執(zhí)行指定的 Runnable 任務(wù),可以通過返回的 ScheduledFuture> 與該任務(wù)進(jìn)行交互。
第二個(gè) Schedule 方法:
delay 指定的時(shí)間后,執(zhí)行指定的 Callable
(ScheduledFuture 接口 繼承自 Future 接口,所以 ScheduledFuture 和任務(wù)的交互方式與 Future 一致。所以通過ScheduledFuture,可以 判斷定時(shí)任務(wù)是否已經(jīng)完成,獲得定時(shí)任務(wù)的返回值,或者取消任務(wù)等)
scheduleAtFixedRate 方法:
initialDelay 指定的時(shí)間后,開始按周期 period 執(zhí)行指定的 Runnable 任務(wù)。
假設(shè)調(diào)用該方法后的時(shí)間點(diǎn)為 0,那么第一次執(zhí)行任務(wù)的時(shí)間點(diǎn)為 initialDelay,第二次為 initialDelay + period,第三次為 initialDelay + period + period,以此類推。
scheduleWithFixedDelay 方法:
initialDelay 指定的時(shí)間后,開始按指定的 delay 延期性的執(zhí)行指定的 Runnable 任務(wù)。
假設(shè)調(diào)用該方法后的時(shí)間點(diǎn)為 0,每次任務(wù)需要耗時(shí) T(i)(i 為第幾次執(zhí)行任務(wù)),那么第一次執(zhí)行任務(wù)的時(shí)間點(diǎn)為 initialDelay,第一次完成任務(wù)的時(shí)間點(diǎn)為 initialDelay + T(1),則第二次執(zhí)行任務(wù)的時(shí)間點(diǎn)為 initialDelay + T(1) + delay;第二次完成任務(wù)的時(shí)間點(diǎn)為 initialDelay + (T(1) + delay) + T(2),所以第三次執(zhí)行任務(wù)的時(shí)間點(diǎn)為 initialDelay + T(1) + delay + T(2) + delay,以此類推。
我們來實(shí)踐下 ScheduledThreadPoolExecutor 的 scheduleAtFixedRate 方法:
public class ScheduledExecutorServiceTest { public static void main(String[] args) throws Exception { ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor(); TimerTask timerTask = new TimerTask(2000); // 任務(wù)需要 2000 ms 才能執(zhí)行完畢 System.out.printf("起始時(shí)間:%s ", new SimpleDateFormat("HH:mm:ss").format(new Date())); // 延時(shí) 1 秒后,按 3 秒的周期執(zhí)行任務(wù) timer.scheduleAtFixedRate(timerTask, 1000, 3000, TimeUnit.MILLISECONDS); } private static class TimerTask implements Runnable { private final int sleepTime; private final SimpleDateFormat dateFormat; public TimerTask(int sleepTime) { this.sleepTime = sleepTime; dateFormat = new SimpleDateFormat("HH:mm:ss"); } @Override public void run() { System.out.println("任務(wù)開始,當(dāng)前時(shí)間:" + dateFormat.format(new Date())); try { System.out.println("模擬任務(wù)運(yùn)行..."); Thread.sleep(sleepTime); } catch (InterruptedException ex) { ex.printStackTrace(System.err); } System.out.println("任務(wù)結(jié)束,當(dāng)前時(shí)間:" + dateFormat.format(new Date())); System.out.println(); } } }
運(yùn)行結(jié)果:
可以看到運(yùn)行結(jié)果完全符合預(yù)期 —— 延時(shí) 1 秒后,每隔 3 秒執(zhí)行一次任務(wù)。
上面是任務(wù)的運(yùn)行時(shí)間小于周期時(shí)間的情況 —— 那如果任務(wù)運(yùn)行的時(shí)間大于給定的執(zhí)行周期呢?(比如任務(wù)運(yùn)行需要 3 s,但是我們指定的周期為 2 s)
修改 main 方法:
public static void main(String[] args) throws Exception { ScheduledExecutorService timer = Executors.newScheduledThreadPool(2); TimerTask timerTask = new TimerTask(3000); // 每個(gè)任務(wù)需要 3000 ms 才能執(zhí)行完畢 System.out.printf("起始時(shí)間:%s ", new SimpleDateFormat("HH:mm:ss").format(new Date())); timer.scheduleAtFixedRate(timerTask, 1000, 2000, TimeUnit.MILLISECONDS); }
運(yùn)行結(jié)果:
可以看到此時(shí)雖然我們指定的周期為 2 s,但是因?yàn)槿蝿?wù)的運(yùn)行就需要 3 s(超過周期),所以這種情況下 scheduleAtFixedRate 的處理方式為 上一次任務(wù)剛完成,則緊接著立即運(yùn)行下一次任務(wù),而不是使用線程池中的空閑線程來運(yùn)行任務(wù)以維護(hù) 2 秒這個(gè)周期 —— 由此可見,每個(gè)定時(shí)任務(wù)在 ScheduledThreadPoolExecutor 中,都是串行運(yùn)行的,即下一次運(yùn)行任務(wù)一定在上一次任務(wù)結(jié)束之后。
(scheduleWithFixedDelay 方法 的使用也十分簡(jiǎn)單,請(qǐng)有興趣的讀者自己實(shí)踐)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/66448.html
摘要:目標(biāo)線程由運(yùn)行狀態(tài)轉(zhuǎn)換為就緒狀態(tài),也就是讓出執(zhí)行權(quán)限,讓其他線程得以優(yōu)先執(zhí)行,但其他線程能否優(yōu)先執(zhí)行時(shí)未知的。函數(shù)的官方解釋是意思是使調(diào)用該函數(shù)的線程讓出執(zhí)行時(shí)間給其他已就緒狀態(tài)的線程。 線程允許在同一個(gè)進(jìn)程中同時(shí)存在多個(gè)程序控制流,即通過線程可以實(shí)現(xiàn)同時(shí)處理多個(gè)任務(wù)的功能。線程會(huì)共享進(jìn)程范圍內(nèi)的資源,例如內(nèi)存句柄和文件句柄,但每個(gè)線程都有各自的程序計(jì)數(shù)器、棧以及局部變量。 多線程的實(shí)...
摘要:也是自帶的一個(gè)基于線程池設(shè)計(jì)的定時(shí)任務(wù)類。其每個(gè)調(diào)度任務(wù)都會(huì)分配到線程池中的一個(gè)線程執(zhí)行,所以其任務(wù)是并發(fā)執(zhí)行的,互不影響。 原創(chuàng)不易,如需轉(zhuǎn)載,請(qǐng)注明出處https://www.cnblogs.com/baixianlong/p/10659045.html,否則將追究法律責(zé)任?。?! 一、在JAVA開發(fā)領(lǐng)域,目前可以通過以下幾種方式進(jìn)行定時(shí)任務(wù) 1、單機(jī)部署模式 Timer:jdk中...
摘要:創(chuàng)建線程的方式方式一將類聲明為的子類。將該線程標(biāo)記為守護(hù)線程或用戶線程。其中方法隱含的線程為父線程?;謴?fù)線程,已過時(shí)。等待該線程銷毀終止。更多的使當(dāng)前線程在鎖存器倒計(jì)數(shù)至零之前一直等待,除非線 知識(shí)體系圖: showImg(https://segmentfault.com/img/bVbef6v?w=1280&h=960); 1、線程是什么? 線程是進(jìn)程中獨(dú)立運(yùn)行的子任務(wù)。 2、創(chuàng)建線...
摘要:有三種狀態(tài)運(yùn)行關(guān)閉終止。類類,提供了一系列工廠方法用于創(chuàng)建線程池,返回的線程池都實(shí)現(xiàn)了接口。線程池的大小一旦達(dá)到最大值就會(huì)保持不變,在提交新任務(wù),任務(wù)將會(huì)進(jìn)入等待隊(duì)列中等待。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。 這是java高并發(fā)系列第19篇文章。 本文主要內(nèi)容 介紹Executor框架相關(guān)內(nèi)容 介紹Executor 介紹ExecutorService 介紹線程池ThreadP...
閱讀 2121·2023-04-26 02:23
閱讀 1859·2021-09-03 10:30
閱讀 1427·2019-08-30 15:43
閱讀 1262·2019-08-29 16:29
閱讀 623·2019-08-29 12:28
閱讀 2398·2019-08-26 12:13
閱讀 2357·2019-08-26 12:01
閱讀 2487·2019-08-26 11:56