应用场景:
发送短信,日志记录,等与主线程无关的业务。
第一步:
需要在启动类加入@EnableAsync使异步调用@Async注解生效,
在需要异步执行的方法上加入此注解即可@Async("threadPool"),threadPool为自定义线程池
在默认情况下,未设置TaskExecutor时,默认是使用SimpleAsyncTaskExecutor这个线程池,但此线程不是真正意义上的线程池,因为线程不重用,每次调用都会创建一个新的线程。可通过控制台日志输出可以看出,每次输出线程名都是递增的。所以最好我们来自定义一个线程池。
什么情况下会导致@Async异步方法会失效?
- 调用同一个类下注有@Async异步方法:在spring中像@Async和@Transactional、cache等注解本质使用的是动态代理,其实Spring容器在初始化的时候Spring容器会将含有AOP注解的类对象“替换”为代理对象(简单这么理解),那么注解失效的原因就很明显了,就是因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器,那么解决方法也会沿着这个思路来解决。
- 调用的是静态(static )方法
- 调用(private)私有化方法
第二步:自己来个demo
package com.thread.pool.task.thread_pool.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {
/**
* 如下方式会完全使@Async 失效
* 1,异步方法使用static修饰
* 2.异步类没有使用Component注解(或其他注解导致spring无法扫描到异步类
* 3.异步方法不能与异步方法在同一类在同一个类
* 4.在本类中调用本类得异步方法无效 在spring中像@Async和@Transactional、cache等注解本质使用的是动态代理,其实Spring容器在初始化的时候Spring容器会将含有AOP注解的类对象“替换”为代理对象(简单这么理解),那么注解失效的原因就很明显了,就是因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器,那么解决方法也会沿着这个思路来解决。
* 5.如果使用springboot框架必须在启动类中增加@EnableAsnc注解
* @return
*/
@Bean("threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(1);
threadPoolTaskExecutor.setMaxPoolSize(4);
threadPoolTaskExecutor.setQueueCapacity(4);
threadPoolTaskExecutor.setKeepAliveSeconds(30);
threadPoolTaskExecutor.setThreadNamePrefix("异步调用线程名称前缀:");
threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
package com.thread.pool.task.thread_pool.service;
public interface HelloService {
void hello(int i);
}
package com.thread.pool.task.thread_pool.service.impl;
import com.thread.pool.task.thread_pool.service.HelloService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class HelloServiceImpl implements HelloService {
@Override
@Async("threadPoolTaskExecutor")
public void hello(int i) {
System.out.println("异步调用第:"+ i+" 次");
}
}
package com.thread.pool.task.thread_pool.controller;
import com.thread.pool.task.thread_pool.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloWorldController {
@Autowired
private HelloService helloService;
@GetMapping(value = "/hello")
@ResponseBody
public String hello(){
for (int i = 0; i < 10; i++) {
helloService.hello(i);
}
return "hello";
}
}
查看控制台输出是无序说明异步调用:
我们把@Async 先注释掉再进行请求查看:结果是按照顺序输出
- 异步请求和异步调用的使用到这里基本就差不多了,有问题还希望大家多多指出。
- 如果感觉不明显还有另一种方式,就是把循环中输出的i追加到一个字符串中进行返回,如果hello()方法上没有加@Async注解那么返回的字符串一定是0-9,如果加上了@Async注解返回的字符串就是比较随机,拼接返回的字符串就是在主线程执行完之前异步调用执行的结果的拼接结果。