因项目上有动态任务的需求,因此写了一个demo
项目需求:
1、多任务执行,可增删改查(不同任务使用工厂模型)
2、任务执行时间可延时控制
3、任务平滑切换(同一模型任务,如有当前任务,定时任务执行前,当前任务需继续执行)
线程池选择:ThreadPoolTaskScheduler
ThreadPoolTaskScheduler是spring框架根据java内置的定时任务线程池ScheduledExecutorService,封装的一个适用于spring框架的线程池工具类,它比ScheduledExecutorService最大的优势是,支持cron表达式(因为我们这边需要设置任务开始时间,没有使用cron表达式)
效果1:多任务并行动态运行
下图为接口入参,模型编号为test1和test2,任务类型为当前任务,任务周期为5000毫秒
结果:两个任务同时运行
效果2:任务平滑切换
现在(19:00)将test1任务周期改为8000毫秒,rwlx为定时任务
结果:在定时任务开始之前,当前任务正常运行,到了定时时间,平滑切换
实现步骤:
具体逻辑可看代码内注解,比较详细
首先依次创建以下几个类
1、控制层类
@RestController
@RequestMapping("/modelService/v1")
public class ModelController {
@Autowired
private ModelService modelService;
@RequestMapping(value = "/analyseModels.do", method = {RequestMethod.POST})
public String Analysis(@RequestBody RequestParam requestParam){
try {
modelService.analysis(requestParam.getMxbh(),requestParam.getSfqy(),requestParam.getMxkssj(),requestParam.getRwlx(),requestParam.getCron());
} catch (Exception e) {
return e.getMessage();
}
return "成功";
}
}
2、实现层类
@Service
public interface ModelService {
void analysis(String mxbh, String sfqy, Date mxkssj, Integer rwlx, long cron);
}
@Service
public class ModelServiceImpl implements ModelService {
@Autowired
private Analysiser analysiser;
@Override
public void analysis(String mxbh, String sfqy, Date mxkssj, Integer rwlx, long cron) {
//任务类型为0时,为当前任务;否则为定时任务
if(rwlx==0)
analysiser.currentTaskAnalysis(mxbh, sfqy,mxkssj,cron, getTask(mxbh));
else
analysiser.cronTaskAnalsis(mxbh, sfqy,mxkssj,cron, getTask(mxbh));
}
private Class<? extends CompareTask> getTask(String mxbh) {
switch (mxbh) {
case "test1": {
return TestCompareTask.class;
}
case "test2":{
return TestCompareTask2.class;
}
}
return null;
}
}
3、任务实现类
@Service
@Slf4j
public class Analysiser {
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Autowired
private TestCompareTask testCompareTask;
// 定时任务管理器(用于存储正在执行中的任务) ConcurrentHashMap保证线程安全
public static ConcurrentHashMap<String, ScheduledFuture> currentTaskemap = new ConcurrentHashMap<String, ScheduledFuture>();
private ScheduledFuture<?> future;
//private String cron = "0/1 * * * * ?";
//该方法用于增加、修改、取消当前任务
public <T> void currentTaskAnalysis(String mxbh, String sfqy, Date mxkssj, long cron, Class<T> task) {
//mxbh可以唯一确定一个任务;sfqy表示任务停止或启动;mxkssj表示任务开始的时间;cron表示定时周期,task表示定时任务
//判断任务管理器里是否已存在该任务且需要启用任务,如果存在则先停止;否则直接启动新任务。(sqy字段:0为启用,1为停止)
if(!StringUtils.isEmpty(sfqy)&&sfqy.equals("0")){
if(currentTaskemap.containsKey(mxbh)){
ScheduledFuture scheduledFuture = currentTaskemap.get(mxbh);
scheduledFuture.cancel(true);
currentTaskemap.remove(mxbh);
}
try {
future = threadPoolTaskScheduler.scheduleAtFixedRate(()->{
String string ="";
try {
CompareTask compareTask = (CompareTask)task.newInstance();
string = compareTask.getCompareTask(mxbh);
} catch (Exception e) {
log.error("-1",e );
}
log.debug("定时任务"+string+"开启成功,运行周期为:"+cron+"毫秒");
}, mxkssj,cron);
currentTaskemap.put(mxbh, future);
System.out.println(currentTaskemap.toString());
} catch (Exception e) {
log.error("-1",e );
}
}else if(!StringUtils.isEmpty(sfqy)&&sfqy.equals("1")&¤tTaskemap.containsKey(mxbh)){
if (true) {
currentTaskemap.get(mxbh).cancel(true);
currentTaskemap.remove(mxbh);
System.out.println(currentTaskemap);
}
log.debug("定时任务关闭");
}
}
//该方法用于同一模型,有定时任务时,平滑切换任务
public void cronTaskAnalsis(String mxbh, String sfqy, Date mxkssj, long cron, Class<? extends CompareTask> task) {
//创建线程任务,在mxkssj前当前任务继续执行,到了mxkssj时间,当前任务停止,执行定时任务
if(!StringUtils.isEmpty(sfqy)&&sfqy.equals("0")){
/*到达指定时间mxkssj,切换任务*/
ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule( ()->{
if (currentTaskemap.containsKey(mxbh)) {//判断任务池里已有该部门的模型时,需先将之前的任务取消
currentTaskemap.get(mxbh).cancel(true);//获取任务future并取消
currentTaskemap.remove(mxbh);//移除任务
}
currentTaskAnalysis(mxbh, sfqy,mxkssj,cron, task);
log.debug("定时任务开启成功");
},mxkssj);
}
}
}
4、线程池配置类
@Configuration
@Slf4j
public class ThreadPoolConfig {
@Bean(name = "threadPoolTaskScheduler")
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
executor.setPoolSize(60);
executor.setRemoveOnCancelPolicy(true);
executor.setThreadNamePrefix("analyseTaskExecutor-");
//executor.setAwaitTerminationSeconds(60);
return executor;
}
}
5、参数类
@Data
public class RequestParam {
private String mxbh;//模型编号
private String sfqy;//是否启用
private String sffx;
private Integer rwlx;//0为修改当前任务,1为新建定时任务
private long cron;//定时周期
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date mxkssj;//模型分析开始时间
}
使用工厂模式,根据mxbh选择不同类型的模型实现
根据上图getTask()方法内传入不同mxbh,实现不同模型的对象
private Class<? extends CompareTask> getTask(String mxbh) {
switch (mxbh) {
case "test1": {
return TestCompareTask.class;
}
case "test2":{
return TestCompareTask2.class;
}
}
return null;
}
6、模型实现类
public interface CompareTask{
String getCompareTask(String mxbh);
}
@Component
public class TestCompareTask implements CompareTask {
@Override
public String getCompareTask(String mxbh) {
//根据模型规则做处理
System.out.println("test1任务执行成功");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "test1";
}
}
@Component
public class TestCompareTask2 implements CompareTask {
@Override
public String getCompareTask(String bmbh) {
System.out.println("test2任务执行成功");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "test2";
}
}
多有不足,欢迎各位大佬,一起交流学习