@Async如何使用
- 異步的方法上加上@Async異步注解
- 啟動類中需要加上@EnableAsync才有效
使用時類似於下列函數:
new Thread(()-> System.out.println("hello world !"))
@Async線程池
- 默認線程池
無論重復多少次,都默認8個左右的線程在跑
異步線程:task-1執行成功
異步線程:task-2執行成功
異步線程:task-3執行成功
異步線程:task-4執行成功
異步線程:task-5執行成功
異步線程:task-6執行成功
異步線程:task-7執行成功
異步線程:task-1執行成功
異步線程:task-2執行成功
異步線程:task-8執行成功
異步線程:task-3執行成功
異步線程:task-8執行成功
異步線程:task-6執行成功
異步線程:task-8執行成功
異步線程:task-5執行成功
異步線程:task-3執行成功
異步線程:task-2執行成功
異步線程:task-1執行成功 - 自定義線程池配置
/**
* 為Async配置自定義線程池
* 可存放的最多線程數為:MAX_POOL_SIZE+QUEUE_CAPACITY,同時進入線程池的數量超過這個就會報錯
* 線程名稱最多為MAX_POOL_SIZE個
*/
@Configuration
public class MyTaskExecutorConfig implements AsyncConfigurer {
/**
* 設置ThreadPoolExecutor的核心池大小。
*/
private static final int CORE_POOL_SIZE = 2;
/**
* 設置ThreadPoolExecutor的最大池大小。
*/
private static final int MAX_POOL_SIZE = 3;
/**
* 設置ThreadPoolExecutor的BlockingQueue的容量。
*/
private static final int QUEUE_CAPACITY = 5;
/**
* 配置類實現AsyncConfigurer接口並重寫getAsyncExcutor方法,並返回一個ThreadPoolTaskExevutor
* 這樣我們就獲得了一個基於線程池的TaskExecutor
*/
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
taskExecutor.initialize();
return taskExecutor;
}
}
異步線程:ThreadPoolTaskExecutor-2執行成功
異步線程:ThreadPoolTaskExecutor-1執行成功
異步線程:ThreadPoolTaskExecutor-3執行成功
異步線程:ThreadPoolTaskExecutor-1執行成功
異步線程:ThreadPoolTaskExecutor-3執行成功
異步線程:ThreadPoolTaskExecutor-2執行成功
異步線程:ThreadPoolTaskExecutor-1執行成功
異步線程:ThreadPoolTaskExecutor-3執行成功
@Async和@Controller同時使用存在異常
AbstractHandlerMethodMapping初始bean時,在afterPropertiesSet方法中initHandlerMethods()關聯我們的SpringMVCBean
// 類型判斷
if (beanType != null && isHandler(beanType)) {
// url處理
detectHandlerMethods(beanName);
}
// Controller和RequestMapping注解判斷
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
a、@Controller不使用@Async注解時
@RestController
@Slf4j
public class MemberServiceImpl1 {
@GetMapping("/addUser1")
public String addUser() {
log.info(">>>流程1");
log.info(">>>流程2");
return "success";
}
}
b、@Controller使用@Async注解,但不繼承接口時,異步失效
@RestController
@Slf4j
public class MemberServiceImpl3 {
@GetMapping("/addUser3")
public String addUser() {
log.info(">>>流程1");
addUserLog();
log.info(">>>流程3");
return "success";
}
@Async()
public String addUserLog() {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
log.info(">>>流程2");
return "success";
}
}
c、@Controller使用@Async注解,同時繼承接口
@RestController
@Slf4j
public class MemberServiceImpl4 implements MemberService {
@Override
@GetMapping("/addUser4")
public String addUser() {
log.info(">>>流程1");
addUserLog();
log.info(">>>流程3");
return "success";
}
@Async()
public String addUserLog() {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
log.info(">>>流程2");
return "success";
}
}
@Async使用建議
- a、異步執行的建議單獨開啟一個類實現,或者從容器中直接獲取到該代理類后執行
/**
* 異步代碼寫到別的地方,不要和Controller注解同時使用
*/
@RestController
@Slf4j
public class ResolveServiceImpl {
@Autowired
private MemberServiceManage memberServiceManage;
@GetMapping("/resolve2")
public String addUser() {
log.info(">>>流程1");
memberServiceManage.addUserLog();
log.info(">>>流程3");
return "success";
}
}
@Component
@Slf4j
public class MemberServiceManage {
@Async
public String addUserLog() {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
log.info(">>>流程2");
return "success";
}
}
/**
* 使用SpringUtils獲得bean后調用,不要使用this操作
*/
@RestController
@Slf4j
public class ResolveServiceImpl {
@GetMapping("/resolve1")
public String addUser() {
log.info(">>>流程1");
//把直接調用改為從容器中取一次
ResolveServiceImpl bean = SpringUtils.getBean(ResolveServiceImpl.class);
bean.addUserLog();
log.info(">>>流程3");
return "success";
}
@Async()
public String addUserLog() {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
log.info(">>>流程2");
return "success";
}
}
- b、異步的方法上不要加static,加了static就不走AOP了