异步任务la

异步任务

多线程,线程池

非常简单,在配置文件中,添加线程池的配置就可以生效了

  • 修改配置文件
复制代码
spring:
  task:
    execution:
      pool:
        core-size: 10
        max-size: 20
        queue-capacity: 1000
  • 代码使用
复制代码
    @Resource
    ThreadPoolTaskExecutor taskExecutor;
复制代码
taskExecutor.execute(()->{
    redisTemplate.opsForValue().set(key,loginUser,15,TimeUnit.DAYS);
});

异步注解

必须跨类才能生效

  • 配置类中,开启注解
复制代码
@Configuration
@EnableTransactionManagement
@EnableAsync//开启异步任务注解
public class ServerConfig {}

只需要在想使用多线程执行的方法上面,写上@Async就可以了

注意:不能同类调用

复制代码
    @Async
    public void saveOne(WebUserInfo userInfo) {
        super.save(userInfo);
    }

注意:在使用线程的过程中,如果需要子线程和主线程同时操作同一个对象,最好克隆出一个新的对象,给子线程使用。

复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WebUserPossession extends Model<WebUserPossession> implements Cloneable{
​
    @TableId(type = IdType.INPUT)
    private Integer uid;
    //财富值:消费增长
    private Long richNum;
    //魅力值:接单收礼物增长
    private Long charmNum;
​
    @Override
    public WebUserPossession clone(){
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return (WebUserPossession) clone;
    }
}
复制代码
//异步了
WebUserPossession newProssession = possession.clone();
taskExecutor.execute(()->{
    //假设,我需要在子线程中,修改对象中的数据
    //又不想让子线程中修改的数据,影响主线程中的对象
    //需要克隆
    newProssession.setRichNum(10L);//这里是10保存
    newProssession.insert();
});

定时任务

定期的,固定时间的,去执行方法

  • 修改配置类
复制代码
@Configuration
@EnableTransactionManagement
@EnableAsync
@EnableScheduling       //开启定时任务
public class ServerConfig {}

固定不可以更改的定时任务

任务在写完了之后,如果想要修改,就必须重启服务器才能生效。

复制代码
@Component
public class TestTask1 {
    //可以直接获取其他的Service对象,调用其中的方法
    //这样就可以调用数据库和Redis
    @Resource
    NewsService newsService;
    //cron 表达式,你希望当前的方法,每隔多久执行1次
    //秒 分 小时 日期 月份 星期 年(可以省略不写)
    @Scheduled(cron = "0/10 * * * * ?")
    @Async
    public void f1(){
        System.out.println("------------------------------------------");
    }
    //每秒钟执行1次,在每分钟的第10秒到第20秒执行
    @Scheduled(cron = "10-20 * * * * ?")
    @Async
    public void f2(){
        System.out.println("================================");
    }
}
  • 如果想修改最大任务线程数量

  • 修改配置文件

复制代码
spring:
  task:
    execution:
      pool:
        core-size: 10
        max-size: 20
        queue-capacity: 1000
    scheduling:
      pool:
        size: 10

非固定可以被用户操作更改的定时任务

业务逻辑不能轻易更改,只能改变定时任务的开始或者关闭,以及定时任务的周期

定时任务的表达式,要存储到数据库中。

可以通过修改数据库中的表达式内容,来改变定时任务的规则

可以随时开始/停止定时任务

新建数据库

复制代码
CREATE TABLE `qq_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` smallint DEFAULT '0' COMMENT '任务状态 0关闭 1开启',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

添加测试数据

复制代码
INSERT INTO `qingqing`.`qq_task` (`name`, `cron`) VALUES ('定时查询Test', '0/10 * * * * ?')

Java中生成代码

新建测试执行任务的类

复制代码
package com.javasm.qingqing.task.clazz;
​
import org.springframework.stereotype.Component;
​
@Component
public class PrintLogTask implements Runnable{
    @Override
    public void run() {
        System.out.println("-------输出测试日志----------");
    }
}
  • 修改数据库,把已有的执行人物类地址,更新上去
复制代码
UPDATE `qingqing`.`qq_task` SET `clazz` = 'com.javasm.qingqing.task.clazz.PrintLogTask' WHERE `id` = 1

开始任务方法

复制代码
@Component
public class JavasmSchedulerConfigurer implements SchedulingConfigurer {


    ScheduledTaskRegistrar scheduledTaskRegistrar;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        //当前方法,是在项目启动的时候,执行
        //新建线程池,专门负责调用定时任务
        ScheduledExecutorService executorService =
                Executors.newScheduledThreadPool(10);
        ConcurrentTaskScheduler taskScheduler =
                new ConcurrentTaskScheduler(executorService);
        //开启注册 一个定时任务对象
        taskRegistrar.setScheduler(taskScheduler);
        //ScheduledTaskRegistrar taskRegistrar  参数是开启定时任务关键对象
        //当前方法只有在项目启动的时候执行,不能反复执行
        //写一个全局变量,来接收参数,方便随时调用
        this.scheduledTaskRegistrar = taskRegistrar;
    }

    //Spring容器对象
    @Resource
    ApplicationContext applicationContext;

    //注册定时任务的方法----开启定时任务
    public boolean regTask(QqTask task) {
        if (task == null){
            return false;
        }
        //获取任务id
        Integer id = task.getId();
        //获取执行的任务类com.javasm.qingqing.task.clazz.PrintLogTask
        String clazz = task.getClazz();
        //表达式
        String cron = task.getCron();
        //clazz 对应的对象怎么获取到
        try {
            //通过反射获取对象
            Class<?> aClass = Class.forName(clazz);
             /*Object o = aClass.getConstructor().newInstance();
            Runnable runnable = (Runnable) o;*/
            //通过Spring容器获取
            Object bean = applicationContext.getBean(aClass);
            Runnable runnable1 = (Runnable) bean;
            //创建任务对象
            CronTask cronTask = new CronTask(runnable1, cron);
            //开始定时任务
            this.scheduledTaskRegistrar.scheduleCronTask(cronTask);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return true;
    }
}
  • 需要注意的代码
复制代码
        //创建任务对象
        CronTask cronTask = new CronTask(runnable1, cron);
        //开始定时任务
        this.scheduledTaskRegistrar.scheduleCronTask(cronTask);
  • Service
复制代码
@Service
public class TaskServiceImpl implements TaskService {

    @Resource
    JavasmSchedulerConfigurer schedulerConfigurer;
    @Resource
    QqTaskService qqTaskService;

    @Override
    public void start(Integer id) {
        QqTask qqTask = qqTaskService.getById(id);
        boolean b = schedulerConfigurer.regTask(qqTask);
        System.out.println(b);
    }
}

停止

复制代码
@Component
public class JavasmSchedulerConfigurer implements SchedulingConfigurer {


    ScheduledTaskRegistrar scheduledTaskRegistrar;

    private Map<Integer,ScheduledTask> taskMap = new ConcurrentHashMap<>();

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        //当前方法,是在项目启动的时候,执行
        //新建线程池,专门负责调用定时任务
        ScheduledExecutorService executorService =
                Executors.newScheduledThreadPool(10);
        ConcurrentTaskScheduler taskScheduler =
                new ConcurrentTaskScheduler(executorService);
        //开启注册 一个定时任务对象
        taskRegistrar.setScheduler(taskScheduler);
        //ScheduledTaskRegistrar taskRegistrar  参数是开启定时任务关键对象
        //当前方法只有在项目启动的时候执行,不能反复执行
        //写一个全局变量,来接收参数,方便随时调用
        this.scheduledTaskRegistrar = taskRegistrar;
    }

    //Spring容器对象
    @Resource
    ApplicationContext applicationContext;

    //注册定时任务的方法----开启定时任务
    public boolean regTask(QqTask task) {
        if (task == null){
            return false;
        }
        //获取任务id
        Integer id = task.getId();
        //获取执行的任务类com.javasm.qingqing.task.clazz.PrintLogTask
        String clazz = task.getClazz();
        //表达式
        String cron = task.getCron();
        //clazz 对应的对象怎么获取到
        try {
            //通过反射获取对象
            Class<?> aClass = Class.forName(clazz);
             /*Object o = aClass.getConstructor().newInstance();
            Runnable runnable = (Runnable) o;*/
            //通过Spring容器获取
            Object bean = applicationContext.getBean(aClass);
            Runnable runnable1 = (Runnable) bean;
            //创建任务对象
            CronTask cronTask = new CronTask(runnable1, cron);
            //开始定时任务
            ScheduledTask scheduledTask = this.scheduledTaskRegistrar.scheduleCronTask(cronTask);
            //停止定时任务
            //scheduledTask.cancel();
            taskMap.put(id,scheduledTask);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return true;
    }


    //停止定时任务
    public boolean stop(Integer id){
        ScheduledTask scheduledTask = taskMap.get(id);
        if (scheduledTask != null){
            //停止定时任务
            scheduledTask.cancel();
            //从map中移除这个定时任务
            taskMap.remove(id);
            return true;
        }
        return false;
    }
}
  • 核心代码
复制代码
scheduledTask.cancel();
  • Service
复制代码
    @Override
    public void stop(Integer id) {
        schedulerConfigurer.stop(id);
    }

修改开始/停止时的状态

复制代码
@Service
public class TaskServiceImpl implements TaskService {
​
    @Resource
    JavasmSchedulerConfigurer schedulerConfigurer;
    @Resource
    QqTaskService qqTaskService;
    @Resource
    ThreadPoolTaskExecutor taskExecutor;
​
    @Override
    public void start(Integer id) {
        QqTask qqTask = qqTaskService.getById(id);
        if (schedulerConfigurer.regTask(qqTask)){
            taskExecutor.execute(()->{
                qqTask.setStatus(1);
                qqTask.updateById();
            });
        }
    }
​
    @Override
    public void stop(Integer id) {
        if (schedulerConfigurer.stop(id)){
            taskExecutor.execute(()->{
                QqTask task = new QqTask();
                task.setId(id);
                task.setStatus(0);
                task.updateById();
            });
        }
​
    }
}
相关推荐
星释1 小时前
Rust 练习册 116:杂志剪贴侦探游戏
开发语言·后端·rust
SadSunset1 小时前
(16)MyBatis执行流程分析(偏上层架构)
java·架构·mybatis
Tan_Ying_Y1 小时前
JVM内存结构,什么是栈桢?
java·jvm
木井巳2 小时前
【多线程】Thread类及常用方法
java·java-ee
+++.2 小时前
c++雪花屏(vsCode+cmake+mingw+ninja)
开发语言·c++·vscode
小年糕是糕手2 小时前
【C++】内存管理(下)
java·c语言·开发语言·数据结构·c++·算法
缺点内向2 小时前
如何使用C#将Excel工作表拆分为独立文件
开发语言·c#·.net·excel
CoderYanger2 小时前
第 479 场周赛Q2——3770. 可表示为连续质数和的最大质数
java·数据结构·算法·leetcode·职场和发展
L.EscaRC2 小时前
Spring Boot开发中加密数据的模糊搜索
java·spring boot·后端