@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)變量具有全局作用域,對全局造成影響,保證系統(tǒng)整個(gè)時(shí)區(qū)一致
如果只想針對某部分設(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)對 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í)候不得以特殊字符開頭,不然會報(bào)錯踩坑)

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ù)在后臺異步執(zhí)行,不阻塞當(dāng)前線程或應(yīng)用程序的其他部分時(shí),使用線程池。例如,處理異步消息、發(fā)送電子郵件、執(zhí)行后臺計(jì)算等。
- 提高并發(fā)性: 程序需要同時(shí)處理多個(gè)并發(fā)任務(wù)時(shí),線程池可以有效地管理和分配系統(tǒng)資源,提高系統(tǒng)的并發(fā)性和性能。這對于處理大量獨(dú)立的任務(wù)或請求非常有用。
- 避免線程創(chuàng)建和銷毀的開銷: 線程的創(chuàng)建和銷毀通常會帶來較大的開銷。通過使用線程池,可以避免頻繁地創(chuàng)建和銷毀線程,而是重用現(xiàn)有線程,減少資源開銷。
- 限制資源使用: 使用線程池可以限制同時(shí)執(zhí)行的任務(wù)數(shù)量,以控制系統(tǒng)資源的使用。這對于避免系統(tǒng)過度負(fù)載或資源耗盡非常重要。
- 任務(wù)隊(duì)列管理: 線程池通常具有任務(wù)隊(duì)列,可以將需要執(zhí)行的任務(wù)添加到隊(duì)列中。這使得任務(wù)按照預(yù)定的順序執(zhí)行,而無需手動管理線程的執(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)錯的時(shí)候清楚是哪個(gè)線程報(bào)錯了
@Bean("PoolThread")
public ThreadPoolTaskExecutor threadPoolWorkTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//線程池創(chuàng)建的核心線程數(shù),線程池維護(hù)線程的最少數(shù)量,即使沒有任務(wù)需要執(zhí)行,也會一直存活
executor.setCorePoolSize(8);
//如果設(shè)置allowCoreThreadTimeout=true(默認(rèn)false)時(shí),核心線程會超時(shí)關(guān)閉
//executor.setAllowCoreThreadTimeOut(true);
//阻塞隊(duì)列 當(dāng)核心線程數(shù)達(dá)到最大時(shí),新任務(wù)會放在隊(duì)列中排隊(duì)等待執(zhí)行
executor.setQueueCapacity(124);
//最大線程池?cái)?shù)量,當(dāng)線程數(shù)>=corePoolSize,且任務(wù)隊(duì)列已滿時(shí)。線程池會創(chuàng)建新線程來處理任
//任務(wù)隊(duì)列已滿時(shí), 且當(dāng)線程數(shù)=maxPoolSize,,線程池會拒絕處理任務(wù)而拋出異常
executor.setMaxPoolSize(64);
//當(dāng)線程空閑時(shí)間達(dá)到keepAliveTime時(shí),線程會退出,直到線程數(shù)量=corePoolSize
//允許線程空閑時(shí)間30秒,當(dāng)maxPoolSize的線程在空閑時(shí)間到達(dá)的時(shí)候銷毀
//如果allowCoreThreadTimeout=true,則會直到線程數(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 線程;如果添加到線程池失敗,那么主線程會自己去執(zhí)行該任務(wù),不會等待線程池中的線程去執(zhí)行
// AbortPolicy():該策略是線程池的默認(rèn)策略,如果線程池隊(duì)列滿了丟掉這個(gè)任務(wù)并且拋出RejectedExecutionException異常。
// DiscardPolicy():如果線程池隊(duì)列滿了,會直接丟掉這個(gè)任務(wù)并且不會有任何異常
// DiscardOldestPolicy():丟棄隊(duì)列中最老的任務(wù),隊(duì)列滿了,會將最早進(jìn)入隊(duì)列的任務(wù)刪掉騰出空間,再嘗試加入隊(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開發(fā)者的喜愛,這篇文章主要介紹了Spring+quartz實(shí)現(xiàn)定時(shí)發(fā)送郵件功能實(shí)例,有興趣的可以了解一下。2017-03-03
java Hibernate多對多映射詳解及實(shí)例代碼
這篇文章主要介紹了java Hibernate多對多映射詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-01-01
JAVA中String類與StringBuffer類的區(qū)別
這篇文章主要為大家詳細(xì)介紹了JAVA中String類與StringBuffer類的區(qū)別,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
java實(shí)現(xiàn)XML與JSON轉(zhuǎn)換的便捷實(shí)用方法
這篇文章主要為大家介紹了java實(shí)現(xiàn)XML與JSON轉(zhuǎn)換的便捷實(shí)用方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
單一職責(zé)原則_動力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了單一職責(zé)原則的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Java基礎(chǔ)之反射技術(shù)相關(guān)知識總結(jié)
今天帶大家復(fù)習(xí)Java基礎(chǔ)知識,文中對Java反射技術(shù)介紹的非常詳細(xì),對正在學(xué)習(xí)Java的小伙伴們很有幫助,,需要的朋友可以參考下2021-05-05
使用Idea簡單快速搭建springcloud項(xiàng)目的圖文教程
這篇文章主要介紹了使用Idea簡單快速搭建springcloud項(xiàng)目,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
Springboot自動配置與@Configuration配置類詳解
這篇文章主要介紹了SpringBoot中的@Configuration與自動配置,在進(jìn)行項(xiàng)目編寫前,我們還需要知道一個(gè)東西,就是SpringBoot對我們的SpringMVC還做了哪些配置,包括如何擴(kuò)展,如何定制,只有把這些都搞清楚了,我們在之后使用才會更加得心應(yīng)手2022-07-07

