原來你是這樣的SpringBoot--Async異步任務

本節我們一起學習一下SpringBoot中的異步調用,主要用於優化耗時較長的操作,提高系統性能和吞吐量。

一、新建項目,啓動異步調用

首先給啓動類增加註解@EnableAsync,支持異步調用

@EnableAsync
@SpringBootApplication
public class CathySpringbootDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(CathySpringbootDemoApplication.class, args);
    }

}

然後定義要執行的Task,分類增加一個同步方法和異步方法,其中異步方法需要增加註解@Async

@Component
public class AsyncTask {
    /**
     * 異步任務,需要註解@Async
     *
     * @param taskId 任務編號id
     * @param second 執行時長,模擬慢任務
     * @return
     */
    @Async
    public Future<Boolean> asyncExec(int taskId, Long second) {
        exec(taskId, second);
        return new AsyncResult<>(Boolean.TRUE);
    }

    public void exec(int taskId, Long second) {
        System.out.println("開始執行任務" + taskId);
        try {
            Thread.sleep(second * 1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("結束執行任務" + taskId);
    }
}

其實接下來就可以在controller中創建接口來進行簡單的測試了

@RestController
@RequestMapping("/async")
public class AsyncController {
    @Autowired
    AsyncTask asyncTask;

    @GetMapping("sync_task")
    public String syncTask() {
        long start = System.currentTimeMillis();
        asyncTask.exec(1, 3L);
        asyncTask.exec(2, 3L);
        asyncTask.exec(3, 3L);
        long time = System.currentTimeMillis() - start;
        return "同步執行,耗時" + time;
    }

    @GetMapping("async_task")
    public String asyncTask() {
        long start = System.currentTimeMillis();
        Future<Boolean> f1 = asyncTask.asyncExec(1, 3L);
        Future<Boolean> f2 = asyncTask.asyncExec(2, 3L);
        Future<Boolean> f3 = asyncTask.asyncExec(3, 3L);
        try {
            f1.get();
            f2.get();
            f3.get();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

        long time = System.currentTimeMillis() - start;
        return "異步執行,耗時" + time;
    }
}

啓動程序,查看接口響應結果:
http://localhost:16001/async/sync_task
img

http://localhost:16001/async/async_task
img

注意:異步方法和調用一定要寫在不同的類中

二、線程池配置

上面的例子,在耗時服務多的情況下,使用異步方法確實提高了響應速度。但是它默認啓用的是Spring默認的線程池SimpleAsyncTaskExecutor,不太靈活。我們把異步請求多增加幾次調用看看效果:

@GetMapping("async_task")
    public String asyncTask() {
        long start = System.currentTimeMillis();
        List<Future<Boolean>> list = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            Future<Boolean> fi = asyncTask.asyncExec(i, 10L);
            list.add(fi);
        }
        for (int i = 0; i < 20; i++) {

            list.forEach(x -> {
                try {
                    x.get();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } catch (ExecutionException e) {
                    throw new RuntimeException(e);
                }
            });
        }

        long time = System.currentTimeMillis() - start;
        return "異步執行,耗時" + time;
    }

img
從上面的運行效果來看,一旦超過8個並行執行的任務,就開始出現等待了。

接下來,我們自定義線程池

@Bean
 public TaskExecutor threadPoolTaskExecutor(){
     ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
     executor.setCorePoolSize(8);
     executor.setMaxPoolSize(16);
     executor.setQueueCapacity(20);
     executor.setKeepAliveSeconds(30);
     executor.setWaitForTasksToCompleteOnShutdown(true);
     executor.setThreadNamePrefix("task-thread-");
     executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());

     executor.initialize();
     return executor;
 }

然後在異步方法的註解中,明確指定所使用的線程池

@Async("threadPoolTaskExecutor")
    public Future<Boolean> asyncExec(int taskId, Long second) {
        exec(taskId, second);
        return new AsyncResult<>(Boolean.TRUE);
    }

執行效果如下:

img

img

可以看出,線程池設置的參數已經生效。


本人公衆號[ 敬YES ]同步更新,歡迎大家關注~

img

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