@Scheduled定時(shí)器使用注意事項(xiàng)及說明
@Scheduled定時(shí)器使用注意事項(xiàng)
首先定時(shí)器可以固定執(zhí)行時(shí)間使用cron表達(dá)式,也可以控制方法執(zhí)行的間隔時(shí)間fixedDelay,這里使用的是cron,創(chuàng)建了schedule包,將定時(shí)任務(wù)放在了schedule包下,下面開始進(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中,通過 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è)類,類目以功能+Schedule結(jié)尾,該類加上@Component表示交給spring管理,定時(shí)器的表達(dá)式可以在nacos中聲明,防止后期需要改定時(shí)任務(wù)的時(shí)候頻繁修改代碼,只需要修改nacos配置文件即可(nacos配置文件中聲明定時(shí)器名字的時(shí)候不得以特殊字符開頭,不然會(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ú)配置線程池
@Async("PoolThread")
public void testScheduled() {
//定時(shí)器執(zhí)行的邏輯代碼
}
}以上代碼使用到了 @Async異步注解,這里我使用了一個(gè)線程池,可以根據(jù)自己的業(yè)務(wù)需求自行決定。
定義線程池的意義在與所有執(zhí)行定時(shí)器都是副線程執(zhí)行,不影響主線程。
使用線程池的情況
- 異步執(zhí)行任務(wù): 任務(wù)在后臺(tái)異步執(zhí)行,不阻塞當(dāng)前線程或應(yīng)用程序的其他部分時(shí),使用線程池。例如,處理異步消息、發(fā)送電子郵件、執(zhí)行后臺(tái)計(jì)算等。
- 提高并發(fā)性: 程序需要同時(shí)處理多個(gè)并發(fā)任務(wù)時(shí),線程池可以有效地管理和分配系統(tǒng)資源,提高系統(tǒng)的并發(fā)性和性能。這對(duì)于處理大量獨(dú)立的任務(wù)或請求非常有用。
- 避免線程創(chuàng)建和銷毀的開銷: 線程的創(chuàng)建和銷毀通常會(huì)帶來較大的開銷。通過使用線程池,可以避免頻繁地創(chuàng)建和銷毀線程,而是重用現(xiàn)有線程,減少資源開銷。
- 限制資源使用: 使用線程池可以限制同時(shí)執(zhí)行的任務(wù)數(shù)量,以控制系統(tǒng)資源的使用。這對(duì)于避免系統(tǒng)過度負(fù)載或資源耗盡非常重要。
- 任務(wù)隊(duì)列管理: 線程池通常具有任務(wù)隊(duì)列,可以將需要執(zhí)行的任務(wù)添加到隊(duì)列中。這使得任務(wù)按照預(yù)定的順序執(zhí)行,而無需手動(dòng)管理線程的執(zhí)行。
總結(jié):
如果定時(shí)器執(zhí)行的比較頻繁,不想每次都new線程,而且內(nèi)次都從線程池里取線程,用完線程池自己根據(jù)GC回收機(jī)制銷毀,可以考慮編寫線程池
線程池代碼
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è)線程報(bào)錯(cuò)了
@Bean("PoolThread")
public ThreadPoolTaskExecutor threadPoolWorkTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//線程池創(chuàng)建的核心線程數(shù),線程池維護(hù)線程的最少數(shù)量,即使沒有任務(wù)需要執(zhí)行,也會(huì)一直存活
executor.setCorePoolSize(8);
//如果設(shè)置allowCoreThreadTimeout=true(默認(rèn)false)時(shí),核心線程會(huì)超時(shí)關(guān)閉
//executor.setAllowCoreThreadTimeOut(true);
//阻塞隊(duì)列 當(dāng)核心線程數(shù)達(dá)到最大時(shí),新任務(wù)會(huì)放在隊(duì)列中排隊(duì)等待執(zhí)行
executor.setQueueCapacity(124);
//最大線程池?cái)?shù)量,當(dāng)線程數(shù)>=corePoolSize,且任務(wù)隊(duì)列已滿時(shí)。線程池會(huì)創(chuàng)建新線程來處理任
//任務(wù)隊(duì)列已滿時(shí), 且當(dāng)線程數(shù)=maxPoolSize,,線程池會(huì)拒絕處理任務(wù)而拋出異常
executor.setMaxPoolSize(64);
//當(dāng)線程空閑時(shí)間達(dá)到keepAliveTime時(shí),線程會(huì)退出,直到線程數(shù)量=corePoolSize
//允許線程空閑時(shí)間30秒,當(dāng)maxPoolSize的線程在空閑時(shí)間到達(dá)的時(shí)候銷毀
//如果allowCoreThreadTimeout=true,則會(huì)直到線程數(shù)量=0
executor.setKeepAliveSeconds(30);
//spring 提供的 ThreadPoolTaskExecutor 線程池,是有setThreadNamePrefix() 方法的。
//jdk 提供的ThreadPoolExecutor 線程池是沒有 setThreadNamePrefix() 方法的
executor.setThreadNamePrefix("PoolThread");
// rejection-policy:拒絕策略:當(dāng)線程數(shù)已經(jīng)達(dá)到maxSize的時(shí)候,如何處理新任務(wù)
// CallerRunsPolicy():交由調(diào)用方線程運(yùn)行,比如 main 線程;如果添加到線程池失敗,那么主線程會(huì)自己去執(zhí)行該任務(wù),不會(huì)等待線程池中的線程去執(zhí)行
// AbortPolicy():該策略是線程池的默認(rèn)策略,如果線程池隊(duì)列滿了丟掉這個(gè)任務(wù)并且拋出RejectedExecutionException異常。
// DiscardPolicy():如果線程池隊(duì)列滿了,會(huì)直接丟掉這個(gè)任務(wù)并且不會(huì)有任何異常
// DiscardOldestPolicy():丟棄隊(duì)列中最老的任務(wù),隊(duì)列滿了,會(huì)將最早進(jìn)入隊(duì)列的任務(wù)刪掉騰出空間,再嘗試加入隊(duì)列
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
executor.initialize();
return executor;
}
}總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java?Date和SimpleDateFormat時(shí)間類詳解
這篇文章主要介紹了java?Date和SimpleDateFormat時(shí)間類詳解,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08
IDEA 中使用 ECJ 編譯出現(xiàn) java.lang.IllegalArgumentException的錯(cuò)誤問題
這篇文章主要介紹了IDEA 中使用 ECJ 編譯出現(xiàn) java.lang.IllegalArgumentException問題 ,本文內(nèi)容簡短給大家介紹的好,需要的朋友可以參考下2020-05-05
Nacos客戶端配置中心緩存動(dòng)態(tài)更新實(shí)現(xiàn)源碼
這篇文章主要為大家介紹了Nacos客戶端配置中心緩存動(dòng)態(tài)更新實(shí)現(xiàn)源碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-03-03
Java?SE循環(huán)一些基本練習(xí)題總結(jié)
循環(huán)語句可以在滿足循環(huán)條件的情況下,反復(fù)執(zhí)行某一段代碼,這段被重復(fù)執(zhí)行的代碼被稱為循環(huán)體語句,下面這篇文章主要給大家總結(jié)介紹了關(guān)于Java?SE循環(huán)一些基本練習(xí)題,需要的朋友可以參考下2024-03-03
JAVA使用爬蟲抓取網(wǎng)站網(wǎng)頁內(nèi)容的方法
這篇文章主要介紹了JAVA使用爬蟲抓取網(wǎng)站網(wǎng)頁內(nèi)容的方法,實(shí)例分析了java爬蟲的兩種實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
Spring Boot 3.x 集成 Eureka Server/Cl
隨著SpringBoot 3.x版本的開發(fā)嘗試,本文記錄了在集成Eureka Server/Client時(shí)所遇到的問題和解決方案,文中詳細(xì)介紹了搭建服務(wù)、配置文件和測試步驟,感興趣的朋友跟隨小編一起看看吧2024-09-09
java byte數(shù)組與16進(jìn)制間相互轉(zhuǎn)換的示例
這篇文章主要介紹了java byte數(shù)組與16進(jìn)制間相互轉(zhuǎn)換的示例,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-10-10

