@Scheduled定時(shí)器使用注意事項(xiàng)及說(shuō)明
@Scheduled定時(shí)器使用注意事項(xiàng)
首先定時(shí)器可以固定執(zhí)行時(shí)間使用cron表達(dá)式,也可以控制方法執(zhí)行的間隔時(shí)間fixedDelay,這里使用的是cron,創(chuàng)建了schedule包,將定時(shí)任務(wù)放在了schedule包下,下面開(kāi)始進(jìn)入正題。
一般在程序中直接使用定時(shí)器,但是最好設(shè)置一下JVM的默認(rèn)時(shí)區(qū),因?yàn)镴VM默認(rèn)時(shí)區(qū)可能和本機(jī)時(shí)區(qū)不一樣,不同操作系統(tǒng)默認(rèn)時(shí)區(qū)可能不一樣。
使用靜態(tài)變量static聲明的靜態(tài)變量具有全局作用域,對(duì)全局造成影響,保證系統(tǒng)整個(gè)時(shí)區(qū)一致
如果只想針對(duì)某部分設(shè)置時(shí)區(qū)需要顯示指定時(shí)區(qū),不影響全局結(jié)果
package com.test.hello.task; import com.test.hello.service.TestEntityService; import com.test.hello.service.TestOneEntityService; import com.test.hello.service.TestTwoEntityService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.TimeZone; @Slf4j @Component public class TestTask { //全局修改時(shí)區(qū),設(shè)置JVM的默認(rèn)時(shí)區(qū),影響整個(gè) Java 虛擬機(jī)中所有涉及日期和時(shí)間的操作所使用的時(shí)區(qū)。 //在Java中,通過(guò) TimeZone.setDefault() 方法可以實(shí)現(xiàn)對(duì) JVM 默認(rèn)時(shí)區(qū)的修改。 static { TimeZone.setDefault(TimeZone.getTimeZone("GMT+8")); } //顯示指定時(shí)區(qū),不影響全局 // 顯式指定使用的時(shí)區(qū)為 GMT+8 ZoneId zoneId = ZoneId.of("GMT+8"); // 獲取當(dāng)前時(shí)間 LocalDateTime currentTime = LocalDateTime.now(zoneId); // 格式化日期時(shí)間 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String formattedTime = currentTime.format(formatter); @Autowired private TestEntityService testEntityService; @Autowired private TestOneEntityService testOneEntityService; @Autowired private TestTwoEntityService testTwoEntityService; /** * 每天的00:00:00執(zhí)行任務(wù) */ @Scheduled(cron = "0 0 0 * * *") public void scheduled() { log.info("=====>>>>>使用cron {}", testEntityService.countSum()); } @Scheduled(cron = "0 0/1 * * * ?") public void scheduledStatisticsOnAboardNum() { log.info("=====>>>>>統(tǒng)計(jì): 使用cron {}", testEntityService.countSum()); log.info("=====>>>>>)統(tǒng)計(jì): 使用cron {}", testOneEntityService.countSum()); log.info("=====>>>>>合計(jì)統(tǒng)計(jì): 使用cron {}", testTwoEntityService.countSum()); } }
為了美觀性,每個(gè)定時(shí)都單獨(dú)放在一個(gè)類(lèi),類(lèi)目以功能+Schedule結(jié)尾,該類(lèi)加上@Component表示交給spring管理,定時(shí)器的表達(dá)式可以在nacos中聲明,防止后期需要改定時(shí)任務(wù)的時(shí)候頻繁修改代碼,只需要修改nacos配置文件即可(nacos配置文件中聲明定時(shí)器名字的時(shí)候不得以特殊字符開(kāi)頭,不然會(huì)報(bào)錯(cuò)踩坑)
package com.test.stats.schedule; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component @Slf4j public class TestSchedule { //定時(shí)器表達(dá)式配置在nacos中 @Scheduled(cron = "${testSchedule.oneCron}") //單獨(dú)配置線(xiàn)程池 @Async("PoolThread") public void testScheduled() { //定時(shí)器執(zhí)行的邏輯代碼 } }
以上代碼使用到了 @Async異步注解,這里我使用了一個(gè)線(xiàn)程池,可以根據(jù)自己的業(yè)務(wù)需求自行決定。
定義線(xiàn)程池的意義在與所有執(zhí)行定時(shí)器都是副線(xiàn)程執(zhí)行,不影響主線(xiàn)程。
使用線(xiàn)程池的情況
- 異步執(zhí)行任務(wù): 任務(wù)在后臺(tái)異步執(zhí)行,不阻塞當(dāng)前線(xiàn)程或應(yīng)用程序的其他部分時(shí),使用線(xiàn)程池。例如,處理異步消息、發(fā)送電子郵件、執(zhí)行后臺(tái)計(jì)算等。
- 提高并發(fā)性: 程序需要同時(shí)處理多個(gè)并發(fā)任務(wù)時(shí),線(xiàn)程池可以有效地管理和分配系統(tǒng)資源,提高系統(tǒng)的并發(fā)性和性能。這對(duì)于處理大量獨(dú)立的任務(wù)或請(qǐng)求非常有用。
- 避免線(xiàn)程創(chuàng)建和銷(xiāo)毀的開(kāi)銷(xiāo): 線(xiàn)程的創(chuàng)建和銷(xiāo)毀通常會(huì)帶來(lái)較大的開(kāi)銷(xiāo)。通過(guò)使用線(xiàn)程池,可以避免頻繁地創(chuàng)建和銷(xiāo)毀線(xiàn)程,而是重用現(xiàn)有線(xiàn)程,減少資源開(kāi)銷(xiāo)。
- 限制資源使用: 使用線(xiàn)程池可以限制同時(shí)執(zhí)行的任務(wù)數(shù)量,以控制系統(tǒng)資源的使用。這對(duì)于避免系統(tǒng)過(guò)度負(fù)載或資源耗盡非常重要。
- 任務(wù)隊(duì)列管理: 線(xiàn)程池通常具有任務(wù)隊(duì)列,可以將需要執(zhí)行的任務(wù)添加到隊(duì)列中。這使得任務(wù)按照預(yù)定的順序執(zhí)行,而無(wú)需手動(dòng)管理線(xiàn)程的執(zhí)行。
總結(jié):
如果定時(shí)器執(zhí)行的比較頻繁,不想每次都new線(xiàn)程,而且內(nèi)次都從線(xiàn)程池里取線(xiàn)程,用完線(xiàn)程池自己根據(jù)GC回收機(jī)制銷(xiāo)毀,可以考慮編寫(xiě)線(xiàn)程池
線(xiàn)程池代碼
package com.test.hello.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.ThreadPoolExecutor; @Configuration public class ThreadPoolTaskConfig { //這里的命名可以在報(bào)錯(cuò)的時(shí)候清楚是哪個(gè)線(xiàn)程報(bào)錯(cuò)了 @Bean("PoolThread") public ThreadPoolTaskExecutor threadPoolWorkTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //線(xiàn)程池創(chuàng)建的核心線(xiàn)程數(shù),線(xiàn)程池維護(hù)線(xiàn)程的最少數(shù)量,即使沒(méi)有任務(wù)需要執(zhí)行,也會(huì)一直存活 executor.setCorePoolSize(8); //如果設(shè)置allowCoreThreadTimeout=true(默認(rèn)false)時(shí),核心線(xiàn)程會(huì)超時(shí)關(guān)閉 //executor.setAllowCoreThreadTimeOut(true); //阻塞隊(duì)列 當(dāng)核心線(xiàn)程數(shù)達(dá)到最大時(shí),新任務(wù)會(huì)放在隊(duì)列中排隊(duì)等待執(zhí)行 executor.setQueueCapacity(124); //最大線(xiàn)程池?cái)?shù)量,當(dāng)線(xiàn)程數(shù)>=corePoolSize,且任務(wù)隊(duì)列已滿(mǎn)時(shí)。線(xiàn)程池會(huì)創(chuàng)建新線(xiàn)程來(lái)處理任 //任務(wù)隊(duì)列已滿(mǎn)時(shí), 且當(dāng)線(xiàn)程數(shù)=maxPoolSize,,線(xiàn)程池會(huì)拒絕處理任務(wù)而拋出異常 executor.setMaxPoolSize(64); //當(dāng)線(xiàn)程空閑時(shí)間達(dá)到keepAliveTime時(shí),線(xiàn)程會(huì)退出,直到線(xiàn)程數(shù)量=corePoolSize //允許線(xiàn)程空閑時(shí)間30秒,當(dāng)maxPoolSize的線(xiàn)程在空閑時(shí)間到達(dá)的時(shí)候銷(xiāo)毀 //如果allowCoreThreadTimeout=true,則會(huì)直到線(xiàn)程數(shù)量=0 executor.setKeepAliveSeconds(30); //spring 提供的 ThreadPoolTaskExecutor 線(xiàn)程池,是有setThreadNamePrefix() 方法的。 //jdk 提供的ThreadPoolExecutor 線(xiàn)程池是沒(méi)有 setThreadNamePrefix() 方法的 executor.setThreadNamePrefix("PoolThread"); // rejection-policy:拒絕策略:當(dāng)線(xiàn)程數(shù)已經(jīng)達(dá)到maxSize的時(shí)候,如何處理新任務(wù) // CallerRunsPolicy():交由調(diào)用方線(xiàn)程運(yùn)行,比如 main 線(xiàn)程;如果添加到線(xiàn)程池失敗,那么主線(xiàn)程會(huì)自己去執(zhí)行該任務(wù),不會(huì)等待線(xiàn)程池中的線(xiàn)程去執(zhí)行 // AbortPolicy():該策略是線(xiàn)程池的默認(rèn)策略,如果線(xiàn)程池隊(duì)列滿(mǎn)了丟掉這個(gè)任務(wù)并且拋出RejectedExecutionException異常。 // DiscardPolicy():如果線(xiàn)程池隊(duì)列滿(mǎn)了,會(huì)直接丟掉這個(gè)任務(wù)并且不會(huì)有任何異常 // DiscardOldestPolicy():丟棄隊(duì)列中最老的任務(wù),隊(duì)列滿(mǎn)了,會(huì)將最早進(jìn)入隊(duì)列的任務(wù)刪掉騰出空間,再?lài)L試加入隊(duì)列 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); executor.initialize(); return executor; } }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring+quartz實(shí)現(xiàn)定時(shí)發(fā)送郵件功能實(shí)例
spring提供的定時(shí)發(fā)送郵件功能一直深受廣大web開(kāi)發(fā)者的喜愛(ài),這篇文章主要介紹了Spring+quartz實(shí)現(xiàn)定時(shí)發(fā)送郵件功能實(shí)例,有興趣的可以了解一下。2017-03-03java Hibernate多對(duì)多映射詳解及實(shí)例代碼
這篇文章主要介紹了java Hibernate多對(duì)多映射詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-01-01JAVA中String類(lèi)與StringBuffer類(lèi)的區(qū)別
這篇文章主要為大家詳細(xì)介紹了JAVA中String類(lèi)與StringBuffer類(lèi)的區(qū)別,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12基于Java編寫(xiě)一個(gè)簡(jiǎn)單的風(fēng)控組件
這篇文章主要為大家詳細(xì)介紹了如何基于Java編寫(xiě)一個(gè)簡(jiǎn)單的風(fēng)控組件,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定的幫助,需要的可以參考一下2022-12-12java實(shí)現(xiàn)XML與JSON轉(zhuǎn)換的便捷實(shí)用方法
這篇文章主要為大家介紹了java實(shí)現(xiàn)XML與JSON轉(zhuǎn)換的便捷實(shí)用方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12單一職責(zé)原則_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了單一職責(zé)原則的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08Java基礎(chǔ)之反射技術(shù)相關(guān)知識(shí)總結(jié)
今天帶大家復(fù)習(xí)Java基礎(chǔ)知識(shí),文中對(duì)Java反射技術(shù)介紹的非常詳細(xì),對(duì)正在學(xué)習(xí)Java的小伙伴們很有幫助,,需要的朋友可以參考下2021-05-05使用Idea簡(jiǎn)單快速搭建springcloud項(xiàng)目的圖文教程
這篇文章主要介紹了使用Idea簡(jiǎn)單快速搭建springcloud項(xiàng)目,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01Springboot自動(dòng)配置與@Configuration配置類(lèi)詳解
這篇文章主要介紹了SpringBoot中的@Configuration與自動(dòng)配置,在進(jìn)行項(xiàng)目編寫(xiě)前,我們還需要知道一個(gè)東西,就是SpringBoot對(duì)我們的SpringMVC還做了哪些配置,包括如何擴(kuò)展,如何定制,只有把這些都搞清楚了,我們?cè)谥笫褂貌艜?huì)更加得心應(yīng)手2022-07-07