使用
@Resource ThreadPoolTaskExecutor taskExecutor;
taskExecutor.execute(()->{ //业务代码 });
开启异步注解
@Configuration @MapperScan("com.javasm.mingming.*.dao") @EnableAsync//开启异步任务注解 public class ServerConfig { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
使用
在方法上面,加 只有跨类调用的时候生效 @Async
启动
@Configuration @MapperScan("com.javasm.mingming.*.dao") @EnableAsync//开启异步任务注解 @EnableScheduling//启动定时任务 public class ServerConfig { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
在编码阶段,已经固定了定时的逻辑,不能在不停止服务器的情况下更改逻辑
配置信息可以不设置,原本自带的
spring: task: execution: pool: core-size: 10 max-size: 50 queue-capacity: 1000 scheduling: pool: size: 10
@Component public class TestTask { Logger logger = LogManager.getLogger(TestTask.class); //我希望多久/什么频率, 执行一次/多次 当前的方法 //每隔5秒执行1次 //cron="秒 分 小时 日期 月份 星期 年" //星期和日期互斥,不能同时设置,必须有1个是? 年是可以省略的 @Scheduled(cron = "0/5 * * * * ?") @Async//异步 public void f1(){ logger.info("---------------测试f1 ------每隔5秒执行1次"); } //从第10秒开始,每秒执行1次,第20秒的时候终止 @Scheduled(cron = "10-20 * * * * ?") @Async//可选,是否加异步,取决于自己 public void f2(){ logger.info("===========f2--从第10秒开始,每秒执行1次,第20秒的时候终止"); } }
#经典案例: 0 * * * * * ? 每分钟执行一次 “30 * * * * ?” 每分钟第30秒触发任务 “30 10 * * * ?” 每小时的10分30秒触发任务 “30 10 1 * * ?” 每天1点10分30秒触发任务 “30 10 1 20 * ?” 每月20号1点10分30秒触发任务 “30 10 1 20 10 ? *” 每年10月20号1点10分30秒触发任务 “30 10 1 20 10 ? 2011” 2011年10月20号1点10分30秒触发任务 “30 10 1 ? 10 * 2011” 2011年10月每天1点10分30秒触发任务 “30 10 1 ? 10 SUN 2011” 2011年10月每周日1点10分30秒触发任务 “15,30,45 * * * * ?” 每分钟的第15秒,30秒,45秒时触发任务 “15-45 * * * * ?” 15到45秒内,每秒都触发任务 “15/5 * * * * ?” 每分钟的每15秒开始触发,每隔5秒触发一次 “15-30/5 * * * * ?” 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次 “0 0/3 * * * ?” 每小时的第0分0秒开始,每三分钟触发一次 “0 15 10 ? * MON-FRI” 星期一到星期五的10点15分0秒触发任务 “0 15 10 L * ?” 每个月最后一天的10点15分0秒触发任务 “0 15 10 LW * ?” 每个月最后一个工作日的10点15分0秒触发任务 “0 15 10 ? * 5L” 每个月最后一个星期四的10点15分0秒触发任务 “0 15 10 ? * 5#3”每个月第三周的星期四的10点15分0秒触发任务
同步新闻
@Resource NewsService newsService; @Scheduled(cron = "0 0 0/1 * * ?") @Async public void f3(){ logger.info("每天0:0:0开始同步新闻列表,每隔1个小时执行1次"); newsService.syncNews(); }
大部分场景是固定的定时任务
新建数据库
CREATE TABLE `sys_task` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL COMMENT '任务名称', `clazz` varchar(255) DEFAULT NULL COMMENT '执行任务的类', `cron` varchar(255) DEFAULT NULL COMMENT '定时任务表达式', `status` int DEFAULT '0' COMMENT '状态0关闭1开启', `ctime` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
创建配置文件
@Component public class JavasmSchedulerConfigurer implements SchedulingConfigurer { Logger logger = LogManager.getLogger(JavasmSchedulerConfigurer.class); private ScheduledTaskRegistrar scheduledTaskRegistrar; private Mapmap = new ConcurrentHashMap<>(); @Override public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { //项目启动的时候,就已经调用了这个方法,是在启动成功之前调用的 //考虑到有可能有多个定时任务,所以,创建一个线程池,专门用来存放定时任务 ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10); ConcurrentTaskScheduler taskScheduler = new ConcurrentTaskScheduler(executorService); //开启注册 定时任务对象 scheduledTaskRegistrar.setScheduler(taskScheduler); //放到全局变量,这样其他方法,就可以调用/使用 参数了 this.scheduledTaskRegistrar = scheduledTaskRegistrar; } @Resource ApplicationContext applicationContext; //创建并开始任务 public boolean regTask(SysTask task){ if (task == null){ return false; } //任务id Integer id = task.getId(); //执行任务的类 String clazz = task.getClazz(); //表达式 String cron = task.getCron(); //new对象 try { Class> aClass = Class.forName(clazz); //Object o = aClass.getConstructor().newInstance(); Object bean = applicationContext.getBean(aClass); //Runnable接口是Java提供的一种用于实现多线程的接口,通过实现Runnable接口,可以将任务逻辑放入run()方法中,随后通过Thread类或其他执行器(如ExecutorService)来创建和启动线程,从而实现多线程并发执行 //这里通过反射从数据获得包名路径 Runnable runnable = (Runnable) bean; //执行任务对象 CronTask cronTask = new CronTask(runnable, cron); //任务开始, ScheduledTask scheduledTask = this.scheduledTaskRegistrar.scheduleCronTask(cronTask); //把已经开始的任务对象,存入全局,等待停止 map.put(id,scheduledTask); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } return true; } public void stop(Integer id) { ScheduledTask scheduledTask = map.get(id); if (scheduledTask != null){ //停止定时任务 scheduledTask.cancel(); map.remove(id); } } }
被强行转换为Runnable的
@Component public class NewTask implements Runnable{ Logger logger = LogManager.getLogger(NewTask.class); @Resource NewsService newsService; @Override public void run() { logger.info("同步新闻列表"); newsService.syncNews(); } }
@Service("sysTaskService") public class SysTaskServiceImpl extends ServiceImplimplements SysTaskService { @Resource JavasmSchedulerConfigurer javasmSchedulerConfigurer; @Resource ThreadPoolTaskExecutor taskExecutor; @Override public void startTask(Integer id) { //查询 任务信息 SysTask sysTask = getById(id); if (javasmSchedulerConfigurer.regTask(sysTask)){ taskExecutor.execute(()->{ //成功 //修改任务状态 sysTask.setStatus(1); updateById(sysTask); }); } } @Override public void stopTask(Integer id) { javasmSchedulerConfigurer.stop(id); //主要业务的代码,不能放到多线程中 //主要业务执行之后的次要业务,比如说修改状态,添加一些附表的值/修改缓存等,可以放入子线程,用来提高效率 //线程池 taskExecutor.execute(()->{ SysTask sysTask = new SysTask(); sysTask.setId(id); sysTask.setStatus(0); updateById(sysTask); }); } }
开始 停止