
最近遇到一個(gè)需求,定時(shí)任務(wù)的業(yè)務(wù)邏輯不會(huì)改變,但需要?jiǎng)討B(tài)添加、移除定時(shí)任務(wù),而且定時(shí)執(zhí)行時(shí)間有可能隨時(shí)改變,這可怎么實(shí)現(xiàn)呢?
首先,配置定時(shí)任務(wù)線(xiàn)程池;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
/**
* 定時(shí)任務(wù)線(xiàn)程池配置類(lèi)
* @author 程就人生
* @Date
*/
@Configuration
public class AsyncTheadConfig {
@Bean("threadPoolTaskScheduler")
public ThreadPoolTaskScheduler getThreadPoolTaskScheduler(){
// 定時(shí)任務(wù)線(xiàn)程池
ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
// 線(xiàn)程池大小
executor.setPoolSize(10);
// 線(xiàn)程執(zhí)行前綴
executor.setThreadNamePrefix("ThreadPoolTaskScheduler-");
// executor.setWaitForTasksToCompleteOnShutdown(true);
// executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}
第二步,建立任務(wù),里面包含了定時(shí)任務(wù)需要實(shí)現(xiàn)的業(yè)務(wù)邏輯;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
/**
* 定時(shí)任務(wù)的業(yè)務(wù)邏輯
* @author 程就人生
* @Date
*/
public class TimerThread implements Runnable{
private String uid;
public TimerThread(String uid){
this.uid = uid;
}
@Override
public void run() {
ZonedDateTime zdt = ZonedDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(Thread.currentThread().getName() + " 任務(wù):" + uid + ",執(zhí)行時(shí)間:" + zdt.format(dtf));
}
}
第三步,應(yīng)用定時(shí)任務(wù),包括添加、移除;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/index")
@RestController
public class IndexController1 {
// 定時(shí)任務(wù)線(xiàn)程池
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
// 任務(wù)隊(duì)列管理
@SuppressWarnings("rawtypes")
private ConcurrentHashMap<String, ScheduledFuture> futureMap = new ConcurrentHashMap<String, ScheduledFuture>();
// 加入新的任務(wù)進(jìn)來(lái)
@SuppressWarnings({ "rawtypes" })
@GetMapping("/insert/{uid}/{time}")
public Object insert(@PathVariable("uid") String uid, @PathVariable("time") String time){
// 定時(shí)任務(wù)執(zhí)行類(lèi)
TimerThread timerCollectData = new TimerThread(uid);
// 通過(guò)ThreadPoolTaskScheduler類(lèi),設(shè)定定時(shí)時(shí)間
ScheduledFuture future = threadPoolTaskScheduler.schedule(timerCollectData, new CronTrigger("*/"+time+" * * * * ?"));
// 加入到隊(duì)列中
futureMap.put(uid, future);
return null;
}
// 移除已有的一個(gè)任務(wù)
@SuppressWarnings("rawtypes")
@GetMapping("/remove/{uid}")
public Object remove(@PathVariable("uid") String uid){
ScheduledFuture scheduledFuture = futureMap.get(uid);
if(scheduledFuture != null){
// 取消定時(shí)任務(wù)
scheduledFuture.cancel(true);
// 如果任務(wù)取消需要消耗點(diǎn)時(shí)間
boolean cancelled = scheduledFuture.isCancelled();
while (!cancelled) {
scheduledFuture.cancel(true);
System.out.println(uid + "取消中");
}
// 最后從隊(duì)列中刪除
futureMap.remove(uid);
}
return null;
}
}
最后,運(yùn)行入口程序,打開(kāi)瀏覽器進(jìn)行測(cè)試;通過(guò)瀏覽器分別執(zhí)行了localhost:8080/index/insert/1000/10、localhost:8080/index/insert/2000/20,也就是添加了兩個(gè)任務(wù),任務(wù)1000每10s執(zhí)行一次,任務(wù)2000每20s執(zhí)行一次;

執(zhí)行 http://localhost:8080/index/remove/1000,把1000的任務(wù)移除掉,再看執(zhí)行結(jié)果,只剩下任務(wù)2000,ok,動(dòng)態(tài)添加、移除定時(shí)任務(wù)編碼完成。

當(dāng)然,這里為了測(cè)試,把管理任務(wù)的隊(duì)列直接放到了Controller里,實(shí)際應(yīng)用時(shí)應(yīng)保持全局唯一。
最后總結(jié)
通過(guò)這個(gè)需求,我們又用到了一個(gè)類(lèi)ThreadPoolTaskScheduler,它有別于ThreadPoolTaskExecutor類(lèi),有興趣有時(shí)間的可以查看源碼。
動(dòng)態(tài)添加、移除定時(shí)任務(wù)的操作流程,大致可以分為以下四個(gè)步驟:
1.建立一個(gè)定時(shí)任務(wù)線(xiàn)程池;
2.為定時(shí)任務(wù)線(xiàn)程池建立一個(gè)隊(duì)列,來(lái)管理這些任務(wù);
3.根據(jù)唯一標(biāo)識(shí),往定時(shí)任務(wù)線(xiàn)程池和隊(duì)列里分別添加這個(gè)任務(wù);
4.根據(jù)唯一標(biāo)識(shí),從定時(shí)任務(wù)線(xiàn)程池里取消一個(gè)任務(wù),并從隊(duì)列里移除這個(gè)任務(wù)。