前言:
在最近的工作当中,接触到了@Async这个注解的使用,于是我通过学习网上对于这个注解的讲解进行了一个总结,主要是为了对这个注解能够加深印象以及基本的概念。
目录
一、简介
二、基本使用
(一)不带有返回值的异步任务
(二)带有返回值的异步任务
三、自定义线程池
四、常见失效场景
(一)主启动类没有添加@EnableAsync注解
(二)A方法调用被@Async注解修饰的B方法
(三)需要通过@Autowired或@Resource进行注入,不可手动new
(四)被@Async注解修饰的方法必须不可以是static和private,必须为public
五、结语
在Spring当中,被@Async注解标记的方法,称为异步方法,这些方法会在线程当中独立执行,开发人员无需等待它的完成,可以继续其他的业务操作;Spring容器在初始化Bean时,会先判断Bean中是否使用了@Async注解,创建切点,根据切点创建代理,在调用@Async注解标注的方法时,会调用代理,执行切点的Invoke方法,将方法的执行交给线程池进行异步执行。
1、创建UserController类
@RestController
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/getAll")
public void getUsers(){
System.out.println("业务开始");
userService.getUsers();
System.out.println("业务结束");
}
}
2、创建UserService类,模拟业务操作
@Service
public class UserService {
@Async
public void getUsers(){
try {
Thread.sleep(2000);
System.out.println("查询到了所有的用户信息!");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
3、在启动类当中加上@EnableAsync注解,开启异步任务
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
1、创建UserController类
@RestController
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/getAll")
public String getUsers() throws ExecutionException, InterruptedException {
System.out.println("业务开始");
Future result = userService.getUsers();
System.out.println("业务结束");
return result.get();
}
}
2、创建UserService类,模拟业务操作
@Service
public class UserService {
@Async
public Future getUsers(){
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+"查询到了所有的用户信息!");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return new AsyncResult<>("异步任务执行好了");
}
}
3、在启动类当中加上@EnableAsync注解,开启异步任务
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
1、编写ExecutorConfig自定义配置类
@Configuration
public class ExecutorConfig {
@Bean("htt")
public Executor taskExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数5:线程池创建时候初始化的线程数
executor.setCorePoolSize(5);
//最大线程数10:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(10);
//缓冲队列100:用来缓冲执行任务的队列
executor.setQueueCapacity(100);
//允许线程的空闲时间30秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(30);
//线程池对拒绝任务的处理策略:这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;
//如果执行程序已关闭,则会丢弃该任务
executor.setThreadNamePrefix("htt-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
2、修改UserService类,在@Async注解旁边指定我们的Bean名称,从而可以使用我们自定义的线程池
@Service
public class UserService {
@Async("htt")
public void getUsers(){
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+"查询到了所有的用户信息!");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
我们在Controller层当中调用test方法,场景如下
@RestController
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/getAll")
public void getUsers(){
System.out.println("业务开始");
test();
System.out.println("业务结束");
}
@Async
public void test(){
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+"查询到了所有的用户信息!");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
原因:这是因为@Async注解是基础Spring的AOP动态代理模式实现的,test这个方法并没有走Spring的代理类,调用的getUsers方法是原生的并不是Spring所代理的对象,因为并没有经过Spring容器托管,从而造成了注解的失效。
在Controller层当中手动new一个Service对象,调用其中的异步方法
@RestController
public class UserController {
private UserService userService = new UserService();
@RequestMapping("/getAll")
public void getUsers(){
System.out.println("业务开始");
userService.getUsers();
System.out.println("业务结束");
}
}
结果如下
以上就是我目前对于@Async注解的一些理解和总结,如有遗漏,欢迎在评论区补充!