异步任务与定时任务

三生有幸呦 2024-08-28 12:33:02 阅读 55

1.异步任务

        基于TaskExecutionAutoConfiguration配置类中注册的ThreadPoolTaskExecutor线程池对象进行异步任务执行。

1.1 手工执行异步任务

在yml中配置线程池参数

<code>spring:

task:

execution:

pool:

core-size: 5

max-size: 20

queue-capacity: 1000

@Resource

private ThreadPoolTaskExecutor taskExecutor;

taskExecutor.execute(runnable);

1.2 基于异步注解

启动异步注解识别

@Configuration

@EnableAsync//异步任务注解识别

public class AsyncConfig {

}

在需要异步执行的方法添加@Async注解

@Async

public void test(String str){

log.info(str);

}

       不建议直接在本类中使用,因为异步代码散乱到项目各个类中,不易后期维护,切必须跨类才支持异步注解使用。

2.定时任务

        基于TaskSchedulingAutoConfiguration配置类中注册的ThreadPoolTaskScheduler任务调度线程池对象。 不论是基于注解还是基于SchedulingConfigurer进行定时任务实现,都需要首先在配置类中启用定时任务。

@Configuration

@EnableAsync//异步任务

@EnableScheduling //定时任务

public class AsyncConfig {

}

2.1 基于注解

@Component

public class WeatherTask {

private static Logger logger = LogManager.getLogger(WeatherTask.class);

//秒 分 小时 日期 月份 星期 年(可选,留空)

// 日期 或 星期,必定有一个是 ?

//这一分钟之内,从第20秒开始执行,每隔5秒执行一次

@Scheduled(cron = "20/5 * * * * ?")

public void test1(){

logger.info("---------------WeatherTask-----------");

}

//一分钟之内,从第10秒开始,到第20秒,每秒执行1次

@Scheduled(cron = "10-20 * * * * ?")

public void test2(){

logger.info("---------------test2-----------");

}

}

        以上两个任务是串行任务,在一个线程下执行,因为ThreadPoolTaskScheduler线程池中只有1个核心线程数。要使用并行任务,有如下两种方法

添加@Async注解

@Component

public class WeatherTask {

// ThreadPoolTaskScheduler唯一的1个线程用来解析@Scheduled,进入任务调度;

// 到时间要执行任务方法,使用ThreadPoolTaskExecutor来执行方法。

private static Logger logger = LogManager.getLogger(WeatherTask.class);

@Async

@Scheduled(cron = "20/5 * * * * ?")

public void test1(){

logger.info("---------------WeatherTask-----------");

}

@Async

@Scheduled(cron = "10-20 * * * * ?")

public void test2(){

logger.info("---------------test2-----------");

}

}

扩大ThreadPoolTaskScheduler线程数

在yml中配置线程池参数

spring:

task:

scheduling:

pool:

size: 2

不建议基于注解使用定时任务,因为任务状态不可管理。

2.2基于SchedulingConfigurer任务调度对象

需要定时的类名等存入数据库

通过这个类获取任务并执行

CronTask cronTask = new CronTask(r, taskCron);

ScheduledTask scheduledTask = scheduledTaskRegistrar.scheduleCronTask(cronTask);

tasks.put(id,scheduledTask);

创建数据库表sys_task任务表与dao,service,controller

DROP TABLE IF EXISTS `sys_task`;

CREATE TABLE `sys_task` (

`id` varchar(50) NOT NULL,

`task_name` varchar(50) DEFAULT NULL,

`task_clz` varchar(50) DEFAULT NULL,

`task_cron` varchar(50) DEFAULT NULL,

`task_status` int(11) DEFAULT NULL,

`create_by` varchar(50) DEFAULT NULL,

`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `sys_task` VALUES ('1', '任务1', 'com.javasm.redisdemo.task.SmsTaskRunnable', '1/5 * * * * ?', '0', 'admin', '2023-10-07 17:40:50');

@TableName("sys_task")

public class Task {

@TableId(type = IdType.ASSIGN_ID)

private String id;

private String taskName;

private String taskClz;

private String taskCron;

private Integer taskStatus;

private String createBy;

private String createTime;

}

public interface TaskDao extends BaseMapper<Task> {

}

public interface TaskService extends IService<Task> {

List<Task> getRunningTask();

}

@Service("taskService")

public class TaskServiceImpl extends ServiceImpl<TaskDao, Task> implements TaskService {

@Override

public List<Task> getRunningTask() {

QueryWrapper w = new QueryWrapper();

w.eq("task_status", "0");

return this.list(w);

}

}

从SchedulingConfigurer派生子类,在启动tomcat时,加载sys_task下的任务记录启动

@Component

public class JavasmSchedulerConfiguer implements SchedulingConfigurer {

@Resource

private TaskService taskService;

@Resource

private TaskSchedulingProperties properties;

private ScheduledTaskRegistrar scheduledTaskRegistrar;

private Map<String,ScheduledTask> tasks = new HashMap<>();

@Override

public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {

//自定义线程池大小,把串行任务改为并行任务

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);

TaskScheduler taskScheduler = new ConcurrentTaskScheduler(scheduledExecutorService);

scheduledTaskRegistrar.setTaskScheduler(taskScheduler);

this.scheduledTaskRegistrar=scheduledTaskRegistrar;

List<Task> list = taskService.getRunningTask();

for (Task task : list) {

regisTask(task);

}

}

public boolean regisTask(Task task){

String id = task.getId();

String taskClz = task.getTaskClz();//要求类必须从Runnable接口派生,大家自己改造可以不从此接口派生

String taskCron = task.getTaskCron();

Runnable r = createRunnable(taskClz);

if(r!=null){

CronTask cronTask = new CronTask(r, taskCron);

ScheduledTask scheduledTask = scheduledTaskRegistrar.scheduleCronTask(cronTask);

tasks.put(id,scheduledTask);

return true;

}else{

return false;

}

}

public void cancel(String id){

ScheduledTask scheduledTask = tasks.get(id);

if(scheduledTask!=null){

scheduledTask.cancel();

tasks.remove(id);

}

}

//传入类名,必须从Runnable接口派生,穿件Runnable对象

public Runnable createRunnable(String clzName){

try {

Class<?> clz = Class.forName(clzName);

Constructor<?> constructor = clz.getConstructor();

Object o = constructor.newInstance();

if(o instanceof Runnable)

return (Runnable)o;

else

return null;

} catch (Exception e) {

return null;

}

}

}

提供接口供停止/启用任务

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;

import com.javasm.redisdemo.common.AsyncExec;

import com.javasm.redisdemo.common.AxiosResult;

import com.javasm.redisdemo.common.Flags;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController

@RequestMapping("task")

public class TaskController {

@Resource

private TaskService taskService;

@Resource

private JavasmSchedulerConfiguer schedulerConfiguer;

@Resource

AsyncExec asyncExec;

//停止任务

@GetMapping("stop/{id}")

public AxiosResult stopTask(@PathVariable String id){

//停止任务运行

schedulerConfiguer.cancel(id);

//update数据库表任务状态

UpdateWrapper w = new UpdateWrapper();

w.set("task_status", Flags.DISABLED);

w.eq("id",id);

taskService.update(w);

return AxiosResult.suc();

}

//启动任务

@GetMapping("start/{id}")

public AxiosResult startTask(@PathVariable String id){

//注册任务

Task task = taskService.getById(id);

boolean isok = schedulerConfiguer.regisTask(task);

//update数据库表任务状态

if(isok){

UpdateWrapper w = new UpdateWrapper();

w.set("task_status", Flags.OK);

w.eq("id",id);

taskService.update(w);

}

return AxiosResult.suc();

}

//执行一次任务

@GetMapping("one/{id}")

public AxiosResult executeTask(@PathVariable String id){

Task task = taskService.getById(id);

Runnable runnable = schedulerConfiguer.createRunnable(task.getTaskClz());

asyncExec.execute(runnable);

return AxiosResult.suc();

}

}

2.3 认识cron表达式

        Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义: 秒 分 时 日 月 周 年

字段 允许值 允许的特殊字符
秒(Seconds) 0~59的整数 , - * / 四个字符
分(Minutes) 0~59的整数 , - * / 四个字符
小时(Hours) 0~23的整数 , - * / 四个字符
日期(DayofMonth) 1~31的整数(但是你需要考虑你月的天数) ,- * ? / L W C 八个字符
月份(Month) 1~12的整数或者 JAN-DEC , - * / 四个字符
星期(DayofWeek) 1~7的整数或者 SUN-SAT (1=SUN) , - * ? / L C # 八个字符
年(可选,留空)(Year) 1970~2099 , - * / 四个字符

,:表示列出枚举值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。

-  :表示范围。例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次。

*  :通配。

/  :表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次。

?:日与周必定有一个是?。

L  :表示最后,只能出现在DayofWeek和DayofMonth域。如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。

#  :  用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。

#经典案例:

“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秒触发任务



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。