一。引入了spring-boot-starter包即可,无需额外jar包:

        <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter</artifactId>         </dependency>

二。启动类添加注解@EnableScheduling:

@SpringBootApplication//@EnableAutoConfiguration  @EnableScheduling//支持定时任务 public class SpringBootApplication {      public static void main(String[] args) {         SpringApplication.run(SpringBootApplication.class, args);     }  }

三。定时任务代码编写:

import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;  import java.util.Date;   @Component public class DemoSchedule {      @Scheduled(cron = "0 0/1 * * * ?")//每隔一分钟执行     public void test1() {         System.out.println("定时任务执行:" + new Date());     }      }

其中,cron表达式是spring内置支持的时间表达式,详解待续,可百度“在线cron”,帮你自动生成。 

 

上面使用的定时任务很简单,但是有缺点:

多个定时任务使用的是同一个调度线程,所以任务是阻塞执行的,执行效率不高。如果出现任务阻塞,导致一些场景的定时计算没有实际意义,比如每天12点的一个计算任务被阻塞到1点去执行,会导致结果并非我们想要的。

解决方式1:替换默认线程池,默认线程池只有一个线程:

@Configuration public class ScheduledConfig implements SchedulingConfigurer {      @Autowired     private TaskScheduler myThreadPoolTaskScheduler;      @Override     public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {         //简单粗暴的方式直接指定         //scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(2));         //也可以自定义的线程池,方便线程的使用与维护         scheduledTaskRegistrar.setTaskScheduler(myThreadPoolTaskScheduler);     }      @Bean(name = "myThreadPoolTaskScheduler")     public TaskScheduler getMyThreadPoolTaskScheduler() {         ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();         taskScheduler.setPoolSize(20);//定时任务数量需小于池大小。否则,还是会有定时任务等待执行         taskScheduler.setThreadNamePrefix("taskScheduler-");         taskScheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());         //调度器shutdown被调用时等待当前被调度的任务完成         taskScheduler.setWaitForTasksToCompleteOnShutdown(true);         //等待时长         taskScheduler.setAwaitTerminationSeconds(60);         return taskScheduler;     } }

解决方式2:方式一的本质改变了任务调度器默认使用的线程池,接下来这种是不改变调度器的默认线程池,而是把任务交给一个异步线程池去执行:

首先在启动类上添加@EnableAsync 注解开启异步任务支持
然后在定时任务的方法加上@Async即可,默认使用的线程池为SimpleAsyncTaskExecutor(该线程池默认来一个任务创建一个线程,就会不断创建大量线程,极有可能压爆服务器内存。当然它有自己的限流机制,这里就不多说了)
项目中为了更好的控制线程的使用,我们可以自定义我们自己的线程池,使用方式@Async("myThreadPool")

  @Scheduled(fixedRate = 1000*10,initialDelay = 1000*20)   @Async("myThreadPoolTaskExecutor")   //@Async   public void scheduledTest02(){       System.out.println(Thread.currentThread().getName()+"--->xxxxx--->"+Thread.currentThread().getId());   }    //自定义线程池   @Bean(name = "myThreadPoolTaskExecutor")   public TaskExecutor  getMyThreadPoolTaskExecutor() {       ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();       taskExecutor.setCorePoolSize(20);//定时任务能够同时执行的线程数。定时任务数量需小于池大小。否则,还是会有定时任务等待执行       taskExecutor.setMaxPoolSize(200);//定时任务能被同时调度的数量。调度后不一定马上执行,取决于上面的corePoolSize       taskExecutor.setQueueCapacity(25);       taskExecutor.setKeepAliveSeconds(200);       taskExecutor.setThreadNamePrefix("Haina-ThreadPool-");       // 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者       taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());       //调度器shutdown被调用时等待当前被调度的任务完成       taskExecutor.setWaitForTasksToCompleteOnShutdown(true);       //等待时长       taskExecutor.setAwaitTerminationSeconds(60);       taskExecutor.initialize();       return taskExecutor;   }

 

上面能够解决多定时任务的阻塞问题,但如果多定时任务同时还是动态的呢?即cron表达式动态传入呢?

1.自定义定时任务调度线程池注入spring。默认线程池只有一个线程。

2.实现SchedulingConfigurer接口。多个任务编写多个类实现SchedulingConfigurer接口即可。

@Configuration public class ThreadPoolTaskSchedulerConfig {      //自定义定时任务调度线程池注入spring     @Bean     public ThreadPoolTaskScheduler threadPoolTaskScheduler() {         ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();         threadPoolTaskScheduler.setPoolSize(20);//定时任务数量需小于池大小。否则,可能会有定时任务等待执行         threadPoolTaskScheduler.setThreadNamePrefix("taskScheduler-");         threadPoolTaskScheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());         threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);//调度器shutdown被调用时等待当前被调度的任务完成         threadPoolTaskScheduler.setAwaitTerminationSeconds(60);//等待时长         return threadPoolTaskScheduler;     }  }
@Component public class MySchedulingConfigurer implements SchedulingConfigurer {      @Override     public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {         scheduledTaskRegistrar.addTriggerTask(task(), trigger());     }      private Runnable task() {         return new Runnable() {             @Override             public void run() {                 //业务逻辑部分                 System.out.println("task1 ==== I am going:" + LocalDateTime.now());             }         };     }      private Trigger trigger() {         return new Trigger() {             @Override             public Date nextExecutionTime(TriggerContext triggerContext) {                 //每一次任务触发,都会执行这里的方法一次,重新获取下一次的执行时间。所以它是下下次才生效的,即不是实时生效                 String cron = "0/10 * * * * ?";//10秒执行一次,动态注入:这里改为从数据库获取表达式就可以了                 CronTrigger cronTrigger = new CronTrigger(cron);                 Date date = cronTrigger.nextExecutionTime(triggerContext);                 return date;             }         };     }  }

以上spring task动态定时任务缺点很明显,时间修改后不能马上生效,要等到下次执行加载Trigger后更新cron,下下次才会生效,所以想要即时生效就得用Quartz了!可参考:https://blog.csdn.net/yzh_1346983557/article/details/103990044