ScheduledThreadPoolExecutor學習

簡介

ScheduledThreadPoolExecutor 是 Java 中的一個類,它屬于 java.util.concurrent 包。這個類是一個線程池,用于在給定的延遲后運行命令,或者定期地執行命令。它是 ThreadPoolExecutor 的一個子類,專門用于處理需要定時或周期性執行的任務。

ScheduledThreadPoolExecutor 的主要特點如下:

  • 線程池大小固定:線程池的大小在創建時就已經設定,并且之后不會改變。
  • 任務調度:它可以用來調度一次性任務,也可以用來調度重復執行的任務。
  • 延遲執行:任務可以在給定的延遲之后開始執行。
  • 周期性執行:任務可以周期性地執行,例如每隔固定時間執行一次。

ScheduledThreadPoolExecutor 提供的主要方法包括:

  • schedule(Runnable command, long delay, TimeUnit unit): 在給定的延遲后執行一次任務。
  • scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit): 以固定的速率周期性地執行任務。
  • scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit): 在給定的初始延遲后首次執行任務,然后每次任務執行完畢之后等待指定的延遲再次執行。

源碼

public class ScheduledThreadPoolExecutorextends ThreadPoolExecutorimplements ScheduledExecutorService {public ScheduledFuture<?> schedule(Runnable command,long delay,TimeUnit unit) {//任務或單位為空 則拋出異常if (command == null || unit == null)throw new NullPointerException();//把要執行的任務包裝成 RunnableScheduledFutureRunnableScheduledFuture<?> t = decorateTask(command,new ScheduledFutureTask<Void>(command, null,triggerTime(delay, unit)));//延時執行定時任務delayedExecute(t);return t;}//邏輯同上public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay,TimeUnit unit) {if (callable == null || unit == null)throw new NullPointerException();RunnableScheduledFuture<V> t = decorateTask(callable,new ScheduledFutureTask<V>(callable,triggerTime(delay, unit)));delayedExecute(t);return t;}public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit) {//任務和單位為空則拋出異常if (command == null || unit == null)throw new NullPointerException();if (period <= 0)//執行周期時間小于等于0 則拋出異常throw new IllegalArgumentException();//將任務包裝成ScheduledFutureTaskScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit),unit.toNanos(period));//然后包裝成RunnableScheduledFutureRunnableScheduledFuture<Void> t = decorateTask(command, sft);sft.outerTask = t;//延時執行任務delayedExecute(t);return t;}//同上public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit) {if (command == null || unit == null)throw new NullPointerException();if (delay <= 0)throw new IllegalArgumentException();ScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit),unit.toNanos(-delay));RunnableScheduledFuture<Void> t = decorateTask(command, sft);sft.outerTask = t;delayedExecute(t);return t;}//所謂的包裝就是直接返回RunnableScheduledFuture對象protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) {return task;}//從scheduleWithFixedDelay方法的源代碼,我們可以看出在將Runnable對象封裝成ScheduledFutureTask時,設置了執行周期,//但是此時設置的執行周期與scheduleAtFixedRate方法設置的執行周期不同。//此時設置的執行周期規則為:下一次任務執行的時間是上一次任務完成的時間加上delay時長,時長單位由TimeUnit決定。//也就是說,具體的執行時間不是固定的,但是執行的周期是固定的,整體采用的是相對固定的延遲來執行定時任務public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit) {//傳入的Runnable對象和TimeUnit為空,則拋出空指針異常if (command == null || unit == null)throw new NullPointerException();//任務延時時長小于或者等于0,則拋出非法參數異常if (delay <= 0)throw new IllegalArgumentException();//將Runnable對象封裝成ScheduledFutureTask任務//并設置固定的執行周期來執行任務ScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit),unit.toNanos(-delay));//調用decorateTask方法,本質上直接返回ScheduledFutureTask任務RunnableScheduledFuture<Void> t = decorateTask(command, sft);//設置執行的任務sft.outerTask = t;//執行延時任務delayedExecute(t);return t;}private void setNextRunTime() {//距離下次執行任務的時長long p = period;//固定頻率執行,//上次執行任務的時間//加上任務的執行周期if (p > 0)time += p;//相對固定的延遲//使用的是系統當前時間//加上任務的執行周期elsetime = triggerTime(-p);}//這兩個triggerTime方法的代碼比較簡單,就是獲取下一次執行任務的具體時間。//有一點需要注意的是:delay <(Long.MAX_VALUE >> 1判斷delay的值是否小于Long.MAX_VALUE的一半,//如果小于Long.MAX_VALUE值的一半,則直接返回delay,否則需要處理溢出的情況。private long triggerTime(long delay, TimeUnit unit) {return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));}long triggerTime(long delay) {return now() +((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));}private long overflowFree(long delay) {//獲取隊列中的節點Delayed head = (Delayed) super.getQueue().peek();//獲取的節點不為空,則進行后續處理if (head != null) {//從隊列節點中獲取延遲時間long headDelay = head.getDelay(NANOSECONDS);//如果從隊列中獲取的延遲時間小于0,并且傳遞的delay//值減去從隊列節點中獲取延遲時間小于0if (headDelay < 0 && (delay - headDelay < 0))//將delay的值設置為Long.MAX_VALUE + headDelaydelay = Long.MAX_VALUE + headDelay;}//返回延遲時間return delay;}private void delayedExecute(RunnableScheduledFuture<?> task) {//如果當前線程池已經關閉//則執行線程池的拒絕策略if (isShutdown())reject(task);//線程池沒有關閉else {//將任務添加到阻塞隊列中super.getQueue().add(task);//如果當前線程池是SHUTDOWN狀態//并且當前線程池狀態下不能執行任務//并且成功從阻塞隊列中移除任務if (isShutdown() &&!canRunInCurrentRunState(task.isPeriodic()) &&remove(task))//取消任務的執行,但不會中斷執行中的任務task.cancel(false);else//調用ThreadPoolExecutor類中的ensurePrestart()方法ensurePrestart();}}void reExecutePeriodic(RunnableScheduledFuture<?> task) {//線程池當前狀態下能夠執行任務if (canRunInCurrentRunState(true)) {//將任務放入隊列super.getQueue().add(task);//線程池當前狀態下不能執行任務,并且成功移除任務if (!canRunInCurrentRunState(true) && remove(task))//取消任務task.cancel(false);else//調用ThreadPoolExecutor類的ensurePrestart()方法ensurePrestart();}}//ScheduledThreadPoolExecutor類中的onShutdown方法的主要邏輯就是先判斷線程池調用shutdown方法后,是否繼續執行現有的延遲任務和定時任務,//如果不再執行,則取消任務并清空隊列;如果繼續執行,將隊列中的任務強轉為RunnableScheduledFuture對象之后,從隊列中刪除并取消任務。//大家需要好好理解這兩種處理方式。最后調用ThreadPoolExecutor類的tryTerminate方法。@Overridevoid onShutdown() {//獲取隊列BlockingQueue<Runnable> q = super.getQueue();//在線程池已經調用shutdown方法后,是否繼續執行現有延遲任務boolean keepDelayed = getExecuteExistingDelayedTasksAfterShutdownPolicy();//在線程池已經調用shutdown方法后,是否繼續執行現有定時任務boolean keepPeriodic = getContinueExistingPeriodicTasksAfterShutdownPolicy();//在線程池已經調用shutdown方法后,不繼續執行現有延遲任務和定時任務if (!keepDelayed && !keepPeriodic) {//遍歷隊列中的所有任務for (Object e : q.toArray())//取消任務的執行if (e instanceof RunnableScheduledFuture<?>)((RunnableScheduledFuture<?>) e).cancel(false);//清空隊列q.clear();}//在線程池已經調用shutdown方法后,繼續執行現有延遲任務和定時任務else {//遍歷隊列中的所有任務for (Object e : q.toArray()) {//當前任務是RunnableScheduledFuture類型if (e instanceof RunnableScheduledFuture) {//將任務強轉為RunnableScheduledFuture類型RunnableScheduledFuture<?> t = (RunnableScheduledFuture<?>) e;//在線程池調用shutdown方法后不繼續的延遲任務或周期任務//則從隊列中刪除并取消任務if ((t.isPeriodic() ? !keepPeriodic : !keepDelayed) ||t.isCancelled()) {if (q.remove(t))t.cancel(false);}}}}//最終調用tryTerminate()方法tryTerminate();}}

示例

import java.util.concurrent.Executors;  
import java.util.concurrent.ScheduledThreadPoolExecutor;  
import java.util.concurrent.TimeUnit;  public class ScheduledThreadPoolExecutorExample {  public static void main(String[] args) {  // 創建一個包含單個線程的ScheduledThreadPoolExecutor  ScheduledThreadPoolExecutor executor = Executors.newScheduledThreadPool(1);  // 創建一個Runnable任務  Runnable task = () -> System.out.println("Task is running: " + System.currentTimeMillis());  // 在10秒后執行該任務  executor.schedule(task, 10, TimeUnit.SECONDS);  // 每隔5秒執行一次該任務  executor.scheduleAtFixedRate(task, 0, 5, TimeUnit.SECONDS);  // 在初始延遲15秒后執行,之后每次任務執行完等待2秒再次執行  executor.scheduleWithFixedDelay(task, 15, 2, TimeUnit.SECONDS);  // 注意:在實際應用中,你應該適當地關閉線程池以避免資源泄露  // executor.shutdown(); // 這將平滑地關閉線程池,等待所有任務完成  }  
}

在這個例子中,我們創建了一個 ScheduledThreadPoolExecutor 實例,它包含單個線程。我們定義了三個不同的任務調度:一個在10秒后執行,一個每隔5秒執行一次,還有一個在初始延遲15秒后執行,然后每次任務執行完畢之后等待2秒再次執行。

請注意,在實際應用中,當你不再需要 ScheduledThreadPoolExecutor 時,應該調用 shutdown 或 shutdownNow 方法來關閉線程池,以避免資源泄露。同時,線程池中的任務也應該設計為能夠在適當的時候結束執行,避免無限期地占用資源。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/712351.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/712351.shtml
英文地址,請注明出處:http://en.pswp.cn/news/712351.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

解釋索引是什么以及它們是如何提高查詢性能的

索引在數據庫管理系統中是一個重要的數據結構&#xff0c;用于幫助快速檢索數據庫表中的數據。它可以被看作是一個指向表中數據的指針列表&#xff0c;這些指針按照某種特定的順序&#xff08;如字母順序或數字順序&#xff09;排列。索引的工作原理類似于書籍的目錄&#xff1…

Python爬蟲實戰第二例【二】

零.前言&#xff1a; 本文章借鑒&#xff1a;Python爬蟲實戰&#xff08;五&#xff09;&#xff1a;根據關鍵字爬取某度圖片批量下載到本地&#xff08;附上完整源碼&#xff09;_python爬蟲下載圖片-CSDN博客 大佬的文章里面有API的獲取&#xff0c;在這里我就不贅述了。 一…

kitex 入門和基于grpc的使用

&#x1f4d5;作者簡介&#xff1a; 過去日記&#xff0c;致力于Java、GoLang,Rust等多種編程語言&#xff0c;熱愛技術&#xff0c;喜歡游戲的博主。 &#x1f4d7;本文收錄于kitex系列&#xff0c;大家有興趣的可以看一看 &#x1f4d8;相關專欄Rust初階教程、go語言基礎系…

【Web】青少年CTF擂臺挑戰賽 2024 #Round 1 wp

好家伙&#xff0c;比賽結束了還有一道0解web題是吧( 隨緣寫點wp(簡單過頭&#xff0c;看個樂就好) 目錄 EasyMD5 PHP的后門 PHP的XXE Easy_SQLi 雛形系統 EasyMD5 進來是個文件上傳界面 說是只能上傳pdf&#xff0c;那就改Content-Type為application/pdf&#xff0c;改…

11.盛最多水的容器

題目&#xff1a;給定一個長度為 n 的整數數組 height 。有 n 條垂線&#xff0c;第 i 條線的兩個端點是 (i, 0) 和 (i, height[i]) 。 找出其中的兩條線&#xff0c;使得它們與 x 軸共同構成的容器可以容納最多的水。 返回容器可以儲存的最大水量。 解題思路&#xff1a;可以…

判斷閏年(1000-2000)

判斷規則&#xff1a;1.能被4整除&#xff0c;不能被100整除是閏年,2.能被400整除是閏年 #include <stdio.h>int is_leap_year(int n){if((n % 400 0)||((n % 4 0)&&(n % 100 ! 0)))return 1;elsereturn 0; } int main() {int i 0;int count 0;for(i 1000;…

基于PHP的在線英語學習平臺

有需要請加文章底部Q哦 可遠程調試 基于PHP的在線英語學習平臺 一 介紹 此在線英語學習平臺基于原生PHP開發&#xff0c;數據庫mysql。系統角色分為學生&#xff0c;教師和管理員。(附帶參考設計文檔) 技術棧&#xff1a;phpmysqlphpstudyvscode 二 功能 學生 1 注冊/登錄/…

C++/Python簡單練手題

前言 最近需要開始使用python&#xff0c;但是對python了解的并不多&#xff0c;于是先從很早之前剛學C時寫過的一些練手題開始&#xff0c;使用python來實現相同的功能&#xff0c;在溫習python基礎語法的同時&#xff0c;也一起來感受感受python的魅力 99乘法表 c&#xf…

kettle開發-Day43-加密環境下運行作業

前言&#xff1a; 金三銀四&#xff0c;開年第一篇我們來介紹下&#xff0c;怎么在加密情況下運行我們的kettle作業及任務。無疑現在所有企業都認識到加密的重要性&#xff0c;加密后的文件在對外傳輸的時候不能被訪問&#xff0c;訪問時出現一堆亂碼&#xff0c;同時正常的應用…

1分鐘學會Python字符串前后綴與編解碼

1.前綴和后綴 前綴和后綴指的是&#xff1a;字符串是否以指定字符開頭和結尾 2.startswith() 判斷字符串是否以指定字符開頭&#xff0c;若是返回True&#xff0c;若不是返回False str1 "HelloPython"print(str1.startswith("Hello")) # Trueprint…

Navicat Premium 16:打破數據庫界限,實現高效管理mac/win版

Navicat Premium 16是一款功能強大的數據庫管理工具&#xff0c;旨在幫助用戶更輕松地連接、管理和保護各種數據庫。該軟件支持多種數據庫系統&#xff0c;如MySQL、Oracle、SQL Server、PostgreSQL等&#xff0c;并提供了直觀的圖形界面&#xff0c;使用戶能夠輕松地完成各種數…

【力扣白嫖日記】585.2016年的投資

前言 練習sql語句&#xff0c;所有題目來自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免費數據庫練習題。 今日題目&#xff1a; 585.2016年的投資 表&#xff1a;Person 列名類型pidinttiv_2015floattiv_2016floatlatfloatlonfloat pid …

AI也來打摜蛋,難道人工智能也能當領導?

在人工智能&#xff08;AI&#xff09;的研究領域中&#xff0c;游戲被視為現實世界的簡化模型&#xff0c;常常是研究的首選平臺。這些研究主要關注游戲代理的決策過程。例如&#xff0c;中國的傳統卡牌游戲“摜蛋”&#xff08;字面意思是“扔雞蛋”&#xff09;就是一個挑戰…

Unity(第十七部)Unity自帶的角色控制器

組件Character Controller 中文角色控制器 using System.Collections; using System.Collections.Generic; using UnityEngine;public class player : MonoBehaviour {private CharacterController player;void Start(){player GetComponent<CharacterController>();}v…

對于爬蟲的學習

本地爬取 package MyApi.a08regexdemo;import java.util.regex.Matcher; import java.util.regex.Pattern;public class RegexDemo03 {public static void main(String[] args) {//要求&#xff1a;找出里面所有javaxxString str"Java自從95年問世以來&#xff0c;經歷了…

騰訊日常實習-數據科學-初試涼經

個人背景&#xff1a;雙985 騰訊會議面了一個小時左右&#xff0c;過程如下&#xff1a; 1.面試官首先介紹了一下部門&#xff08;騰訊云&#xff09;的情況和業務方向。 2.讓我介紹一下自己&#xff08;目前情況&#xff0c;科研經歷&#xff0c;項目經歷&#xff09;。 3.就我…

HarmonyOS—編譯構建概述

編譯構建是將應用/服務的源代碼、資源、第三方庫等&#xff0c;通過編譯工具轉換為可直接在硬件設備上運行的二進制機器碼&#xff0c;然后再將二進制機器碼封裝為HAP/APP軟件包&#xff0c;并為HAP/APP包進行簽名的過程。其中&#xff0c;HAP是可以直接運行在模擬器或真機設備…

牛皮癬發作和復發的觸發因素

谷禾健康 銀屑病&#xff0c;又叫牛皮癬&#xff0c;會導致出現皮疹伴發癢的鱗狀斑塊&#xff0c;最常見于膝蓋、肘部、軀干和頭皮。通常呈周期性發展&#xff0c;發作數周或數月&#xff0c;然后消退一段時間&#xff0c;長期的發作和復發會給患者帶來很大的痛苦和困擾&#x…

Qt5.9.9交叉編譯(帶sqlite3、OpenSSL)

1、交叉編譯工具鏈 這里ARM平臺是ARM CortexA9的&#xff0c;一般交叉編譯工具鏈demo板廠商都會提供&#xff0c;若未提供或想更換新版本的交叉編譯工具鏈可參考以下方式獲取。 1.1 下載適用于ARM CortexA9的交叉編譯工具鏈 Linaro Releases下載gcc4的最新版xxxx-i686_arm-li…

洛谷P1009階乘之和

題目描述 用高精度計算出S1!2!3!?n!&#xff08;n≤50&#xff09;。 其中 ! 表示階乘&#xff0c;定義為 n!n(n?1)(n?2)?1。例如&#xff0c;5!543211205!54321120。 輸入格式 一個正整數 n。 輸出格式 一個正整數 S&#xff0c;表示計算結果。 輸入輸出樣例 輸入…