SpringBoot - 四種常見定時器

常見實現方案

  • @Scheduled注解:基于注解
  • Timer().schedule創建任務:基于封裝類Timer
  • 線程:使用線程直接執行任務即可,可以與thread、線程池、ScheduleTask等配合使用
  • quartz配置定時器:基于springquartz框架


@Scheduled注解實現定時器

使用注解標記需要定時執行的方法,并設置執行時間,便可使其在指定的時間執行指定方法

步驟:

  • 使用注解@Scheduled標記目標方法,參數為執行時間
  • 使用注解@EnableScheduling標記目標方法所在的類,或者直接標記項目啟動類

@Scheduled(fixedDelay = 5000):方法執行完成后等待5秒再次執行

@Scheduled(fixedRate = 5000):方法每隔5秒執行一次

@Scheduled(initialDelay=1000, fixedRate=5000):延遲1秒后執行第一次,之后每隔5秒執行一次

fixedDelayString、fixedRateString、initialDelayString:與上訴三種作用一直,但參數為字符串類型,因而可以使用占位符,形如@Scheduled(fixedDelayString = "${time.fixedDelay}")

@Scheduled(cron = "0 0,30 0,8 ? * ? "):方法在每天的8點30分0秒執行,參數為字符串類型,那么同理也可使用占位符

cron 該參數接收一個cron表達式cron表達式是一個字符串,字符串以5或6個空格隔開,分開共6或7個域,[年]不是必須的域,可以省略[年],則一共6個域

[秒] [分] [小時] [日] [月] [周] [年]
序號說明必填允許填寫的值允許的通配符
10-59, - * /
20-59, - * /
30-23, - * /
41-31, - * ? / L W
51-12 / JAN-DEC, - * /
61-7 or SUN-SAT, - * ? / L #
71970-2099, - * /

通配符說明:

  • * 表示所有值。 例如:在分的字段上設置 *,表示每一分鐘都會觸發。
  • ? 表示不指定值。使用的場景為不需要關心當前設置這個字段的值。例如:要在每月的10號觸發一個操作,但不關心是周幾,所以需要周位置的那個字段設置為”?” 具體設置為 0 0 0 10 * ?
  • - 表示區間。例如 在小時上設置 “10-12”,表示 10,11,12點都會觸發。
  • , 表示指定多個值,例如在周字段上設置 “MON,WED,FRI” 表示周一,周三和周五觸發
  • / 用于遞增觸發。如在秒上面設置”5/15” 表示從5秒開始,每增15秒觸發(5,20,35,50)。 在日字段上設置’1/3’所示每月1號開始,每隔三天觸發一次。
  • L 表示最后的意思。在日字段設置上,表示當月的最后一天(依據當前月份,如果是二月還會依據是否是潤年[leap]), 在周字段上表示星期六,相當于”7”或”SAT”。如果在”L”前加上數字,則表示該數據的最后一個。例如在周字段上設置”6L”這樣的格式,則表示“本月最后一個星期五”
  • W 表示離指定日期的最近那個工作日(周一至周五). 例如在日字段上置”15W”,表示離每月15號最近的那個工作日觸發。如果15號正好是周六,則找最近的周五(14號)觸發, 如果15號是周未,則找最近的下周一(16號)觸發.如果15號正好在工作日(周一至周五),則就在該天觸發。如果指定格式為 “1W”,它則表示每月1號往后最近的工作日觸發。如果1號正是周六,則將在3號下周一觸發。(注,”W”前只能設置具體的數字,不允許區間”-“)。
  • # 序號(表示每月的第幾個周幾),例如在周字段上設置”6#3”表示在每月的第三個周六.注意如果指定”#5”,正好第五周沒有周六,則不會觸發該配置(用在母親節和父親節再合適不過了) ;小提示:’L’和 ‘W’可以一組合使用。如果在日字段上設置”LW”,則表示在本月的最后一個工作日觸發;周字段的設置,若使用英文字母是不區分大小寫的,即MON與mon相同。

示例

每隔5秒執行一次:*/5 * * * * ?

每隔1分鐘執行一次:0 */1 * * * ?

每天23點執行一次:0 0 23 * * ?

每天凌晨1點執行一次:0 0 1 * * ?

每月1號凌晨1點執行一次:0 0 1 1 * ?

每月最后一天23點執行一次:0 0 23 L * ?

每周星期六凌晨1點實行一次:0 0 1 ? * L

在26分、29分、33分執行一次:0 26,29,33 * * * ?

每天的0點、13點、18點、21點都執行一次:0 0 0,13,18,21 * * ?

使用占位符

另外,cron屬性接收的cron表達式支持占位符

配置文件:

time:cron: */5 * * * * *interval: 5

每5秒執行一次:

    @Scheduled(cron="${time.cron}")void testPlaceholder1() {System.out.println("Execute at " + System.currentTimeMillis());}@Scheduled(cron="*/${time.interval} * * * * *")void testPlaceholder2() {System.out.println("Execute at " + System.currentTimeMillis());}

第一次等待10秒,之后每3秒一次

@Component
@EnableScheduling
public class ScheduleTest {private int count = 0;/*** 第一次等待10秒,之后每3秒鐘執行一次*/@Scheduled(initialDelay = 10000, fixedRate = 3000)public void test1() {System.out.println(count + ":" + (new Date()).toString());count++;}}

?


Timer().schedule實現定時器

核心包括Timer和TimerTask,均為jkd自帶的工具類

TimerTask實際上就是一個Runnable而已,繼承Runnable并添加了幾個自定義的參數和方法

Timer字面意思即定時器,為jkd自帶的工具類,提供定時執行任務的相關功能

實際上包括三個類:

Timer:即定時器主類,負責管理所有的定時任務,每個Timer擁有一個私有的TaskQueue和TimerThread,

TaskQueue:即任務隊列,Timer生產任務,然后推到TaskQueue里存放,等待處理,被處理掉的任務即被移除掉

TaskQueue實質上只有一個長度為128的數組用于存儲TimerTask、一個int型變量size表示隊列長度、以及對這兩個數據的增刪改查

TimerThread:即定時器線程,線程會共享TaskQueue里面的數據,TimerThread會對TaskQueue里的任務進行消耗

TimerThread實際上就是一個Thread線程,會不停的監聽TaskQueue,如果隊列里面有任務,那么就執行第一個,并將其刪除(先刪除再執行)

流程分析

Timer生產任務(實際上是從外部接收到任務),并將任務推到TaskQueue里面存放,并喚醒TaskQueue線程(queue.notify())
TimerThread監聽TaskQueue,若里面有任務則將其執行并移除隊里,若沒有任務則讓隊列等待(queue.wait())

構造

public Timer(String name, boolean isDaemon)
  • name:即線程名,用于區分不同的線程,缺省的時候默認使用"Timer-" + serialNumber()生成唯一線程名
  • isDaemon:是否是守護線程,缺省的時候默認為否

方法

schedule(TimerTask task, long delay):指定任務task,在delay毫秒延遲后執行


schedule(TimerTask task, Date time):指定任務task,在time時間點執行一次


schedule(TimerTask task, long delay, long period):指定任務task,延遲delay毫秒后執行第一次,并在之后每隔period毫秒執行一次


schedule(TimerTask task, Date firstTime, long period):指定任務task,在firstTime的時候執行第一次,之后每隔period毫秒執行一次


scheduleAtFixedRate(TimerTask task, long delay, long period):作用與schedule一致
scheduleAtFixedRate(TimerTask task, Date firstTime, long period):作用與schedule一致

實際上最后都會使用sched(TimerTask task, long time, long period),即指定任務task,在time執行第一次,之后每隔period毫秒執行一次

schedule使用系統時間計算下一次,即System.currentTimeMillis()+period

而scheduleAtFixedRate使用本次預計時間計算下一次,即time + period

對于耗時任務,兩者區別較大,請按需求選擇,瞬時任務無區別

取消任務方法:cancel(),會將任務隊列清空,并堵塞線程,且不再能夠接受任務(接受時報錯),并不會銷毀本身的實例和其內部的線程

凈化方法:purge(),凈化會將隊列里所有被取消的任務移除,對剩余任務進行堆排序,并返回移除任務的數量

@Component
public class TimerTest {private Integer count = 0;public TimerTest() {testTimer();}public void testTimer() {new Timer().schedule(new TimerTask() {@Overridepublic void run() {try {//do SomethingSystem.out.println(new Date().toString() + ": " + count);count++;} catch (Exception e) {e.printStackTrace();}}}, 0, 1000);}
}


線程實現定時器

使用thread + runnable

public class ThreadTest {private Integer count = 0;public ThreadTest() {test1();}public void test1() {new Thread(() -> {while (count < 10) {System.out.println(new Date().toString() + ": " + count);count++;try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}}).start();}
}

使用線程池 + runnable

public class ThreadTest {private static final ExecutorService threadPool = Executors.newFixedThreadPool(5);// 線程池private Integer count = 0;public ThreadTest() {test2();}public void test2() {threadPool.execute(() -> {while (count < 10) {System.out.println(new Date().toString() + ": " + count);count++;try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}});}
}

使用org.springframework.scheduling.TaskScheduler+ runnable

設置觸發頻率為3000毫秒

@Component
public class ThreadTest {private Integer count = 0;private final TaskScheduler taskScheduler;public ThreadTest(TaskScheduler taskScheduler) {this.taskScheduler = taskScheduler;test3();}public void test3() {taskScheduler.scheduleAtFixedRate(() -> {System.out.println(new Date().toString() + ": " + count);count++;}, 3000);}
}

設置觸發時間為每天凌晨1點

@Component
public class ThreadTest {private Integer count = 0;private final TaskScheduler taskScheduler;public ThreadTest(TaskScheduler taskScheduler) {this.taskScheduler = taskScheduler;test4();}public void test4() {taskScheduler.schedule(() -> {System.out.println(new Date().toString() + ": " + count);count++;}, new CronTrigger("0 0 1 * * ?"));}
}

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

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

相關文章

golang學習筆記——編寫最簡單的命令行工具

編寫最簡單的命令行工具 用戶輸入bufio 使用go語言編寫最簡單的命令行工具 mkdir hello-cli-demo cd hello-cli-demo # 查看環境變量 go envgo mod初始化 go mod init gitcode.com/m打開vscode&#xff0c;創建main.go package mainimport ("fmt""bufio&qu…

RK3568 CIF和ISP的關聯

1. 引言 在本文檔中&#xff0c;我們將介紹RK3568芯片的CIF&#xff08;Camera Interface&#xff09;和ISP&#xff08;Image Signal Processor&#xff09;模塊。這兩個模塊是RK3568芯片的關鍵組成部分&#xff0c;用于圖像采集和處理。 CIF是一個標準接口&#xff0c;用于…

快速測試 3節點的redis sentinel集群宕機2個節點以后是否仍能正常使用

有同事問我&#xff0c;三個redis sentinel節點&#xff0c;宕機兩個節點以后&#xff0c;是否還能夠正常的通過redis sentinel正常訪問redis的數據。我想了想&#xff0c;理論上是可以的&#xff0c;但是我沒試過&#xff0c;今天有時間就測試了一下。搭建環境和測試代碼的過程…

Java并發(十七)----變量的線程安全分析

1、成員變量和靜態變量是否線程安全 如果它們沒有共享&#xff0c;則線程安全 如果它們被共享了&#xff0c;根據它們的狀態是否能夠改變&#xff0c;又分兩種情況 如果只有讀操作&#xff0c;則線程安全 如果有讀寫操作&#xff0c;則這段代碼是臨界區&#xff0c;需要考慮線…

深入了解Python pydash庫

更多資料獲取 &#x1f4da; 個人網站&#xff1a;ipengtao.com 在數據處理和分析領域&#xff0c;Python一直是一種強大的編程語言。然而&#xff0c;在處理大規模數據集和執行復雜操作時&#xff0c;有時候需要更高效的工具。在本文中&#xff0c;我們將深入探討pydash庫&am…

語義分割 簡介及數據集簡介

參考文章 MS COCO數據集介紹以及pycocotools簡單使用-CSDN博客

[MySQL--進階篇]存儲引擎的體系結構、簡介、特點、選擇

前言 ?Hello!這里是歐_aita的博客。 ?今日語錄&#xff1a;不要在乎別人怎么看你&#xff0c;因為他們根本就沒有時間&#xff0c;他們只關心他們自己。 ?個人主頁&#xff1a;歐_aita ψ(._. )>?個人專欄&#xff1a; 數據結構與算法 MySQL數據庫 存儲引擎 前言MySQL體…

代碼隨想錄算法訓練營第四十一天|343. 整數拆分、96.不同的二叉搜索樹

代碼隨想錄算法訓練營第四十一天|343. 整數拆分、96.不同的二叉搜索樹 整數拆分 343. 整數拆分 文章講解&#xff1a;https://programmercarl.com/0343.%E6%95%B4%E6%95%B0%E6%8B%86%E5%88%86.html 題目鏈接&#xff1a;https://leetcode.cn/problems/integer-break/ 視頻講解…

李宏毅gpt個人記錄

參考&#xff1a; 李宏毅機器學習--self-supervised&#xff1a;BERT、GPT、Auto-encoder-CSDN博客 用無標注資料的任務訓練完模型以后&#xff0c;它本身沒有什么用&#xff0c;GPT 1只能夠把一句話補完&#xff0c;可以把 Self-Supervised Learning 的 Model做微微的調整&am…

32.768KHz時鐘RTC晶振精度PPM值及頻差計算

一個數字電路就像一所城市的交通&#xff0c;晶振的作用就是十字路口的信號燈&#xff0c;因此晶振的品質及其電路應用尤其關鍵。數字電路又像生命體&#xff0c;它的運行就像人身體里的血液流通&#xff0c;它不是由單一的某個器件或器件單元構成&#xff0c;而是由多個器件及…

【Spring Boot 源碼學習】ApplicationListener 詳解

Spring Boot 源碼學習系列 ApplicationListener 詳解 引言往期內容主要內容1. 初識 ApplicationListener2. 加載 ApplicationListener3. 響應應用程序事件 總結 引言 書接前文《初識 SpringApplication》&#xff0c;我們從 Spring Boot 的啟動類 SpringApplication 上入手&am…

如何查詢川菜食材配料的API接口

在當今的美食文化中&#xff0c;菜譜不只是一張簡單的食譜&#xff0c;更是了解美食文化和飲食知識的重要途徑。然而&#xff0c;若沒有準確的食材配料&#xff0c;烹制出的每道菜品都將難以達到完美的味道。因此&#xff0c;為了更好地滿足人們對于菜譜和食譜的需求&#xff0…

C語言習題集(026)

//寫一個函數&#xff0c;輸入一個4位數字&#xff0c;要求輸出這4個 //數字字符&#xff0c;但每兩個數字間空一個空格。如輸入 //1990&#xff0c;應輸出"1 9 9 0"。 /* */ //解答&#xff1a; #include<stdio.h> void change(int a) { if(a/10!0) { chang…

linux權限管理以及shell

1.shell 1.1什么是shell? shell即外殼&#xff0c;是運行在linux系統上的一個腳本語言&#xff0c;包裹在linux內核的外面。我們常說的linux操作系統實際上是linux內核。我們使用的所有指令都是一個個程序&#xff0c;而shell指令就是一個將我們用戶的操作翻譯給linux內核的程…

軟件設計之組合模式

組合模式&#xff1a;將對象組合成樹形結構。 案例&#xff1a;公司管理。一個公司可以分總公司和分公司&#xff0c;無論是總公司還是分公司都有自己的部門&#xff0c;如人力資源管理部門、財務部門。分公司可以建立自己在不同地域的辦事處。請使用組合模式打印出某個公司的…

SpringSecurity6 | 登陸后的跳轉

SpringSecurity6 | 自定義認證規則 ?作者簡介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;熱愛Java后端開發者&#xff0c;一個想要與大家共同進步的男人&#x1f609;&#x1f609; &#x1f34e;個人主頁&#xff1a;Leo的博客 &#x1f49e;當前專欄&#xff1a; Ja…

第九天:信息打點-CDN繞過篇amp;漏洞回鏈amp;接口探針amp;全網掃描amp;反向郵件

信息打點-CDN繞過篇 cdn繞過文章&#xff1a;https://www.cnblogs.com/qiudabai/p/9763739.html 一、CDN-知識點 1、常見訪問過程 1、沒有CDN情況下傳統訪問&#xff1a;用戶訪問域名-解析服務器IP–>訪問目標主機 2.普通CDN&#xff1a;用戶訪問域名–>CDN節點–>…

面向LLM的App架構——業務維度

這是兩篇面向LLM的大前端架構的第一篇&#xff0c;主要寫我對LLM業務的認知以及由此推演出的大前端架構。由于我是客戶端出身&#xff0c;所以主要以客戶端角度來描述&#xff0c;并不影響對前端的適用性。 對LLM的認知 基于Google對AGI的論文&#xff0c;AGI或者LLM一定會朝…

淺談ClickHouse性能監控與調優

ClickHouse性能監控與調優 ClickHouse是一個高性能的列式數據庫管理系統&#xff0c;適用于實時分析和大數據處理。本文將詳細講解如何監控ClickHouse的性能指標、日志和查詢統計信息&#xff0c;以及如何進行故障排查和性能調優。 一、監控性能指標 1. 系統表 ClickHouse提…

網絡層重點協議——IP協議詳解

??????今天給大家分享的是網絡層的重點協議——IP協議。 清風的CSDN博客 &#x1f6e9;?&#x1f6e9;?&#x1f6e9;?希望我的文章能對你有所幫助&#xff0c;有不足的地方還請各位看官多多指教&#xff0c;大家一起學習交流&#xff01; ??????動動你們發財的…