1.在Spring中经常会用到定时任务,一般会在业务方法上使用@Schedule(cron="定时执行规则"),无法实现从前台动态设置定时任务。
在java中固定频率的任务使用ScheduleExecutorService对象来执行,ScheduleAtFixedRate固定频率执行任务和scheduleWithFixedDelay固定延迟后执行任务。
private static SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); public static void main(String[] args){ ScheduledExecutorService scheduleTask= Executors.newScheduledThreadPool(1); ScheduledFuture<?> scheduledFuture = scheduleTask.scheduleAtFixedRate(() -> { System.out.println("每隔10秒执行一次"+dateFormat.format(new Date())); }, 0, 10, TimeUnit.SECONDS); scheduleTask.schedule(() -> { System.out.println(dateFormat.format(new Date())+"取消定时执行任务"); scheduledFuture.cancel(true); },60,TimeUnit.SECONDS); }
在固定频率,每隔10秒执行一次,1分钟后取消任务。
执行结果见下:
在Spring-context中,提供丰富化的ThreadPoolScheduleTask对象和Trigger触发器,可以很方便地设置任务的执行时间。
在trigger中会根据Cron将下一次执行时间,在此次执行后设置,所以如果要动态改变执行时间,需要先取消任务(清空任务BlockQueue)。
根据上次执行时间和cron设置TriggerContext中的执行时间,保证设置与执行的同步一致性。
代码见下:
@Component public class DynamicScheduledTask { @Autowired private ThreadPoolTaskScheduler taskScheduler; private ScheduledFuture future; private static SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); private String cron = ""; public void setCron(final String cron) { this.cron = cron; stopScheduleTask(); future=taskScheduler.schedule(new Runnable() { @Override public void run() { // 定时任务的业务逻辑 System.out.println("动态修改定时任务cron参数,当前时间:" + dateFormat.format(new Date())); } }, new Trigger() { @Override public Date nextExecutionTime(TriggerContext triggerContext) { // 定时任务触发,可修改定时任务的执行周期 if ("".equals(cron) || cron == null) return null; CronTrigger trigger = new CronTrigger(cron); Date nextExecDate =trigger.nextExecutionTime(triggerContext); return nextExecDate; }}); } public void stopScheduleTask(){ if(future !=null) { future.cancel(true); } } }
在这种回调中执行业务任务的方法中,可以看出java的缺陷,并不能将方法作为参数进行传递。在最古老的lisp中宏就可以方法也是一种数据。
在java 1.8中出现函数式编程,用JDK的ScheduleExecutorService使用了函数式,对比发现其实差别并不是很大,只是一种简化。