SpringBoot中的異步調用

一、基礎實現

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是一個計數器,線程完成一個記錄一個,計數器遞減

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章