SpringBoot實現多線程並發動態執行計劃任務

原來寫了一篇關於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;
}

如何使用

  1. 在你的項目中找個地方去實現從數據庫獲取這個對象集合的方法
  2. 把獲取到的數據通過調用 refresh 方法去刷新到計劃任務列表中
  3. 每次數據庫中的計劃任務發生變化後重新執行上面兩步即可
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章