原來寫了一篇關於springboot實現計劃任務的文章,但是有較多的人都在問爲什麼數據庫變更後計劃任務沒刷新,怎麼去動態獲取,怎麼實現多線程併發執行,所以現在新開一篇文章,重新實現計劃任務的方法,抽象出刷新功能和併發功能。
動態獲取並刷新的類:
@Component
public class ScheduledTask implements SchedulingConfigurer {
private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTask.class);
private volatile ScheduledTaskRegistrar registrar;
private final ConcurrentHashMap<Integer, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<Integer, ScheduledFuture<?>>();
private final ConcurrentHashMap<Integer, CronTask> cronTasks = new ConcurrentHashMap<Integer, CronTask>();
@Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
//設置20個線程,默認單線程
registrar.setScheduler(Executors.newScheduledThreadPool(20));
this.registrar = registrar;
}
public void refresh(List<LogTask> tasks){
//取消已經刪除的策略任務
Set<Integer> sids = scheduledFutures.keySet();
for (Integer sid : sids) {
if(!exists(tasks, sid)){
scheduledFutures.get(sid).cancel(false);
}
}
for (LogTask logTask : tasks) {
//ScheduledTaskRunnable t = new ScheduledTaskRunnable(logTask.getTask_id(), logTask.getRule_db_id());
String expression = logTask.getExpression();
//計劃任務表達式爲空則跳過
if(StringUtils.isEmpty(expression)){
continue;
}
//計劃任務已存在並且表達式未發生變化則跳過
if(scheduledFutures.containsKey(logTask.getTask_id()) && cronTasks.get(logTask.getTask_id()).getExpression().equals(expression)){
continue;
}
//如果策略執行時間發生了變化,則取消當前策略的任務
if(scheduledFutures.containsKey(logTask.getTask_id())){
scheduledFutures.get(logTask.getTask_id()).cancel(false);
scheduledFutures.remove(logTask.getTask_id());
cronTasks.remove(logTask.getTask_id());
}
CronTask task = new CronTask(new Runnable() {
@Override
public void run() {
//每個計劃任務實際需要執行的具體業務邏輯
}
}, expression);
ScheduledFuture<?> future = registrar.getScheduler().schedule(task.getRunnable(), task.getTrigger());
cronTasks.put(logTask.getTask_id(), task);
scheduledFutures.put(logTask.getTask_id(), future);
}
}
private boolean exists(List<LogTask> tasks, Integer tid){
for(LogTask logTask:tasks){
if(logTask.getTask_id() == tid){
return true;
}
}
return false;
}
@PreDestroy
public void destroy() {
this.registrar.destroy();
}
}
唯一用的對象類:
@Data
public class LogTask {
private int task_id;
private String expression;
}
如何使用
- 在你的項目中找個地方去實現從數據庫獲取這個對象集合的方法
- 把獲取到的數據通過調用 refresh 方法去刷新到計劃任務列表中
- 每次數據庫中的計劃任務發生變化後重新執行上面兩步即可