SpringBoot定时任务Scheduled简易使用及动态多任务使用
一。引入了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
热门文章
- 动物防疫可以打疫苗吗现在多少钱一次(动物疫苗规定)
- 宠物医生资格证书怎么考的(宠物医生怎么考取)
- 「1月17日」最高速度18.2M/S,2025年Hiddify Next每天更新免费节点订阅链接
- python神经网络tf.name_scope和tf.variable_scope函数区别_python
- 「1月21日」最高速度22.1M/S,2025年Hiddify Next每天更新免费节点订阅链接
- 厦门宠物领养贴吧 厦门宠物领养贴吧论坛
- 宠物用品一件代发网站有哪些品牌(宠物用品一件代发进货渠道)
- 「1月29日」最高速度20.1M/S,2025年Hiddify Next每天更新免费节点订阅链接
- 「1月8日」最高速度18.2M/S,2025年Hiddify Next每天更新免费节点订阅链接
- 兰州宠物交易市场在哪里啊(兰州宠物批发市场在哪里)