多线程
使用Async注解启动线程
1. 使用方法
在启动类增加@EnableAsync注解,在需要执行的异步方法上加上@Async注解,@Async实际上就是多线程封装的
注:调用的方法与多线程的方法在同一个类中,此方式不生效
原因:类内部方法调用时,直接进行内部调用,没有走Spring的代理类。Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。调用方法的是对象本身而不是代理对象,没有经过Spring容器。
2. 必须要写在同一个类中的场景
方法1:懒加载注入
参考:SpringBoot @Async 注解无效_51CTO博客_springboot component注解
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.*;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
public class UserListController {
// ...
@Autowired
@Lazy // 懒加载注入,不加会报错
private UserListController userListController;
@GetMapping("/user/queryAll")
public ActionResult queryUserList() {
List<UserList> users = userListService.queryAll();
userListController.testSleep(); //调用
return ActionResult.defaultOk(users);
}
@Async
public void testSleep(){ // 需要多线程运行的函数
try{
log.info("等待中");
Thread.sleep(10000);
}
catch (InterruptedException e){
e.printStackTrace();
}
}
}
方法2:先获取代理类
SpringBoot 注解@Async不生效的解决方法【异步处理】 - 菜鸟的奋斗之路 - 博客园 (cnblogs.com)
import org.springframework.context.ApplicationContext;
@Slf4j
@RestController
public class UserListController {
// ...
@Autowired
private ApplicationContext applicationContext;
@GetMapping("/user/queryAll")
public ActionResult queryUserList() {
List<UserList> users = userListService.queryAll();
applicationContext.getBean(UserListController.class).testSleep(); //看这里
return ActionResult.defaultOk(users);
}
@Async
public void testSleep(){
try{
log.info("等待中");
Thread.sleep(10000);
}
catch (InterruptedException e){
e.printStackTrace();
}
}
}
3. 分属于不同的类中的场景
正常注入和调用,比较简单,这里不再赘述。
定时任务
Scheduled注解
1. 使用方法
在入口类处加上@EnableScheduling注解,在定时任务的方法处加@Scheduled注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableAsync
@EnableScheduling
@SpringBootApplication
public class UamApplication {
public static void main(String[] args) {
SpringApplication.run(UamApplication.class, args);
}
}
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
public class UserListController {
@Scheduled(fixedDelay = 1000)
public void testSchedule(){
log.info("开始执行任务");
try{
log.info("等待3s中");
Thread.sleep(3000);
}
catch (InterruptedException e){
e.printStackTrace();
}
log.info("执行任务完成");
}
}
输出:
2023-04-13 17:17:17 [scheduling-1] INFO com.jx.uam.controller.UserListController - 开始执行任务
2023-04-13 17:17:17 [scheduling-1] INFO com.jx.uam.controller.UserListController - 等待3s中
2023-04-13 17:17:20 [scheduling-1] INFO com.jx.uam.controller.UserListController - 执行任务完成
2023-04-13 17:17:21 [scheduling-1] INFO com.jx.uam.controller.UserListController - 开始执行任务
2023-04-13 17:17:21 [scheduling-1] INFO com.jx.uam.controller.UserListController - 等待3s中
2023-04-13 17:17:24 [scheduling-1] INFO com.jx.uam.controller.UserListController - 执行任务完成
说明:该定时任务,每1s中触发一次,但每次执行时间为3秒,根据上面的输出,可以看出:此时是顺序执行的,如果需要异步执行,需要再加上@Async注解。
2. 异步调度
@Slf4j
@RestController
public class UserListController {
@Async
@Scheduled(fixedDelay = 1000)
public void testSchedule(){
log.info("开始执行任务");
try{
log.info("等待3s中");
Thread.sleep(3000);
}
catch (InterruptedException e){
e.printStackTrace();
}
log.info("执行任务完成");
}
}
输出:
2023-04-13 17:24:38 [task-1] INFO com.jx.uam.controller.UserListController - 开始执行任务
2023-04-13 17:24:38 [task-1] INFO com.jx.uam.controller.UserListController - 等待3s中
2023-04-13 17:24:39 [task-2] INFO com.jx.uam.controller.UserListController - 开始执行任务
2023-04-13 17:24:39 [task-2] INFO com.jx.uam.controller.UserListController - 等待3s中
2023-04-13 17:24:40 [task-3] INFO com.jx.uam.controller.UserListController - 开始执行任务
2023-04-13 17:24:40 [task-3] INFO com.jx.uam.controller.UserListController - 等待3s中
2023-04-13 17:24:41 [task-1] INFO com.jx.uam.controller.UserListController - 执行任务完成
2023-04-13 17:24:41 [task-4] INFO com.jx.uam.controller.UserListController - 开始执行任务
2023-04-13 17:24:41 [task-4] INFO com.jx.uam.controller.UserListController - 等待3s中
2023-04-13 17:24:42 [task-2] INFO com.jx.uam.controller.UserListController - 执行任务完成
3. Scheduled参数详解
SpringBoot定时任务 @Scheduled详解_爱吃耙土豆的博客-优快云博客
SpringBoot使用@Scheduled注解实现定时任务_springboot 使用scheduled_pan_junbiao的博客-优快云博客
在IDEA中,可以看到@Scheduled注解的参数有以下9项:
- String cron() default “”
通过cron表达式定义规则,注意这里有七个参数。[秒] [分] [小时] [日] [月] [周] [年]
- String zone() default “”
指定获取的时区,默认是空,表示使用服务器所在时区,比如Asia/BeiJingi或者Asia/Shanghai。
- long fixedDelay() default -1L
定时任务执行完成之后再次执行定时任务的延时(需等待上次定时任务完成),单位毫秒
- String fixedDelayString() default “”
与fixedDelay意思相同,只是使用字符串的形式。唯一不同的是可以支持占位符,可以在application.yml中定义数值,这里只用变量占位。例如:
# application.yml
time:
fixedDelay: 5000
@Scheduled(fixedDelayString = "${time.fixedDelay}")
- long fixedRate() default -1L
定时任务开始后再次执行定时任务的延时(需等待上次定时任务完成),单位毫秒
- String fixedRateString() default “”
用法同fixedDelayString
- long initialDelay() default -1L
表示第一次延迟多少毫秒执行,单位是毫秒
- String initialDelayString() default “”
用法同fixedDelayString
- TimeUnit timeUnit() default java.util.concurrent.TimeUnit.MILLISECONDS
默认时间单位是毫秒,可以通过timeUnit属性进行单位的设置,例如
@Scheduled(fixedDelay = 2, timeUnit = TimeUnit.SECONDS)