一、基礎實現
1. 開啓異步調用支持
- 在啓動類上添加
@EnableAsync
註解 @EnableAsync
可以配置在啓動類(程序入口)或者配置類上,這裏我配置在了啓動類上
@SpringBootApplication
@EnableAsync // 開啓異步調用
public class XxxApplication {
public static void main(String[] args) {
SpringApplication.run(XxxApplication .class, args);
}
}
2. 聲明異步方法
注意:
- 在需要異步的方法上添加
@Async
註解; @Async
註解可以配置在類或者方法上,若配置在類上則說明這個類下的所有方法都將支持異步調用- 異步方法所屬的類應該是spring管理的類;
@Service
public class TestServiceImpl implements ITestService {
@Override
@Async // 聲明異步方法
public void testAsynTask(int i) {
LoggerFactory.getLogger(TestServiceImpl.class).info(">>>>>>>>> " + i);
}
}
3.調用異步方法
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private ITestService testService;
@ApiOperation(value = "testAsynTask", notes = "測試springBoot異步調用")
@GetMapping("/testAsynTask")
public String testAsynTask(){
for (int i = 0; i < 10; i++) {
testService.testAsynTask(i);
}
return "success";
}
}
打印結果:
二、進階
有時候,根據需求需要制定一些多線程的策略(配置);或者需要知道線程何時結束、返回值如何獲取…
1. 配置
@Configuration // 定義一個配置類
@EnableAsync // 開啓異步調用
public class BeanConfig {
@Bean
public TaskExecutor taskExecutor() {
// ThreadPoolTaskExecutor 這個類是sring爲我們提供的線程池類
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 設置核心線程數
executor.setCorePoolSize(3);
// 設置最大線程數
executor.setMaxPoolSize(5);
// 設置隊列容量
executor.setQueueCapacity(10);
// 設置線程活躍時間(秒)
executor.setKeepAliveSeconds(60);
// 設置默認線程名稱
executor.setThreadNamePrefix("thread_by_boo-");
// 設置拒絕策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任務結束後再關閉線程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
2. 接收線程的返回值
有時候我們不止希望異步執行任務,還希望任務執行完成後會有一個返回值,在java中提供了Future泛型接口,用來接收任務執行結果,springboot也提供了此類支持,使用實現了ListenableFuture接口的類如AsyncResult來作爲返回值的載體。比如上例中,我們希望返回一個類型爲String類型的值,可以將返回值改造爲:
@Async
public Future<String> testAsynTask(int i) {
log.info(">>>>>>>>> " + i);
return new AsyncResult<>("我是" + i + "wa");
}
調用:
for (int i = 0; i < 10; i++) {
// Future 的 get方法可以獲得返回值
// 阻塞調用 相當於加了個鎖,上一個線程不結束 下一個線程進不去
// testService.testAsynTask(i).get();
// 限時調用 超過規定的調用時間 會報超時異常
// testService.testAsynTask(i).get(1, TimeUnit.SECONDS);
Future<String> res = testService.testAsynTask(i);
}
- 監聽單個線程結束
while(true){
if (res.isDone()){
System.out.println("線程結束....");
break;
}
}
- 監聽多個線程結束(一)
public String testAsynTask() throws Exception {
List<Future> futureList = Lists.newArrayList(); // 聲明一個線程容器
for (int i = 0; i < 10; i++) {
Future<String> res = testService.testAsynTask(i);
futureList.add(res); // 每得到一個線程就往容器中添加一個線程
}
// 監聽線程是否全部結束
while (true){
if (futureList.size() < 1) { // 當線程容器中沒有任何線程的時候 說明線程已經全部結束
System.out.println("線程已經全部結束...");
break;
}
for (int i = 0; i < futureList.size(); i++) {
Future<String> future = futureList.get(i);
if (future.isDone()){ // 線程結束 則將其從線程容器中移除
System.out.println(future.get() + " >> 線程結束");
futureList.remove(i);
break;
}
}
}
return "success";
}
打印結果:
監聽多個線程結束(二)
使用CountDownLatch
工具類
countDownLatch
是一個計數器,線程完成一個記錄一個,計數器遞減