CompletableFuture
1、supplyAsync、runAsync
在 Java 的 CompletableFuture
类中,supplyAsync
和 runAsync
是两个用于异步执行任务的方法。它们的主要区别在于处理返回值的方式和适用场景。下面详细介绍这两个方法。
1. supplyAsync
- 功能:
supplyAsync
方法用于异步执行一个可以返回结果的任务。 - 返回值: 它返回一个
CompletableFuture<T>
,其中T
是通过任务计算得到的结果。
使用场景
适用于需要计算并返回结果的任务。
示例代码
import java.util.concurrent.CompletableFuture; public class SupplyAsyncExample { public static void main(String[] args) { CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { // 模拟一些计算 try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return 42; // 返回计算结果 }); // 等待结果并打印 future.thenAccept(result -> { System.out.println("计算结果: " + result); }); // 主线程可以继续执行其他任务 System.out.println("主线程继续执行..."); // 等待 CompletableFuture 完成 future.join(); // 这里会阻塞直到 future 完成 } }
2. runAsync
- 功能:
runAsync
方法用于异步执行一个不需要返回结果的任务。 - 返回值: 它返回一个
CompletableFuture<Void>
,表示任务的完成状态。
使用场景
适用于只需执行某些操作而不需要计算返回值的任务,例如日志记录、发送通知等。
示例代码
import java.util.concurrent.CompletableFuture; public class RunAsyncExample { public static void main(String[] args) { CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { // 模拟一些操作 try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("任务完成!"); }); // 主线程可以继续执行其他任务 System.out.println("主线程继续执行..."); // 等待 CompletableFuture 完成 future.join(); // 这里会阻塞直到 future 完成 } }
主要区别
特性 | supplyAsync | runAsync |
---|---|---|
返回值 | 返回计算结果的 CompletableFuture<T> | 返回 CompletableFuture<Void> |
用途 | 适用于需要结果的计算任务 | 适用于不需要结果的执行操作 |
示例 | 计算和返回一个值 | 仅执行某些操作(如打印、更新状态) |
总结
- 使用
supplyAsync
当你需要执行一个计算并希望得到结果时。 - 使用
runAsync
当你只想执行一个操作而不需要返回值时。
这两个方法都可以使你的程序在多线程环境中更加高效和响应迅速,特别适合处理 I/O 密集型或计算密集型任务。通过合理使用这两个方法,可以显著提高应用程序的性能和可扩展性。
2、allOf、anyOf
allOf
CompletableFuture.allOf
是 Java 中 CompletableFuture
类的一个静态方法,它允许你组合多个 CompletableFuture
实例,并在所有这些实例都完成时进行某种操作。allOf
返回一个新的 CompletableFuture<Void>
,一旦所有传入的 CompletableFuture
都完成(无论是成功还是失败),这个新的 CompletableFuture
就会完成。
anyOf
CompletableFuture.anyOf
方法用于组合多个 CompletableFuture
实例,直到其中一个完成(成功或失败)。它返回一个新的 CompletableFuture
,一旦任一给定的 CompletableFuture
完成,这个新的 CompletableFuture
就会完成,并且可以处理已完成的结果。
3、get
在 Java 的 CompletableFuture
中,get
方法用于获取 CompletableFuture
的计算结果。如果 CompletableFuture
还没有完成,调用 get
方法会导致当前线程阻塞,直到计算完成或者失败。此外,get
方法还有一个重载版本,允许你在获取结果时指定超时时间。
使用 get
方法
- 基本用法
get()
:阻塞当前线程,直到计算完成并返回结果。get(long timeout, TimeUnit unit)
:阻塞当前线程,直到计算完成、超时或者发生异常。
示例代码
下面是一个使用 CompletableFuture.allOf
和 get
方法的示例,展示如何处理多个异步任务,并在超时情况下进行异常处理。
import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class AllOfWithTimeoutExample { public static void main(String[] args) { // 创建多个异步任务 CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> { sleep(2000); // 模拟长时间运行的任务 return "Result from Future 1"; }); CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> { sleep(1000); // 模拟较短时间的任务 return "Result from Future 2"; }); CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> { sleep(1500); // 模拟中等时间的任务 return "Result from Future 3"; }); // 使用 allOf 等待所有任务完成 CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(future1, future2, future3); try { // 等待所有任务完成,设置超时时间为 3 秒 allOfFuture.get(3, TimeUnit.SECONDS); // 如果超时,将抛出 TimeoutException // 获取每个 future 的结果 String result1 = future1.get(); String result2 = future2.get(); String result3 = future3.get(); // 输出结果 System.out.println("所有任务完成:"); System.out.println(result1); System.out.println(result2); System.out.println(result3); } catch (TimeoutException e) { System.out.println("操作超时,未能在规定时间内完成。"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 恢复中断状态 System.out.println("任务被中断。"); } catch (ExecutionException e) { System.out.println("任务执行异常: " + e.getCause()); } } private static void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 恢复中断状态 } } }
4、thenApply、thenAccept、thenCompose
在 Java 的 CompletableFuture
类中,thenApply
、thenAccept
和 thenCompose
是用于处理异步计算结果的三个重要方法。它们允许你在 CompletableFuture
完成时定义后续操作,但它们的使用场景和返回类型各不相同。下面详细介绍这三个方法。
1. thenApply
- 功能:
thenApply
方法用于对CompletableFuture
的结果进行转换,返回一个新的结果。 - 返回值: 返回一个新的
CompletableFuture<U>
,其中U
是通过给定的函数处理后的结果。
使用场景
适用于需要对异步计算结果进行进一步处理并返回新结果的场景。
示例代码
import java.util.concurrent.CompletableFuture; public class ThenApplyExample { public static void main(String[] args) { CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { return 42; // 原始计算结果 }); CompletableFuture<String> transformedFuture = future.thenApply(result -> { return "结果是: " + result; // 转换结果 }); // 打印转换后的结果 transformedFuture.thenAccept(System.out::println); } }
2. thenAccept
- 功能:
thenAccept
方法用于处理CompletableFuture
的结果,但不返回任何结果。 - 返回值: 返回一个新的
CompletableFuture<Void>
,表示处理完成。
使用场景
适用于只关心异步计算结果但不需要返回新结果的场景。
示例代码
import java.util.concurrent.CompletableFuture; public class ThenAcceptExample { public static void main(String[] args) { CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { return 42; // 原始计算结果 }); future.thenAccept(result -> { System.out.println("结果是: " + result); // 处理结果 }); } }
3. thenCompose
- 功能:
thenCompose
方法用于将一个CompletableFuture
的结果转换为另一个CompletableFuture
。它通常用于处理依赖于前一个CompletableFuture
结果的异步操作。 - 返回值: 返回一个新的
CompletableFuture<U>
,其中U
是通过给定的函数处理后的结果。
使用场景
适用于当后续操作返回 CompletableFuture
时,需要将这些操作串联在一起的场景。
示例代码
import java.util.concurrent.CompletableFuture; public class ThenComposeExample { public static void main(String[] args) { CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { return 42; // 原始计算结果 }); CompletableFuture<String> finalFuture = future.thenCompose(result -> { return CompletableFuture.supplyAsync(() -> "结果是: " + result // 依赖于前一个结果的异步操作 ); }); // 打印最终结果 finalFuture.thenAccept(System.out::println); } }
主要区别
方法 | 功能 | 返回值类型 | 使用场景 |
---|---|---|---|
thenApply | 转换结果并返回新结果 | CompletableFuture<U> | 需要对结果进行处理并返回新结果的场景 |
thenAccept | 处理结果但不返回任何结果 | CompletableFuture<Void> | 只关心计算结果但不需要返回新结果的场景 |
thenCompose | 将一个异步操作的结果与另一个异步操作链接在一起 | CompletableFuture<U> | 当后续操作需要依赖于前一个操作的结果并返回 CompletableFuture 的场景 |
总结
- 使用
thenApply
当你需要对异步计算的结果进行转换并返回新结果时。 - 使用
thenAccept
当你只想处理结果而不需要返回值时。 - 使用
thenCompose
当你需要将一个CompletableFuture
的结果传递给另一个异步操作时。 - thenApply 是用于将前一个任务的结果转换为另一个结果,适合用于处理结果,不启动新的异步计算。
- thenCompose 是用于将前一个任务的结果传递给一个返回新的 CompletableFuture 的函数,适合用于在前一个任务完成后,启动新的异步计算。
这三个方法提供了丰富的组合能力,使得使用 CompletableFuture
进行异步编程变得更加简单和灵活。通过合理使用这些方法,可以构建复杂的异步处理流程。
5、ForkJoinPool
ForkJoinPool
是 Java 中用于并行处理任务的一个线程池,特别适合处理可拆分的任务(Fork-Join 任务)。它是 Java 并发包的一部分,提供了一个高效的任务执行框架,能够充分利用多核处理器的优势。ForkJoinPool
使用了工作窃取算法(Work Stealing),使得空闲的工作线程可以窃取任务,从而提高系统的整体性能。
主要特点
可拆分任务:
ForkJoinPool
特别适合处理可以被拆分成多个子任务的任务。它将大任务拆分成小任务,这些小任务可以并行执行。工作窃取算法: 在工作窃取模型中,每个线程维护一个双端队列(Deque),当任务执行完成后,线程会尝试从自己的队列中获取新的任务。如果队列为空,线程会尝试从其他线程的队列中窃取任务。
简单的 API:
ForkJoinPool
提供了简单的 API,可以通过ForkJoinTask
类及其子类(如RecursiveTask
和RecursiveAction
)来创建可拆分的任务。
总结
ForkJoinPool
是 Java 并发编程中的一个强大工具,适用于需要并行处理的可拆分任务。它通过工作窃取算法提高了多核处理器的利用率,并提供了简单的 API,方便开发者实现任务的分治和并行执行。通过使用 ForkJoinPool
,可以显著提高计算密集型任务的性能。
6、总结
CompletableFuture
是 Java 8 引入的一个强大的工具,用于支持异步编程和并行计算。它提供了一种简洁、灵活的方式来处理异步任务,支持对结果的组合、转换和异常处理。以下是 CompletableFuture
的一些重要特点和使用概述。
主要特点
异步计算:
CompletableFuture
允许你在后台线程中异步执行任务,避免了阻塞主线程,从而提高应用程序的响应性。
结果组合:
- 你可以将多个
CompletableFuture
组合在一起,通过thenCombine
、allOf
、anyOf
等方法来处理多个异步操作的结果。
- 你可以将多个
链式调用:
CompletableFuture
支持链式调用,使得你可以将多个异步操作串联在一起,形成清晰的处理流程。常用的方法包括thenApply
、thenAccept
、thenCompose
等。
异常处理:
- 提供了灵活的异常处理机制,可以使用
exceptionally
、handle
和whenComplete
方法来处理任务执行中的异常。
- 提供了灵活的异常处理机制,可以使用
支持回调:
- 可以注册回调函数,在任务完成时被调用,无论是正常完成还是异常完成。
工作窃取:
- 基于
ForkJoinPool
,CompletableFuture
可以充分利用多核处理器,提高并发执行的效率。
- 基于
常用方法
创建任务:
supplyAsync(Supplier<U> supplier)
:异步执行并返回结果。runAsync(Runnable runnable)
:异步执行不返回结果的任务。
处理结果:
thenApply(Function<? super T,? extends U> fn)
:对结果进行转换,返回新的结果。thenAccept(Consumer<? super T> action)
:处理结果但不返回。thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
:将结果转换为另一个CompletableFuture
。
组合多个任务:
allOf(CompletableFuture<?>... cfs)
:等待所有CompletableFuture
完成。anyOf(CompletableFuture<?>... cfs)
:等待任意一个CompletableFuture
完成。
异常处理:
exceptionally(Function<Throwable, ? extends T> fn)
:处理异常并提供默认值。handle(BiFunction<? super T,Throwable,? extends U> fn)
:处理结果和异常。
示例代码
以下是一个简单的示例,展示了如何使用 CompletableFuture
来执行异步计算并处理结果:
import java.util.concurrent.CompletableFuture; public class CompletableFutureExample { public static void main(String[] args) { CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { return 42; // 异步计算 }); future.thenApply(result -> { return "结果是: " + result; // 转换结果 }).thenAccept(System.out::println) // 打印结果 .exceptionally(ex -> { System.out.println("发生异常: " + ex.getMessage()); return null; }); } }
总结
CompletableFuture
提供了一种强大而灵活的方式来处理异步编程,能够轻松地实现并发操作、结果组合和异常处理。它使得 Java 开发者能够更方便地编写非阻塞代码,提高应用程序的性能和响应性。在现代 Java 应用中,CompletableFuture
是处理异步操作的重要工具。通过合理使用 CompletableFuture
,你可以构建复杂的异步处理流程,并提高代码的可读性和可维护性。