Java中CompletableFuture中4种异步编程方法

💡 原文中文,约5200字,阅读约需13分钟。
📝

内容提要

Java的CompletableFuture框架提供了4种异步编程方法:runAsync()和SupplyAsync()用于启动异步任务,runAsync()适用于不产生结果的任务,supplyAsync()适用于产生结果的任务。thenApply()和thenApplyAsync()用于对结果进行转换,thenApply()在同一线程执行转换函数,thenApplyAsync()在异步执行。runAsync()和thenRun()用于链接任务,supplyAsync()允许链接操作。CompletableFuture框架提供了强大的异步编程能力。

🔎

延伸解读

选择runAsync还是supplyAsync:关注任务是否需要结果

runAsync用于不产生结果的任务,返回CompletableFuture<Void>,适合即发即忘的场景,如记录日志或发送通知。supplyAsync用于产生结果的任务,返回CompletableFuture<T>,适合需要结果进行进一步处理的场景,如从数据库获取数据。两者都启动异步任务,但关键区别在于返回值类型和后续链式操作的能力。

thenApply与thenApplyAsync:执行线程的差异影响性能

thenApply默认使用完成当前CompletableFuture的同一线程执行转换函数,如果转换函数耗时或阻塞,可能阻塞该线程。thenApplyAsync则保证在异步线程(通常来自ForkJoinPool.commonPool())中执行转换函数,避免阻塞当前线程。因此,轻量级顺序转换适合thenApply,而阻塞操作或计算密集型任务适合thenApplyAsync。

链式操作:runAsync与supplyAsync的不同链接方式

runAsync不产生结果,不能直接与thenApply或thenAccept链接,但可以使用thenRun在任务完成后执行另一个任务,实现顺序链接。supplyAsync产生结果,允许使用thenApply转换结果、thenAccept消费结果或thenCompose链接进一步的异步操作,从而构建复杂的异步工作流程。

❓

Q&A

CompletableFuture中runAsync()和supplyAsync()的主要区别是什么?

runAsync()用于执行不产生结果的任务,接受Runnable,返回CompletableFuture<Void>;supplyAsync()用于执行产生结果的任务,接受Supplier,返回CompletableFuture<T>。此外,runAsync()立即在公共线程池中启动任务,而supplyAsync()可能会延迟执行以进行资源管理。

thenApply()和thenApplyAsync()在CompletableFuture中有什么不同?

thenApply()默认使用完成当前CompletableFuture的同一线程执行转换函数,如果转换函数耗时可能阻塞线程;thenApplyAsync()则使用执行器池(通常是ForkJoinPool.commonPool())中的线程异步执行转换函数,避免阻塞当前线程。

runAsync()方法能否与thenApply()或thenAccept()链接?如果不能,有什么替代方案?

runAsync()不能直接与thenApply()或thenAccept()链接,因为它不产生结果。但可以使用thenRun()在runAsync()任务完成后执行另一个任务,实现顺序链接。

supplyAsync()方法如何支持链式操作?请举例说明。

supplyAsync()由于产生结果,可以使用thenApply()转换结果、thenAccept()消费结果或thenCompose()链接进一步的异步操作。例如:CompletableFuture.supplyAsync(() -> "Result").thenApply(String::toUpperCase).thenAccept(System.out::println);

在什么场景下应该使用thenApply()而不是thenApplyAsync()?

thenApply()适合顺序转换和轻量级操作,例如将数字结果转换为字符串或执行快速计算,这些操作不会明显阻塞调用线程。

在什么场景下应该使用thenApplyAsync()而不是thenApply()?

thenApplyAsync()适合异步转换和阻塞操作,例如涉及I/O操作、计算密集型任务或需要并行执行多个转换时,可以避免阻塞调用线程,提高性能。

🏷️

标签

➡️

继续阅读