一文帶你學(xué)會(huì)Java中ScheduledThreadPoolExecutor使用
1. 概要
前面文章的地址:
定時(shí)/延時(shí)任務(wù)-自己實(shí)現(xiàn)一個(gè)簡單的定時(shí)器
ScheduledThreadPoolExecutor 是 Java 并發(fā)包 (java.util.concurrent) 中的一個(gè)類,同時(shí)也是 ThreadPoolExecutor 的一個(gè)子類,這就意味者 ScheduledThreadPoolExecutor 不像 Timer 中使用單個(gè)線程去執(zhí)行任務(wù),ScheduledThreadPoolExecutor 使用了線程池去執(zhí)行,同時(shí) ScheduledThreadPoolExecutor 也具備了 Timer 中的各種功能。
2. 固定速率和固定延時(shí)
2.1 固定速率
固定速率 策略表示任務(wù)在固定的時(shí)間間隔內(nèi)重復(fù)執(zhí)行,不管任務(wù)的執(zhí)行時(shí)間有多長,如果任務(wù)的執(zhí)行時(shí)間超過了時(shí)間間隔,那么下一個(gè)任務(wù)會(huì)在當(dāng)前任務(wù)執(zhí)行完畢之后就會(huì)馬上開始執(zhí)行,下面是一個(gè)例子:假設(shè)我們設(shè)置了一個(gè)固定速率為 5 的任務(wù),從 0s 開始執(zhí)行,也就是說這個(gè)任務(wù) 5s 執(zhí)行一次:
- 第一次執(zhí)行: 在 0s 開始執(zhí)行一次,假設(shè)執(zhí)行時(shí)間是 3s
- 第二次執(zhí)行: 在 5s 開始執(zhí)行第二次,假設(shè)執(zhí)行的時(shí)候被阻塞了,執(zhí)行了 8s
- 第三次執(zhí)行: 在 13s 開始執(zhí)行第三次,假設(shè)執(zhí)行的時(shí)候被阻塞了,執(zhí)行了 3s
- 第四次執(zhí)行: 在 16s 開始執(zhí)行第四次,假設(shè)執(zhí)行的時(shí)候沒有被阻塞
- 第四次執(zhí)行: 在 20s 開始執(zhí)行第五次,假設(shè)執(zhí)行的時(shí)候沒有被阻塞
- …
2.2 固定延時(shí)
固定延時(shí) 策略表示任務(wù)在當(dāng)前任務(wù)執(zhí)行完成之后,固定延時(shí)一段時(shí)間再執(zhí)行下一個(gè)任務(wù),下面是一個(gè)例子:假設(shè)我們設(shè)置了一個(gè)固定速率為 5 的任務(wù),從 0s 開始執(zhí)行,也就是說這個(gè)任務(wù) 5s 執(zhí)行一次:
- 第一次執(zhí)行: 在 0s 開始執(zhí)行一次,假設(shè)執(zhí)行時(shí)間是 3s
- 第二次執(zhí)行: 在 8s 開始執(zhí)行第二次,假設(shè)執(zhí)行的時(shí)候被阻塞了,執(zhí)行了 8s
- 第三次執(zhí)行: 在 21s 開始執(zhí)行第三次,假設(shè)執(zhí)行的時(shí)候被阻塞了,執(zhí)行了 3s
- 第四次執(zhí)行: 在 29 開始執(zhí)行第四次,假設(shè)執(zhí)行的時(shí)候沒有被阻塞
- 第四次執(zhí)行: 在 34s 開始執(zhí)行第五次,假設(shè)執(zhí)行的時(shí)候沒有被阻塞
- …
上面的例子中其實(shí)固定速率的執(zhí)行和 Timer 是一樣的,但是固定延時(shí)有點(diǎn)不一樣,不知道你有沒有發(fā)現(xiàn),固定延時(shí)下執(zhí)行的時(shí)候每一個(gè)任務(wù)和前一個(gè)任務(wù)的時(shí)間間隔一定是 任務(wù)執(zhí)行時(shí)間 + 延時(shí)時(shí)間,其實(shí)相比于 Timer,ScheduledThreadPoolExecutor 的固定延時(shí)看起來更像是 “正宗” 一點(diǎn)的固定延時(shí)
3. API 解釋
3.1 schedule
還是老規(guī)矩,先看下 ScheduledThreadPoolExecutor 的幾個(gè) API,看看用法是什么樣的,首先就是 schedule 方法,這個(gè)方法就是普通的延時(shí)方法,只執(zhí)行一次,非周期調(diào)度
public class Pra { public static void main(String[] args) { ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(16); Thread thread = new Thread(() -> { try { System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime()); if (Math.random() < 0.5) { System.out.println("sleep: 3s"); Thread.sleep(3000); } else { System.out.println("sleep: 8s"); Thread.sleep(8000); } } catch (InterruptedException e) { e.printStackTrace(); } }, "thread-executor-run"); executor.schedule(thread, 0, TimeUnit.SECONDS); } private static String getTime() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); return sdf.format(new Date()); } }
執(zhí)行結(jié)果如下所示,延遲 0s 就開始執(zhí)行任務(wù)
ScheduledThreadPoolExecutor 也提供了一個(gè) callable 類型的任務(wù)的實(shí)現(xiàn),使用 Callable 的好處就是可以獲取任務(wù)的實(shí)現(xiàn)返回值,如下例子所示:
public class Pra { public static void main(String[] args) { ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(16); // 創(chuàng)建一個(gè) Callable 任務(wù) Callable<Integer> task = new Callable<Integer>() { @Override public Integer call() throws Exception { System.out.println("Task is running on thread: " + Thread.currentThread().getName()); return 1; // 返回結(jié)果 } }; // 調(diào)度 Callable 任務(wù),在 0 秒后執(zhí)行 ScheduledFuture<Integer> future = executor.schedule(task, 0, TimeUnit.SECONDS); try { // 獲取任務(wù)的結(jié)果 Integer result = future.get(); System.out.println("Task result: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } private static String getTime() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); return sdf.format(new Date()); } }
輸出結(jié)果如下所示:
3.2 固定延時(shí) - scheduleWithFixedDelay
public class Pra { public static void main(String[] args) { ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(16); Thread thread = new Thread(() -> { try { System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime()); if (Math.random() < 0.5) { System.out.println("sleep: 3s"); Thread.sleep(3000); } else { System.out.println("sleep: 8s"); Thread.sleep(8000); } } catch (InterruptedException e) { e.printStackTrace(); } }, "thread-executor-run"); executor.scheduleWithFixedDelay(thread, 0, 5, TimeUnit.SECONDS); } private static String getTime() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); return sdf.format(new Date()); } }
輸入如下所示:
來分析一下:
首先第一次任務(wù) 2024-11-24 19:19:36:698 啟動(dòng),然后執(zhí)行時(shí)間 3s
第二次任務(wù)在 2024-11-24 19:19:44:718 啟動(dòng),跟第一次任務(wù)相差 8s,延時(shí) = 5s + 3s = 8s
第三次任務(wù)在 2024-11-24 19:19:52:741 啟動(dòng),跟第二次任務(wù)相差 8s,延時(shí) = 5s + 3s = 8s
第四次任務(wù)在 2024-11-24 19:20:05:760 啟動(dòng),跟第三次任務(wù)相差 13s,延時(shí) = 5s + 8s = 13s
第五次任務(wù)在 2024-11-24 19:20:13:773 啟動(dòng),跟第四次任務(wù)相差 8s,延時(shí) = 5s + 3s = 8s
第六次任務(wù)在 2024-11-24 19:20:26:787 啟動(dòng),跟第五次任務(wù)相差 13s,延時(shí) = 5s + 8s = 13s
…
可以看到 scheduleWithFixedDelay 的延時(shí)是相對于當(dāng)前時(shí)間 + 延時(shí)時(shí)間的,跟 Timer 的固定延時(shí)任務(wù)有點(diǎn)不同
3.2 固定速率 - scheduleWithFixedDelay
public class Pra { public static void main(String[] args) { ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(16); Thread thread = new Thread(() -> { try { System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime()); if (Math.random() < 0.5) { System.out.println("sleep: 3s"); Thread.sleep(3000); } else { System.out.println("sleep: 8s"); Thread.sleep(8000); } } catch (InterruptedException e) { e.printStackTrace(); } }, "thread-executor-run"); executor.scheduleAtFixedRate(thread, 0, 5, TimeUnit.SECONDS); } private static String getTime() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); return sdf.format(new Date()); } }
輸出結(jié)果如下:
來分析下上面的執(zhí)行流程,需要注意的是,ScheduledThreadPoolExecutor 的固定速率任務(wù)會(huì)在當(dāng)前任務(wù)執(zhí)行完之后再添加下一次要執(zhí)行的任務(wù)
- 首先第一次任務(wù) 2024-11-24 16:23:25:762 啟動(dòng),然后執(zhí)行時(shí)間 8s,執(zhí)行完之后會(huì)添加一個(gè) 2024-11-24 16:23:30 s 執(zhí)行的任務(wù),當(dāng)前時(shí)間 2024-11-24 16:23:33 s
- 第二次任務(wù)在 2024-11-24 16:23:33:770 啟動(dòng),因?yàn)?2024-11-24 16:23:33:770 超過了 2024-11-24 16:23:30 s 這個(gè)時(shí)間點(diǎn), 所以立刻執(zhí)行,執(zhí)行時(shí)間 3s,執(zhí)行完之后往隊(duì)列里面添加一個(gè) 2024-11-24 16:23:35 s 執(zhí)行的任務(wù),當(dāng)前時(shí)間 2024-11-24 16:23:36 s
- 第二次任務(wù)在 2024-11-24 16:23:36:784 啟動(dòng),因?yàn)?2024-11-24 16:23:36:784 超過了 2024-11-24 16:23:35 s 這個(gè)時(shí)間點(diǎn), 所以立刻執(zhí)行,執(zhí)行時(shí)間 3s,執(zhí)行完之后往隊(duì)列里面添加一個(gè) 2024-11-24 16:23:40 s 執(zhí)行的任務(wù),當(dāng)前時(shí)間 2024-11-24 16:23:39 s
- 第四次任務(wù)在 2024-11-24 16:23:40:739 啟動(dòng),因?yàn)榈谌齻€(gè)任務(wù)執(zhí)行完之后時(shí)間是 2024-11-24 16:23:39 s,還沒有到第四個(gè)任務(wù)執(zhí)行的時(shí)間點(diǎn),這時(shí)候就等待,等到 2024-11-24 16:23:40:739,執(zhí)行時(shí)間 3s,執(zhí)行完之后往隊(duì)列里面添加一個(gè) 2024-11-24 16:23:45 s 執(zhí)行的任務(wù),執(zhí)行完的時(shí)間當(dāng)前是 2024-11-24 16:23:42 s
- 第五次任務(wù)在 2024-11-24 16:23:45:751 啟動(dòng),因?yàn)榈谒膫€(gè)任務(wù)執(zhí)行完之后時(shí)間是 2024-11-24 16:23:42 s,還沒有到第五個(gè)任務(wù)執(zhí)行的時(shí)間點(diǎn),這時(shí)候就等待,等到 2024-11-24 16:23:45:751,執(zhí)行時(shí)間 8s,執(zhí)行完之后往隊(duì)列里面添加一個(gè) 2024-11-24 16:23:50 s 執(zhí)行的任務(wù),執(zhí)行完的時(shí)間當(dāng)前是 2024-11-24 16:23:53 s
- 第六次任務(wù)在 2024-11-24 16:23:53:763 啟動(dòng),因?yàn)榈诹鶄€(gè)任務(wù)執(zhí)行時(shí)間是 2024-11-24 16:23:50 s,而執(zhí)行完第五個(gè)任務(wù)已經(jīng)是 2024-11-24 16:23:53 s了,超過任務(wù)的執(zhí)行時(shí)間,所以會(huì)立馬執(zhí)行,執(zhí)行時(shí)間 3s,執(zhí)行完后往隊(duì)列里面添加一個(gè) 2024-11-24 16:23:55 s 的任務(wù),當(dāng)前時(shí)間是 2024-11-24 16:23:56 s
- …
其實(shí)上面的解釋中可以把后面的毫秒去掉,這樣比較方便理解
4. 小結(jié)
上面就是幾個(gè) API 了,其中固定速率的和 Timer 的實(shí)現(xiàn)方式有點(diǎn)不同,后續(xù)文章中,我會(huì)對 ScheduledThreadPoolExecutor 的源碼進(jìn)行詳細(xì)的解析,當(dāng)然在解析 ScheduledThreadPoolExecutor 之前首先需要知道下線程池的工作原理和工作流程。
以上就是一文帶你學(xué)會(huì)Java中ScheduledThreadPoolExecutor使用的詳細(xì)內(nèi)容,更多關(guān)于Java ScheduledThreadPoolExecutor的資料請關(guān)注腳本之家其它相關(guān)文章!
- Java自帶定時(shí)任務(wù)ScheduledThreadPoolExecutor實(shí)現(xiàn)定時(shí)器和延時(shí)加載功能
- java 定時(shí)器線程池(ScheduledThreadPoolExecutor)的實(shí)現(xiàn)
- java高并發(fā)ScheduledThreadPoolExecutor與Timer區(qū)別
- 詳解Java ScheduledThreadPoolExecutor的踩坑與解決方法
- java高并發(fā)ScheduledThreadPoolExecutor類深度解析
- Java調(diào)度線程池ScheduledThreadPoolExecutor不執(zhí)行問題分析
- Java定時(shí)任務(wù)ScheduledThreadPoolExecutor示例詳解
- Java中的ScheduledThreadPoolExecutor定時(shí)任務(wù)詳解
相關(guān)文章
Spring MVC過濾器-登錄過濾的代碼實(shí)現(xiàn)
本篇文章主要介紹了Spring MVC過濾器-登錄過濾,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧。2017-01-01Spring boot中filter類不能注入@Autowired變量問題
這篇文章主要介紹了Spring boot中filter類不能注入@Autowired變量問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09Java開發(fā)中POJO和JSON互轉(zhuǎn)時(shí)如何忽略隱藏字段的問題
這篇文章主要介紹了Java開發(fā)中POJO和JSON互轉(zhuǎn)時(shí)如何忽略隱藏字段的問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02java用LocalDateTime類獲取當(dāng)天時(shí)間、前一天時(shí)間及本周/本月的開始和結(jié)束時(shí)間
這篇文章主要給大家介紹了關(guān)于java使用LocalDateTime類獲取當(dāng)天時(shí)間、前一天時(shí)間及本周/本月的開始和結(jié)束時(shí)間的相關(guān)資料,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08詳解Java中的反射機(jī)制和動(dòng)態(tài)代理
本文將詳細(xì)介紹反射機(jī)制以及動(dòng)態(tài)代理機(jī)制,而且基本現(xiàn)在的主流框架都應(yīng)用了反射機(jī)制,如spring、MyBatis、Hibernate等等,這就有非常重要的學(xué)習(xí)意義2021-06-06