SpringBoot定時(shí)任務(wù)動(dòng)態(tài)擴(kuò)展ScheduledTaskRegistrar詳解
摘要
本文主要介紹基于SpringBoot定時(shí)任務(wù)ScheduledTaskRegistrar的動(dòng)態(tài)擴(kuò)展,實(shí)現(xiàn)定時(shí)任務(wù)的動(dòng)態(tài)新增和刪除。
ScheduledTaskRegistrar類簡(jiǎn)要描述
平常使用方式配置
Application啟動(dòng)類上添加注解@EnableScheduling
@EnableScheduling
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 在需要定時(shí)的方法上添加定時(shí)注解
@Scheduled(cron = "0/10 * * * * ?")
@Slf4j
@Component
public class OtherScheduler {
@Scheduled(cron = "0/10 * * * * ?")
public void print(){
log.info("每10S打印一次");
}
@Scheduled(cron = "0/5 * * * * ?")
public void print5(){
log.info("每5S打印一次");
}
}
原理分析
默認(rèn)的方式啟動(dòng)把ScheduledAnnotationBeanPostProcessor該類實(shí)例化到SpringBoot的Bean管理中,并且該類持有一個(gè)ScheduledTaskRegistrar屬性,然后掃描出來(lái)?yè)碛?code>@Scheduled注解的方法,添加到定時(shí)任務(wù)中。
- 添加定時(shí)任務(wù)到列表中
掃描到@Scheduled注解的時(shí)候調(diào)用了該方法添加任務(wù)
public void addCronTask(Runnable task, String expression) {
if (!CRON_DISABLED.equals(expression)) {
addCronTask(new CronTask(task, expression));
}
}
- 啟動(dòng)定時(shí)任務(wù)
在對(duì)象實(shí)例化完成后,調(diào)用了afterPropertiesSet方法,該方法實(shí)際使用中執(zhí)行了
public void afterPropertiesSet() {
scheduleTasks();
}
protected void scheduleTasks() {
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
if (this.triggerTasks != null) {
for (TriggerTask task : this.triggerTasks) {
addScheduledTask(scheduleTriggerTask(task));
}
}
if (this.cronTasks != null) {
for (CronTask task : this.cronTasks) {
addScheduledTask(scheduleCronTask(task));
}
}
if (this.fixedRateTasks != null) {
for (IntervalTask task : this.fixedRateTasks) {
addScheduledTask(scheduleFixedRateTask(task));
}
}
if (this.fixedDelayTasks != null) {
for (IntervalTask task : this.fixedDelayTasks) {
addScheduledTask(scheduleFixedDelayTask(task));
}
}
}
private void addScheduledTask(@Nullable ScheduledTask task) {
if (task != null) {
this.scheduledTasks.add(task);
}
}
// 啟動(dòng)任務(wù)核心方法
public ScheduledTask scheduleCronTask(CronTask task) {
ScheduledTask scheduledTask = this.unresolvedTasks.remove(task);
boolean newTask = false;
if (scheduledTask == null) {
scheduledTask = new ScheduledTask(task);
newTask = true;
}
if (this.taskScheduler != null) {
scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());
}
else {
addCronTask(task);
this.unresolvedTasks.put(task, scheduledTask);
}
return (newTask ? scheduledTask : null);
}
DynamicScheduledTaskRegistrar 動(dòng)態(tài)任務(wù)注冊(cè)類
下面改動(dòng)主要涉及到線程池?cái)?shù)量、新增任務(wù)、刪除任務(wù)、銷毀任務(wù)四個(gè)方面;
public class DynamicScheduledTaskRegistrar extends ScheduledTaskRegistrar {
private static final Logger log = LoggerFactory.getLogger(DynamicScheduledTaskRegistrar.class);
private final Map<String,ScheduledTask> scheduledTaskMap = new LinkedHashMap<>(16);
public DynamicScheduledTaskRegistrar(){
super();
// 兩種實(shí)現(xiàn)方案
//ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
//TaskScheduler taskScheduler = new ConcurrentTaskScheduler(scheduledExecutorService);
// 第二種實(shí)現(xiàn)方案
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(8);
taskScheduler.setRemoveOnCancelPolicy(true);
taskScheduler.setThreadNamePrefix("dynamic-scheduled-task-");
taskScheduler.initialize();
this.setScheduler(taskScheduler);
}
/**
* 新增任務(wù)
* @param taskName
* @param cron
* @param runnable
*/
public Boolean addCronTask(String taskName,String cron,Runnable runnable){
if(scheduledTaskMap.containsKey(taskName)){
log.error("定時(shí)任務(wù)["+ taskName+"]已存在,添加失敗");
return Boolean.FALSE;
}
CronTask cronTask = new CronTask(runnable,cron);
ScheduledTask scheduledTask = this.scheduleCronTask(cronTask);
scheduledTaskMap.put(taskName,scheduledTask);
log.info("定時(shí)任務(wù)["+taskName+"]新增成功");
return Boolean.TRUE;
}
/**
* 刪除任務(wù)
* @param taskName
*/
public void cancelCronTask(String taskName){
ScheduledTask scheduledTask = scheduledTaskMap.get(taskName);
if(null != scheduledTask){
scheduledTask.cancel();
scheduledTaskMap.remove(taskName);
}
log.info("定時(shí)任務(wù)["+taskName+"]刪除成功");
}
@Override
public void destroy() {
super.destroy();
scheduledTaskMap.values().forEach(ScheduledTask::cancel);
}
}
線程池?cái)?shù)量問題
由于默認(rèn)是單線程的,如果任務(wù)阻塞時(shí)間過長(zhǎng)則會(huì)導(dǎo)致后續(xù)的任務(wù)阻塞,所以盡量是異步任務(wù)或者是線程池?cái)?shù)量大一點(diǎn),則可以避免這個(gè)問題
DynamicScheduledTaskService
@Service
public class DynamicScheduledTaskService {
private static final Logger log = LoggerFactory.getLogger(DynamicScheduledTaskService.class);
private final DynamicScheduledTaskRegistrar dynamicScheduledTaskRegistrar = new DynamicScheduledTaskRegistrar();
/**
* 新增任務(wù)
* @param taskName
* @param cron
*/
public void add(String taskName,String cron){
Boolean result = dynamicScheduledTaskRegistrar.addCronTask(taskName,cron,() -> print(taskName));
log.info("定時(shí)任務(wù)添加結(jié)果:" + result);
}
/**
* 取消任務(wù)
* @param taskName
*/
public void cancel(String taskName){
dynamicScheduledTaskRegistrar.cancelCronTask(taskName);
}
private void print(String taskName){
log.info(taskName+"開始");
try{
Thread.sleep(9000L);
log.info(taskName+"結(jié)束111");
}catch (Exception ex){
}
log.info(taskName+"結(jié)束");
}
}
SchedulerController
@RestController
@RequestMapping(value = "scheduler")
public class SchedulerController {
@Autowired
private DynamicScheduledTaskService dynamicScheduledTaskService;
@GetMapping(value = "add")
public Object add(String taskName,String cron){
dynamicScheduledTaskService.add(taskName,cron);
return "SUCCESS";
}
@GetMapping(value = "cancel")
public Object cancel(String jobName){
dynamicScheduledTaskService.cancel(jobName);
return "SUCCESS";
}
}
測(cè)試結(jié)果
新增的任務(wù)都睡眠了9S
新增調(diào)度任務(wù)


刪除調(diào)度任務(wù)


以上就是SpringBoot定時(shí)任務(wù)動(dòng)態(tài)擴(kuò)展ScheduledTaskRegistrar詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot ScheduledTaskRegistrar的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- SpringBoot中定時(shí)任務(wù)@Scheduled的多線程使用詳解
- SpringBoot通過@Scheduled實(shí)現(xiàn)定時(shí)任務(wù)及單線程運(yùn)行問題解決
- SpringBoot?ScheduledTaskRegistrar解決動(dòng)態(tài)定時(shí)任務(wù)思路詳解
- SpringBoot中定時(shí)任務(wù)@Scheduled注解的使用解讀
- 解決SpringBoot中的Scheduled單線程執(zhí)行問題
- SpringBoot中使用@scheduled定時(shí)執(zhí)行任務(wù)的坑
- springboot使用定時(shí)器@Scheduled不管用的解決
- 帶你3分鐘帶你搞定Spring Boot中Schedule
相關(guān)文章
RestTemplate發(fā)送HTTP?GET請(qǐng)求使用方法詳解
這篇文章主要為大家介紹了關(guān)于RestTemplate發(fā)送HTTP?GET請(qǐng)求的使用方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家<BR>33+多多進(jìn)步2022-03-03
Java實(shí)現(xiàn)飛機(jī)大戰(zhàn)-連接數(shù)據(jù)庫(kù)并把得分寫入數(shù)據(jù)庫(kù)
這篇文章給大家分享了Java實(shí)現(xiàn)飛機(jī)大戰(zhàn)中連接數(shù)據(jù)庫(kù)并把得分寫入數(shù)據(jù)庫(kù)的相關(guān)知識(shí)點(diǎn)和代碼,有興趣的可以學(xué)習(xí)參考下。2018-07-07
Java使用System.currentTimeMillis()方法計(jì)算程序運(yùn)行時(shí)間的示例代碼
System.currentTimeMillis() 方法的返回類型為 long ,表示毫秒為單位的當(dāng)前時(shí)間,文中通過示例代碼介紹了計(jì)算 String 類型與 StringBuilder 類型拼接字符串的耗時(shí)情況,對(duì)Java計(jì)算程序運(yùn)行時(shí)間相關(guān)知識(shí)感興趣的朋友一起看看吧2022-03-03
JDBC+GUI實(shí)現(xiàn)簡(jiǎn)單學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了JDBC+GUI實(shí)現(xiàn)簡(jiǎn)單學(xué)生管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02
java使用RSA加密方式實(shí)現(xiàn)數(shù)據(jù)加密解密的代碼
這篇文章給大家分享java使用RSA加密方式實(shí)現(xiàn)數(shù)據(jù)加密解密,通過實(shí)例代碼文字相結(jié)合給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友參考下2019-11-11
springboot整合xxl-job的實(shí)現(xiàn)示例
本文主要介紹了springboot整合xxl-job的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
java發(fā)送http get請(qǐng)求的兩種方法(總結(jié))
下面小編就為大家?guī)?lái)一篇java發(fā)送http get請(qǐng)求的兩種方法(總結(jié))。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2017-05-05

