非同期呼び出しを有効にするには、@EnableAsync アノテーションを SpringBoot アプリケーションに追加する必要があります。通常、スレッド プールが構成され、非同期メソッドは特定のスレッド プールによって完了されます。
@Configuration @EnableAsync public class AsyncConfiguration { @Bean("doSomethingExecutor") public Executor doSomethingExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 核心线程数:线程池创建时候初始化的线程数 executor.setCorePoolSize(10); // 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程 executor.setMaxPoolSize(20); // 缓冲队列:用来缓冲执行任务的队列 executor.setQueueCapacity(500); // 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁 executor.setKeepAliveSeconds(60); // 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池 executor.setThreadNamePrefix("do-something-"); // 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程) executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); executor.initialize(); return executor; } }
使用方法は非常に簡単で、非同期が必要なメソッドに @Async アノテーションを追加します。
@RestController public class AsyncController { @Autowired private AsyncService asyncService; @GetMapping("/open/something") public String something() { int count = 10; for (int i = 0; i < count; i++) { asyncService.doSomething("index = " + i); } return "success"; } } @Slf4j @Service public class AsyncService { // 指定使用beanname为doSomethingExecutor的线程池 @Async("doSomethingExecutor") public String doSomething(String message) { log.info("do something, message={}", message); try { Thread.sleep(1000); } catch (InterruptedException e) { log.error("do something error: ", e); } return message; } }
Access: 127.0.0.1:8080/open/something、ログは次のとおりです。
2023 -02-06 23:42:42.486 INFO 21168 --- [io-8200-exec-17] x.g.b.system.controller.AsyncController : 何かを終了します、時間 8 ミリ秒
2023-02-06 23:42:42.488 INFO 21168 --- [ do-something-1] x.gits.boot.system.service.AsyncService : 何かをしてください、message=index = 0
2023-02 -06 23:42:42.488 情報 21168 --- [ do-something-5] x.gits.boot.system.service.AsyncService : 何かをしてください、message=index = 4
2023-02-06 23:42 :42.488 情報 21168 --- [ do-something- 4] x.gits.boot.system.service.AsyncService : 何かをしてください、message=index = 3
2023-02-06 23:42:42.488 INFO 21168 - -- [ do-something-6] x.gits .boot.system.service.AsyncService : 何かを実行します、message=index = 5
2023-02-06 23:42:42.488 INFO 21168 --- [ do- something-9] x.gits.boot.system.service.AsyncService : 何かをします、message=index = 8
2023-02-06 23:42:42.488 INFO 21168 --- [ do-something-8] x .gits.boot.system.service.AsyncService : 何かをしてください、message=index = 7
2023-02-06 23:42:42.488 INFO 21168 --- [do-something-10] x.gits.boot。 system.service.AsyncService : 何かをします、message=index = 9
2023-02-06 23:42:42.488 INFO 21168 --- [ do-something-7] x.gits.boot.system.service.AsyncService : 何かをしてください、message=index = 6
2023-02-06 23:42:42.488 INFO 21168 --- [ do-something-2] x.gits.boot.system.service.AsyncService : 何かをしてください、メッセージ=index = 1
2023-02-06 23:42:42.488 INFO 21168 --- [ do-something-3] x.gits.boot.system.service.AsyncService : 何かをしてください、message=index = 2
非同期実行の効果が得られ、設定したスレッドプールが使用されていることがわかります。
非同期メソッドに戻り値がある場合、非同期メソッドの実行結果の戻り値を取得するにはどうすればよいでしょうか?このとき、非同期で呼び出す必要があるメソッドの戻り値は CompletableFuture です。
CompletableFuture は、Feature の拡張機能です。Feature は単純な非同期タスクのみを処理できますが、CompletableFuture は複数の非同期タスクの複雑な組み合わせを実行できます。次のように:
@RestController public class AsyncController { @Autowired private AsyncService asyncService; @SneakyThrows @ApiOperation("异步 有返回值") @GetMapping("/open/somethings") public String somethings() { CompletableFuture<String> createOrder = asyncService.doSomething1("create order"); CompletableFuture<String> reduceAccount = asyncService.doSomething2("reduce account"); CompletableFuture<String> saveLog = asyncService.doSomething3("save log"); // 等待所有任务都执行完 CompletableFuture.allOf(createOrder, reduceAccount, saveLog).join(); // 获取每个任务的返回结果 String result = createOrder.get() + reduceAccount.get() + saveLog.get(); return result; } } @Slf4j @Service public class AsyncService { @Async("doSomethingExecutor") public CompletableFuture<String> doSomething1(String message) throws InterruptedException { log.info("do something1: {}", message); Thread.sleep(1000); return CompletableFuture.completedFuture("do something1: " + message); } @Async("doSomethingExecutor") public CompletableFuture<String> doSomething2(String message) throws InterruptedException { log.info("do something2: {}", message); Thread.sleep(1000); return CompletableFuture.completedFuture("; do something2: " + message); } @Async("doSomethingExecutor") public CompletableFuture<String> doSomething3(String message) throws InterruptedException { log.info("do something3: {}", message); Thread.sleep(1000); return CompletableFuture.completedFuture("; do something3: " + message); } }
アクセス インターフェイス
C:\Users\Administrator>curl -X GET "http://localhost:8080/open/something" -H "accept: * /*"
何かをする 1: 注文を作成;
何かをする 2: アカウントを減らす;
何かをする 3: ログを保存
コンソール上の主なログは次のとおりです:
2023-02-06 00:27:42.238 情報 5672 --- [ do-something-3] x.gits.boot.system.service.AsyncService : do something3: ログを保存
2023-02- 06 00:27:42.238 INFO 5672 --- [ do-something-2] x.gits.boot.system.service.AsyncService : do something2: アカウントを減らす
2023-02-06 00:27 :42.238 INFO 5672 --- [ do-something-1] x.gits.boot.system.service.AsyncService : do something1: create order
@Async アノテーションいくつかのシナリオは失敗しました。これは、@Async アノテーションが明らかに使用されましたが、マルチスレッドが使用されていなかったことを意味します。
非同期メソッドは static キーワードで変更されます;
非同期クラスは Spring コンテナの Bean ではありません (通常はアノテーション @ を使用します)コンポーネントと @Service であり、Spring によってスキャンできます);
@EnableAsync アノテーションは SpringBoot アプリケーションに追加されません;
In同じクラス、メソッド呼び出し @Async アノテーションを持つ別のメソッドの場合、アノテーションは有効になりません。その理由は、@Async アノテーションが付けられたメソッドがプロキシ クラスで実行されるためです。
アノテーション @Async を使用した非同期メソッドの戻り値は、void または Future とそのサブクラスのみであることに注意してください。戻り結果が他の型の場合、メソッドは引き続き非同期で実行されますが、戻り値はすべて null です。ソース コードの一部は次のとおりです:
AsyncExecutionInterceptor#invoke
上記の例を通して、@Async は実際には Future または CompletableFuture を渡します。非同期実行の場合、Spring はそれをカプセル化し、使いやすくしています。
以上がSpringBoot でマルチスレッドをエレガントに使用する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。