在Java的世界里,CompletableFuture
是异步编程的利器,它为开发者提供了强大的异步处理能力。今天,就让我们一起揭开CompletableFuture
的神秘面纱,深入了解它的使用方法、运行原理、应用场景以及源码分析。
2024最全大厂面试题无需C币点我下载或者在网页打开全套面试题已打包
AI绘画关于SD,MJ,GPT,SDXL,Comfyui百科全书
CompletableFuture的使用详解
CompletableFuture
是Java 8引入的java.util.concurrent
包下的一个类,它实现了Future
接口和CompletionStage
接口,提供了异步计算的能力。
创建CompletableFuture
import java.util.concurrent.CompletableFuture;
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 异步计算逻辑
return "Hello, CompletableFuture!";
});
异步执行
future.thenAccept(System.out::println);
结果处理
future.thenApply(s -> s.toUpperCase())
.thenAccept(System.out::println);
异常处理
future.exceptionally(ex -> {
// 异常处理逻辑
return "Error: " + ex.getMessage();
});
串行化执行
future.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World"))
.thenAccept(System.out::println);
并行执行
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(future1, future2)
.thenRun(() -> System.out.println("All futures are done."));
选择性执行
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<String> result = CompletableFuture.anyOf(future1, future2)
.thenApply(s -> s + "!");
CompletableFuture的实际项目使用
在实际项目中,CompletableFuture
可以用于处理复杂的异步任务流,例如:
- Web服务:在处理HTTP请求时,可以使用
CompletableFuture
来异步处理业务逻辑,提高响应速度。 - 数据处理:在处理大数据时,可以使用
CompletableFuture
来并行处理数据,加快处理速度。 - 任务调度:在需要定时执行任务时,可以使用
CompletableFuture
来实现延迟执行或周期性执行。
CompletableFuture的运行原理
CompletableFuture
内部使用了ForkJoinPool
来执行异步任务。ForkJoinPool
是一个工作窃取(work-stealing)的线程池,它能够有效地利用多核处理器的计算能力。
源码分析
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
// 内部状态变量
volatile Object result; // Either the result or boxed AltResult
volatile WaitNode waiters; // Treiber stack for waiting threads
// 构造函数
public CompletableFuture() {
result = new AltResult(null);
}
// 异步执行方法
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
// 串行化执行方法
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) {
return uniApplyStage(null, fn);
}
// 异常处理方法
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn) {
return uniExceptionally(fn);
}
// 并行执行方法
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
return andTree(cfs, 0, cfs.length - 1);
}
// 选择性执行方法
public static <U> CompletableFuture<U> anyOf(CompletableFuture<U>... cfs) {
return orTree(cfs, 0, cfs.length - 1);
}
// 其他方法...
}
在上述代码中,CompletableFuture
类的构造函数初始化了一个内部状态变量result
,用于存储计算结果或异常信息。supplyAsync
方法用于异步执行任务,thenApply
方法用于对结果进行转换,exceptionally
方法用于处理异常,allOf
和anyOf
方法用于组合多个CompletableFuture
。
CompletableFuture
在高并发场景下的表现通常是非常出色的,因为它利用了Java 8引入的ForkJoinPool
,这是一个工作窃取(work-stealing)的线程池,能够有效地利用多核处理器的计算能力。CompletableFuture
通过异步执行和任务组合,可以显著提高程序的吞吐量和响应速度。
高并发场景下的表现特点:
-
异步执行:
CompletableFuture
允许开发者以异步的方式执行任务,这意味着任务可以在不同的线程上并行执行,从而减少了等待时间,提高了程序的响应速度。 -
任务组合:
CompletableFuture
提供了多种方法来组合多个异步任务,如thenApply
、thenCompose
、thenCombine
等。这些组合操作可以在任务完成时自动触发,减少了手动管理线程的复杂性。 -
线程池管理:
CompletableFuture
默认使用ForkJoinPool.commonPool()
,这是一个共享的线程池,它会根据CPU核心数自动调整线程数量。在高并发场景下,ForkJoinPool
能够有效地管理线程,避免线程过多导致的上下文切换开销。 -
无阻塞设计:
CompletableFuture
的设计是无阻塞的,这意味着它不会因为等待其他任务的完成而阻塞当前线程。相反,它会注册回调,当任务完成时,回调会被执行。 -
异常处理:
CompletableFuture
提供了强大的异常处理机制,可以方便地处理异步任务中可能出现的异常,避免异常导致的程序崩溃。 -
性能优化:在高并发场景下,
CompletableFuture
可以利用ForkJoinPool
的并行执行能力,减少任务执行的总时间,提高程序的整体性能。
注意事项:
-
资源限制:尽管
CompletableFuture
在高并发场景下表现良好,但仍然受到系统资源的限制。如果并发任务过多,可能会导致内存不足或线程过多导致的性能下降。 -
任务依赖:在使用
CompletableFuture
组合任务时,需要注意任务之间的依赖关系。不恰当的任务依赖可能会导致死锁或性能瓶颈。 -
异常处理:在高并发场景下,异常处理尤为重要。确保所有的异常都被适当地捕获和处理,以避免程序崩溃。
-
测试:在高并发场景下,应该对
CompletableFuture
的使用进行充分的测试,以确保在高负载下的稳定性和性能。
通过上述特点和注意事项,我们可以看出CompletableFuture
在高并发场景下是一个非常有用的工具。它能够帮助开发者构建出高性能、高吞吐量的并发应用程序。然而,为了确保最佳性能,开发者需要合理设计任务的并发执行,并进行充分的测试和优化。
CompletableFuture
类提供了许多方法来处理异步计算的结果,包括执行异步任务、组合任务、处理异常和完成回调等。以下是一些常用方法的示例代码:
创建异步任务
import java.util.concurrent.CompletableFuture;
// 使用supplyAsync创建异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Hello, CompletableFuture!";
});
// 使用runAsync创建异步任务(无返回值)
CompletableFuture<Void> futureVoid = CompletableFuture.runAsync(() -> {
// 执行一些操作
});
结果处理
// thenApply方法:对结果进行转换
future.thenApply(s -> s.toUpperCase())
.thenAccept(System.out::println);
// thenAccept方法:消费结果
future.thenAccept(s -> System.out.println("Processed result: " + s));
// thenRun方法:执行一个动作,不关心结果
future.thenRun(() -> System.out.println("Task completed."));
异常处理
// exceptionally方法:处理异常
future.exceptionally(ex -> {
System.out.println("Exception occurred: " + ex.getMessage());
return "Error!";
}).thenAccept(System.out::println);
任务组合
// thenCompose方法:将一个异步任务的结果作为另一个异步任务的输入
CompletableFuture<String> futureCompose = future.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World"));
// thenCombine方法:组合两个异步任务的结果
CompletableFuture<String> futureCombine = future.thenCombine(
CompletableFuture.supplyAsync(() -> " World"),
(s1, s2) -> s1 + s2
);
// allOf方法:等待所有给定的CompletableFuture实例完成
CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(future, futureVoid);
// anyOf方法:等待任何一个给定的CompletableFuture实例完成
CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(future, futureVoid);
完成回调
// whenComplete方法:在任务完成时执行回调,无论成功或失败
future.whenComplete((result, ex) -> {
if (ex == null) {
System.out.println("Result: " + result);
} else {
System.out.println("Exception: " + ex.getMessage());
}
});
// handle方法:在任务完成时执行回调,无论成功或失败,并可以修改结果
future.handle((result, ex) -> {
if (ex == null) {
return result.toUpperCase();
} else {
return "Error: " + ex.getMessage();
}
}).thenAccept(System.out::println);
示例:链式调用
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Hello, CompletableFuture!";
}).thenApply(s -> s.toUpperCase())
.thenAccept(System.out::println)
.exceptionally(ex -> {
System.out.println("Exception occurred: " + ex.getMessage());
return "Error!";
});
以上代码展示了CompletableFuture
的一些基本用法,包括创建异步任务、处理结果、异常处理、任务组合和完成回调。在实际项目中,可以根据具体需求选择合适的方法来构建复杂的异步任务流。
CompletableFuture
能够处理在异步任务执行过程中抛出的异常。这些异常可以是由于代码逻辑错误、资源访问问题、外部服务失败等原因引起的。CompletableFuture
提供了几种方式来处理这些异常:
-
exceptionally方法:这个方法允许你指定一个函数,该函数在
CompletableFuture
完成时(无论是正常完成还是异常完成)被调用。如果CompletableFuture
在执行过程中抛出异常,exceptionally
方法中的函数将被调用,并且可以返回一个替代的结果。CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { // 模拟异常情况 throw new RuntimeException("Something went wrong"); }).exceptionally(ex -> { // 处理异常 System.out.println("Exception occurred: " + ex.getMessage()); return "Error!"; });
-
handle方法:
handle
方法允许你指定一个函数,该函数在CompletableFuture
完成时(无论是正常完成还是异常完成)被调用。与exceptionally
不同的是,handle
方法可以同时处理正常结果和异常。CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { // 模拟异常情况 throw new RuntimeException("Something went wrong"); }).handle((result, ex) -> { if (ex != null) { // 处理异常 System.out.println("Exception occurred: " + ex.getMessage()); return "Error!"; } else { // 处理正常结果 return result; } });
-
whenComplete方法:
whenComplete
方法类似于handle
方法,它允许你指定一个函数,在CompletableFuture
完成时(无论是正常完成还是异常完成)被调用。与handle
不同的是,whenComplete
不返回结果。CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { // 模拟异常情况 throw new RuntimeException("Something went wrong"); }).whenComplete((result, ex) -> { if (ex != null) { // 处理异常 System.out.println("Exception occurred: " + ex.getMessage()); } else { // 处理正常结果 System.out.println("Result: " + result); } });
-
CompletableFuture的链式调用:在链式调用中,如果任何一个
CompletableFuture
抛出异常,整个链式调用将停止,并且异常将被传递到链的下一个exceptionally
或handle
方法。CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { // 模拟异常情况 throw new RuntimeException("Something went wrong"); }).thenApply(s -> s.toUpperCase()) .exceptionally(ex -> { // 处理异常 System.out.println("Exception occurred: " + ex.getMessage()); return "Error!"; });
在使用CompletableFuture
时,应该注意异常处理的重要性。合理的异常处理可以确保程序的健壮性,避免因为异常导致的程序崩溃或数据不一致。此外,异常处理还可以提供更友好的用户反馈,帮助用户理解发生了什么问题。
CompletableFuture
的链式调用是其最强大的特性之一,它允许你将多个异步操作串联起来,形成一个异步任务链。每个操作的结果可以作为下一个操作的输入,从而实现复杂的异步处理流程。
链式调用的工作原理是基于CompletableFuture
的thenApply
、thenAccept
、thenRun
、thenCompose
等方法。这些方法允许你指定一个函数,该函数在前一个CompletableFuture
完成时被调用,并且可以返回一个新的CompletableFuture
。
以下是链式调用的基本步骤:
-
创建初始
CompletableFuture
:使用supplyAsync
或runAsync
方法创建一个CompletableFuture
实例,该实例将异步执行一个任务。 -
添加后续操作:使用
thenApply
、thenAccept
、thenRun
、thenCompose
等方法将后续操作添加到链中。这些方法接受一个函数作为参数,该函数将前一个CompletableFuture
的结果作为输入,并返回一个新的CompletableFuture
。 -
处理结果或异常:在链的末尾,你可以使用
thenAccept
、thenRun
或thenApply
来处理最终结果,或者使用exceptionally
来处理异常。 -
获取最终结果:链式调用完成后,你可以使用
get
方法来获取最终结果。如果链中任何一个CompletableFuture
抛出异常,get
方法将抛出ExecutionException
。
下面是一个简单的链式调用示例:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureChainExample {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Hello";
}).thenApply(s -> s + " World")
.thenApply(s -> s.toUpperCase())
.thenAccept(System.out::println)
.exceptionally(ex -> {
System.out.println("Exception occurred: " + ex.getMessage());
return null;
});
// 阻塞等待结果
future.join();
}
}
在这个例子中,我们首先创建了一个异步任务,该任务返回字符串"Hello"。然后,我们使用thenApply
方法将字符串转换为大写,并使用thenAccept
方法打印结果。如果在链中发生异常,exceptionally
方法将捕获异常并打印错误信息。
链式调用使得CompletableFuture
非常适合构建复杂的异步处理流程,它能够帮助开发者以声明式的方式编写异步代码,从而提高代码的可读性和可维护性。
处理多个CompletableFuture
的依赖关系通常涉及以下几种情况:
- 顺序依赖:一个
CompletableFuture
的结果是另一个CompletableFuture
的输入。 - 并行依赖:多个
CompletableFuture
可以并行执行,但需要等待它们全部完成后再继续。 - 条件依赖:一个
CompletableFuture
的结果决定了是否执行另一个CompletableFuture
。
针对这些情况,CompletableFuture
提供了不同的方法来处理依赖关系:
1. 顺序依赖
使用thenApply
、thenAccept
或thenRun
方法来处理顺序依赖。这些方法允许你指定一个函数,该函数在前一个CompletableFuture
完成时被调用,并且可以返回一个新的CompletableFuture
。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
return "Result1";
});
CompletableFuture<String> future2 = future1.thenApply(result1 -> {
// 使用future1的结果
return "Result2";
});
2. 并行依赖
使用allOf
或anyOf
方法来处理并行依赖。allOf
方法等待所有给定的CompletableFuture
实例完成,而anyOf
方法等待任何一个给定的CompletableFuture
实例完成。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
return "Result1";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
return "Result2";
});
CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2);
allFutures.thenRun(() -> {
// 所有future都已完成
});
3. 条件依赖
使用whenComplete
或handle
方法来处理条件依赖。这些方法允许你指定一个函数,该函数在CompletableFuture
完成时(无论是正常完成还是异常完成)被调用,并且可以返回一个新的CompletableFuture
。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
return "Result1";
});
CompletableFuture<String> future2 = future1.thenApply(result1 -> {
// 使用future1的结果
return "Result2";
}).whenComplete((result, ex) -> {
if (ex == null) {
// 处理正常结果
} else {
// 处理异常
}
});
4. 组合依赖
使用thenCompose
方法来组合依赖。thenCompose
方法允许你将一个CompletableFuture
的结果作为另一个异步任务的输入。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
return "Result1";
});
CompletableFuture<String> future2 = future1.thenCompose(result1 -> {
// 使用future1的结果
return CompletableFuture.supplyAsync(() -> {
return "Result2";
});
});
5. 错误处理
使用exceptionally
方法来处理错误。exceptionally
方法允许你指定一个函数,该函数在CompletableFuture
抛出异常时被调用,并且可以返回一个替代的结果。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
return "Result1";
}).exceptionally(ex -> {
// 处理异常
return "Error";
});
通过上述方法,你可以灵活地处理CompletableFuture
之间的依赖关系,构建出复杂的异步处理流程。在实际应用中,根据具体的业务逻辑和需求选择合适的方法来实现依赖关系的处理。
CompletableFuture
类提供了join()
和get()
两种方法来获取异步计算的结果。尽管它们都可以用来获取CompletableFuture
的计算结果,但它们在行为和使用场景上有所不同。
get()方法
get()
方法是Future
接口中的一个方法,它阻塞当前线程直到Future
完成。如果Future
正常完成,get()
方法将返回计算结果;如果Future
被取消,它将抛出CancellationException
;如果Future
抛出异常,它将抛出ExecutionException
。
try {
Object result = future.get();
// 使用结果
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// 处理中断异常
} catch (ExecutionException e) {
Throwable cause = e.getCause();
// 处理计算过程中抛出的异常
}
join()方法
join()
方法是CompletableFuture
特有的方法,它也阻塞当前线程直到CompletableFuture
完成。如果CompletableFuture
正常完成,join()
方法将返回计算结果;如果CompletableFuture
被取消或抛出异常,它将抛出CompletionException
,这是一个未检查异常,它包装了原始的异常。
try {
Object result = future.join();
// 使用结果
} catch (CompletionException e) {
Throwable cause = e.getCause();
// 处理计算过程中抛出的异常
}
区别
- 异常处理:
get()
方法抛出的是ExecutionException
,而join()
方法抛出的是CompletionException
。CompletionException
是ExecutionException
的子类,它提供了更多的信息,例如原始异常的类型和消息。 - 中断处理:
get()
方法在被中断时会抛出InterruptedException
,而join()
方法不会。如果你需要处理中断,应该使用get()
方法。 - 使用场景:
join()
方法通常用于CompletableFuture
链式调用中,因为它抛出的CompletionException
可以被链式调用中的exceptionally
方法捕获和处理。而get()
方法通常用于需要捕获中断异常的场景。
示例
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
return "Result";
});
try {
String result = future.get(); // 使用get()方法
System.out.println(result);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Interrupted");
} catch (ExecutionException e) {
Throwable cause = e.getCause();
System.out.println("Exception occurred: " + cause.getMessage());
}
try {
String result = future.join(); // 使用join()方法
System.out.println(result);
} catch (CompletionException e) {
Throwable cause = e.getCause();
System.out.println("Exception occurred: " + cause.getMessage());
}
在实际使用中,根据你的需求选择合适的方法。如果你需要处理中断异常,或者你的代码需要与旧的Future
接口兼容,那么get()
方法可能是更好的选择。如果你在CompletableFuture
链式调用中工作,并且希望利用CompletionException
的特性,那么join()
方法可能更适合你。