@Async 注解是 Spring 提供的一个公共线程池, 需要配合 @EnableAsync 注解开启。使用 @Async 注解的方法称之为异步方法, 相当于为该方法开了一个新的线程, 使其在不影响主线程的前提下运行。本文介绍了 @Async 注解的简单使用、自定义线程池以及重写配置等内容。
前言
@Async
注解为spring提供的一个公共线程池,需要配合@EnableAsync
注解开启。
使用@Async
注解的方法称之为异步方法,相当于为该方法开了一个新的线程,使其在不影响主线程的前提下运行。
简单使用
使用@EnableAsync
配置异步功能
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
@Configuration
@EnableAsync
public class AsyncConfig {
}
服务层:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncTestService {
private static final Logger logger = LoggerFactory.getLogger(AsyncTestService.class);
/**
* 异步方法
* @throws InterruptedException
*/
@Async
public void method2() throws InterruptedException {
logger.info("---------method2开始--------------");
Thread.sleep(3000);
logger.info("---------end2--------------");
}
/**
* 异步方法
* @throws InterruptedException
*/
@Async
public void method3() throws InterruptedException {
logger.info("---------method3开始--------------");
Thread.sleep(3000);
logger.info("---------end3--------------");
}
/**
* 非异步方法
* @throws InterruptedException
*/
public void method1() throws InterruptedException {
logger.info("---------------------非异步方法开始-------------------");
Thread.sleep(7777);
logger.info("----------------------非异步方法开始结束--------------------");
}
}
测试:
import com.example.demo.service.AsyncTestService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class WebController {
private static final Logger logger = LoggerFactory.getLogger(WebController.class);
@Autowired
AsyncTestService asyncTestService;
@RequestMapping("ahzoo")
@ResponseBody
public void toTest() throws InterruptedException {
logger.info("----------主线程开始------------");
long start = System.currentTimeMillis();
asyncTestService.method1();
asyncTestService.method2();
asyncTestService.method3();
Thread.sleep(3000);
logger.info("--------------主线程结束,执行用时:" + (System.currentTimeMillis() - start) + "-------------ahzoo.cn------------------");
}
}
注意观察日志打印时间,可以看到主线程的非异步方法先执行完毕,然后主线程中的其他异步方法在不影响主线程的前提下开始执行。最终主线程的用时约等于自身的3秒加非异步方法的7秒多。
自定义
多数情况下,@Async
注解的默认配置就已经足够。
自定义线程池
除了使用@Async
提供的默认线程池外,我们还可以使用自定义线程池(推荐使用spring提供的线程池:ThreadPoolTaskExecutor
),然后指定使用的线程池即可:@Async("自定义的线程池名")
.
示例:
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.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
public class AsyncExecutorConfigurer {
// corePoolSize: 定义核心线程数为 3
private static final Integer CORE_POOL_SIZE = 3;
// maximumPoolSize:定义最大线程数为 9
private static final Integer MAX_POOL_SIZE = 9;
// workQueue:定义任务队列为 ArrayBlockingQueue,并且容量为 90
private static final Integer QUEUE_CAPACITY = 90;
// keepAliveTime: 定义等待时间为 1000ms
private static final Integer KEEP_ALIVE_TIME = 1000;
// 线程池名称前缀
private static final String THREAD_NAME = "Ahzoo-";
@Bean
public Executor getCustomizeAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setThreadNamePrefix(THREAD_NAME);
executor.setKeepAliveSeconds(KEEP_ALIVE_TIME);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 初始化线程池(ThreadPoolTaskExecutor线程池需要进行初始化)
executor.initialize();
return executor;
}
}
使用:
服务层:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncTestService {
private static final Logger logger = LoggerFactory.getLogger(AsyncTestService.class);
/**
* 异步方法
* @throws InterruptedException
*/
@Async
public void method2() throws InterruptedException {
logger.info("---------method2开始--------------");
Thread.sleep(3000);
logger.info("---------end2--------------");
}
/**
* 异步方法
* @throws InterruptedException
*/
@Async
public void method3() throws InterruptedException {
logger.info("---------method3开始--------------");
Thread.sleep(3000);
logger.info("---------end3--------------");
}
/**
* 自定义异步方法
* @throws InterruptedException
*/
@Async("getCustomizeAsyncExecutor")
public void method4() throws InterruptedException{
logger.info("---------method4开始--------------");
Thread.sleep(7000);
logger.info("---------end4--------------");
}
/**
* 自定义异步方法
* @throws InterruptedException
*/
@Async("getCustomizeAsyncExecutor")
public void method5() throws InterruptedException{
logger.info("---------method5开始--------------");
Thread.sleep(9000);
logger.info("---------end5--------------");
}
/**
* 非异步方法
* @throws InterruptedException
*/
public void method1() throws InterruptedException {
logger.info("---------------------非异步方法开始-------------------");
Thread.sleep(7777);
logger.info("----------------------非异步方法开始结束--------------------");
}
}
测试:
import com.example.demo.service.AsyncTestService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class WebController {
private static final Logger logger = LoggerFactory.getLogger(WebController.class);
@Autowired
AsyncTestService asyncTestService;
@RequestMapping("ahzoo")
@ResponseBody
public void toTest() throws InterruptedException {
logger.info("----------主线程开始------------");
long start = System.currentTimeMillis();
asyncTestService.method1();
asyncTestService.method2();
asyncTestService.method3();
asyncTestService.method4();
asyncTestService.method5();
Thread.sleep(3000);
logger.info("--------------主线程结束,执行用时:" + (System.currentTimeMillis() - start) + "-------------ahzoo.cn------------------");
}
}
同样注意观察日志打印时间,可以看到除了非异步方法,各个异步方法之间都是独立异步执行,且不影响主线程。
同时,由于只配置了三个线程池,所以第四个使用@Async
注解的方法并没有立即执行。
重写配置
打开@Async
注解的配置接口AsyncConfigurer
。
public interface AsyncConfigurer {
/**
* 获取异步线程
*/
@Nullable
default Executor getAsyncExecutor() {
// 返回值是一个线程池,但是默认返回的是null,所以实现AsyncConfigurer接口后,只需要在这里返回自定义的线程池即可
return null;
}
/**
* 这个是一个异常处理器,如果要自定义的话,可以重新此方法
*/
@Nullable
default AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
// logger.error("ahzoo");
return null;
}
}
如果自定义配置的话,就实现AsyncConfigurer
类,然后重写方法即可。
线程池的创建方式可以参照上面自定义中的线程池创建方式。
使用方式就是默认的使用方式,直接使用@Async
注解即可。
版权声明
本文依据
CC-BY-NC-SA 4.0
许可协议授权,请您在转载时注明文章来源为
Z次元
,若本文涉及转载第三方内容,请您一同注明。
知识积累
人非生而知之者,孰能无惑?惑而不从师,其为惑也,终不解矣。
评论区
发表评论
前言
简单使用
自定义
自定义线程池
重写配置
1
您好~我是腾讯云开发者社区运营,关注了您分享的技术文章,觉得内容很棒,我们诚挚邀请您加入腾讯云自媒体分享计划。完整福利和申请地址请见:https://cloud.tencent.com/developer/support-plan
作者申请此计划后将作者的文章进行搬迁同步到社区的专栏下,你只需要简单填写一下表单申请即可,我们会给作者提供包括流量、云服务器等,另外还有些周边礼物。|´・ω・)ノ