异步任务
多线程,线程池
非常简单,在配置文件中,添加线程池的配置就可以生效了
- 修改配置文件
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();
});
}
}
}