Spring 提供的对 ThreadPoolExecutor 封装的线程池 ThreadPoolTaskExecutor ,直接使用注解启用 @EnableAsync @Async
要使用到线程池,我们还需要来创建Executors,在使用spring中,已经给我们做了很好的支持。只要 @EnableAsync 就可以使用多线程。使用 @Async 就可以定义一个线程任务。通过spring给我们提供的 ThreadPoolTaskExecutor 就可以使用线程池。
默认情况下,Spring将搜索相关的线程池定义:要么在上下文中搜索唯一的 AsyncServiceExecutorbean ,要么搜索名为 asyncServiceExecutor 的Executor bean。如果两者都无法解析,则将使用 SimpleAsyncTaskExecutor 来处理异步方法调用。
定义配置类
要使用 @Configuration 和 @EnableAsync 这两个注解,表示这是个配置类,并且是线程池的配置类@EnableAsync 开始对异步任务的支持
@Configuration@EnableAsyncpublic class ExecutorConfig {private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);private static final int CORE_POOL_SIZE = 10;private static final int MAX_POOL_SIZE = 100;private static final int KEEP_ALIVE_TIME = 10;private static final int QUEUE_CAPACITY = 200;private static final String THREAD_NAME_PREFIX = "Async-Service-";// bean的名称,默认为首字母小写的方法名@Bean(name = "asyncServiceExecutor")public ThreadPoolTaskExecutor asyncServiceExecutor(){ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 核心线程数(默认线程数)executor.setCorePoolSize(CORE_POOL_SIZE);// 最大线程数executor.setMaxPoolSize(MAX_POOL_SIZE);// 允许线程空闲时间(单位:默认为秒)executor.setQueueCapacity(QUEUE_CAPACITY);// 缓冲队列数executor.setKeepAliveSeconds(KEEP_ALIVE_TIME);// 线程池名前缀executor.setThreadNamePrefix(THREAD_NAME_PREFIX);// 线程池对拒绝任务的处理策略// rejection-policy:当pool已经达到max size的时候,如何处理新任务// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 初始化executor.initialize();return executor;}}
创建业务层
创建一个 Service 接口,是异步线程的接口
public interface AsyncService {/** * 执行异步任务 * 可以根据需求,自己加参数拟定,我这里就做个测试演示 */void service1() throws InterruptedException;void service2() throws InterruptedException;}
实现类
将 Service 层的服务异步化,在service()方法上增加注解 @Async("asyncServiceExecutor") , asyncServiceExecutor 方法是前面ExecutorConfig.java 中的方法名,表明service方法进入的线程池是 asyncServiceExecutor 方法创建的@Async 注解来声明一个或多个异步任务,可以加在方法或者类上,加在类上表示这整个类都是使用这个自定义线程池进行操作
@Servicepublic class AsyncServiceImpl implements AsyncService {private static final Logger logger = LoggerFactory.getLogger(AsyncServiceImpl.class);// 发送提醒短信 1@Async("asyncServiceExecutor")@Overridepublic void service1() throws InterruptedException {logger.info("--------start-service1------------");Thread.sleep(5000); // 模拟耗时logger.info("--------end-service1------------");}// 发送提醒短信 2@Async("asyncServiceExecutor")@Overridepublic void service2() throws InterruptedException {logger.info("--------start-service2------------");Thread.sleep(2000); // 模拟耗时logger.info("--------end-service2------------");}}
接下来就是在 Controller 里或者是哪里通过注解@Autowired注入这个 Service
@RestController@RequestMapping("/async")public class AsyncController {@Autowiredprivate AsyncService service;@GetMapping("/test")public void test() throws InterruptedException {service.service1();service.service2();service.service2();service.service2();}}15:43:27.665 INFO 2244 --- [Async-Service-3] AsyncServiceImpl : --------start-service2------------15:43:27.664 INFO 2244 --- [Async-Service-4] AsyncServiceImpl : --------start-service1------------15:43:27.665 INFO 2244 --- [Async-Service-1] AsyncServiceImpl : --------start-service2------------15:43:27.664 INFO 2244 --- [Async-Service-2] AsyncServiceImpl : --------start-service2------------15:43:29.666 INFO 2244 --- [Async-Service-1] AsyncServiceImpl : --------end-service2------------15:43:29.666 INFO 2244 --- [Async-Service-2] AsyncServiceImpl : --------end-service2------------15:43:29.666 INFO 2244 --- [Async-Service-3] AsyncServiceImpl : --------end-service2------------15:43:32.680 INFO 2244 --- [Async-Service-4] AsyncServiceImpl : --------end-service1------------
@Async失效场景
- 异步方法使用static修饰
- 异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类
- 异步方法不能与异步方法在同一个类中
- 类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
- 在Async 方法上标注@Transactional是没用的。 在Async 方法调用的方法上标注@Transactional 有效。
- 调用被@Async标记的方法的调用者不能和被调用的方法在同一类中不然不会起作用
- 使用@Async时要求是不能有返回值的不然会报错的 因为异步要求是不关心结果的
