提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
本文主要记录下Springboot中异步方法,定时任务以及方法重试的使用
一、异步方法
在代码中默认为同步方法,也就是按照代码的顺序从上至下一步一步执行,每一行代码执行完之后才会执行下一行代码
public void A(){
log.info("A 方法开始执行~");
B();
C();
log.info("A 方法结束执行~");
}
public void B(){
log.info("B 方法开始执行~");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("B 方法结束执行~");
}
public void C(){
log.info("C 方法开始执行~");
log.info("C 方法结束执行~");
}
@Test
void test() {
asynTest.A();
}
执行结果为
20:40:11.438 [main] INFO com.wkh.demo.AsynTest - A 方法开始执行~
20:40:11.441 [main] INFO com.wkh.demo.AsynTest - B 方法开始执行~
20:40:13.453 [main] INFO com.wkh.demo.AsynTest - B 方法结束执行~
20:40:13.453 [main] INFO com.wkh.demo.AsynTest - C 方法开始执行~
20:40:13.453 [main] INFO com.wkh.demo.AsynTest - C 方法结束执行~
20:40:13.453 [main] INFO com.wkh.demo.AsynTest - A 方法结束执行~
如果不想等待某一个方法执行完成之后再执行下一行代码就需要使用异步方法,这里使用的是注解的方式@Async(可以加载类上面也可以加在方法上面),在启动类上增加@EnableAsync注解。
有两个注意点
1:如果是非异步方法调用本类的异步方法,需要单独放在一个类里面,注册到bean里面,因为本类的方法调用用的时this.的方式,无法通过动态代理的方式增强方法,所以会无效
2:异步方法的返回值只能有两种,一种是void一种是Future<>可以指定泛型,返回的时候通过AsyncResult<>对象返回,这个返回的对象有两个常用的方法
isDone():判断异步方法是否执行完成
get():无参的情况会执行阻塞获取,会阻塞代码,直到获取到异步方法的返回值为止才会执行下面的代码
get(long timeout, TimeUnit unit):有参的情况下第一个参数为设置的等待时间,第二个参数为设置的时间单位,这种设置等待时间的方法如果在设置的时间内获取到异步方法的返回值继续执行后面的代码,如果在规定时间内没接收到返回值则会抛出异常
public void A() throws ExecutionException, InterruptedException, TimeoutException {
log.info("A 方法开始执行~");
Future<String> b = asyncDemo.B();
b.get(1000, TimeUnit.MILLISECONDS);
C();
log.info("A 方法结束执行~");
}
public Future<String> B(){
log.info("B 方法开始执行~");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("B 方法结束执行~");
return new AsyncResult<>("执行完成");
}
二、定时任务
使用方法要在启动类上增加@EnableScheduling注解,在需要定时操作的方法上面增加@Scheduled()注解,里面可以添加fixedDelay或fixedRate
@Scheduled(fixedDelay=1000):上一次执行完毕时间点后1秒再次执行
@Scheduled(fixedRate=2000): 上一次开始执行时间点后1秒再次执行
最常用的是cron规则 @Scheduled(cron=“* * * * * ?”) ,可以在cron在线生成网站中生成
有一点要注意,springboot中的定时任务默认情况下是SingleThreadScheduledExecutor,是一个单线程池,有时会遇到某个定时任务会被另一个定时任务所阻塞,可以通过引用线程池来解决这个问题
引用线程池也非常简单,只需要定义一个类实现SchedulingConfigurer接口,重写configureTasks方法,注入到bean里,在里面可以注册线程池
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
//设定一个长度20的定时任务线程池
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(20));
}
}
需要注意的是,虽然引入了多个线程,但是只有不同的定时方法才会使用不同的线程,同一个定时任务还是走同一个线程,如果想解决这个问题可以通过将其改为异步方法来解决
三、方法重试
在实际开发中,可能调用代码的时候会遇到非代码问题出现的问题,导致报错,例如出现网络波动等等,这个时候我们可能就会将某些接口或某些方法设置为最多调用几次才报错返回
在应用的时候需要在pom文件中添加两个依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.11</version>
</dependency>
添加依赖之后需要在启动类上加上@EnableRetry注解开启重试机制
之后在需要方法重试的方法上加上@Retryable注解就可以了(默认为三次)
1.可以自己设置重试的次数@Retryable(maxAttempts = 5)设置次数为5(包括第一次调用)
2.可以设置重试的间隔时间@Retryable(backoff =@Backoff(delay = 2000))
单位是毫秒,默认为1000ms
3.可以指定异常不重试@Retryable(exclude =ArithmeticException.class)
@Retryable(maxAttempts = 5)
public void C(){
log.info("C 方法开始执行~");
int a = 1/0;
log.info("C 方法结束执行~");
}
@Test
void test(){
asynTest.C();
}
总结
异步方法,定时任务以及方法重试在项目中还是经常会使用到的,使用的方式也比较简单,通过上文可以实现基本的使用,自己的学习笔记,如有错误请多指正