在 Java 8 中,CompletableFuture
提供了一种非常强大的方式来处理异步编程,它使得编写非阻塞代码变得更加简单和灵活。相比传统的 Future
,CompletableFuture
可以让你在异步操作完成后,执行多个后续操作(如回调、合并任务、异常处理等),大大提高了代码的可读性和可维护性。今天,我们将深入探讨一些 CompletableFuture
的高级用法,帮助你更高效地利用这个工具。
1. 组合多个异步任务
CompletableFuture
支持多种方法来组合多个异步任务,这样你可以并行执行任务并等待它们完成,然后合并结果。
示例:使用 thenCombine
合并两个异步任务的结果
import java.util.concurrent.CompletableFuture;
public class CompletableFutureCombineExample {
public static void main(String[] args) {
// 异步任务1:计算数字
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return 10;
});
// 异步任务2:计算另一个数字
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return 20;
});
// 使用 thenCombine 将两个任务的结果合并
CompletableFuture<Integer> result = future1.thenCombine(future2, (a, b) -> a + b);
// 获取并打印结果
result.thenAccept(res -> System.out.println("Combined result: " + res));
}
}
解释:
thenCombine()
方法会在两个异步任务都完成后,合并它们的结果。在这个例子中,我们将两个任务的结果相加,得到了合并后的结果30
。- 你可以根据实际需求使用不同的合并方式(如
a * b
、a - b
等)。
这种方式适合用于需要依赖多个异步任务结果的场景。
2. 使用 allOf
和 anyOf
等待多个任务完成
CompletableFuture
提供了 allOf()
和 anyOf()
方法,允许你等待多个异步任务的完成,并执行相应的后续操作。
示例:allOf
等待所有任务完成
import java.util.concurrent.CompletableFuture;
public class CompletableFutureAllOfExample {
public static void main(String[] args) {
// 创建多个异步任务
CompletableFuture<Void> task1 = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(1000);
System.out.println("Task 1 completed");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
CompletableFuture<Void> task2 = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(1500);
System.out.println("Task 2 completed");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 使用 allOf 等待所有任务完成
CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(task1, task2);
// 等待所有任务完成后执行
allOfFuture.thenRun(() -> System.out.println("All tasks completed"));
// 阻塞主线程,等待任务完成
allOfFuture.join();
}
}
解释:
allOf()
方法会等待所有给定的CompletableFuture
实例完成。如果其中任何一个任务失败,整个组合任务也会失败。- 在所有任务完成后,
thenRun()
可以用于执行一些后续操作,比如打印“所有任务完成”。
这种方法适合用于等待多个任务并发执行完毕的场景。
示例:anyOf
等待任意一个任务完成
import java.util.concurrent.CompletableFuture;
public class CompletableFutureAnyOfExample {
public static void main(String[] args) {
// 创建多个异步任务
CompletableFuture<Void> task1 = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(1000);
System.out.println("Task 1 completed");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
CompletableFuture<Void> task2 = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(500);
System.out.println("Task 2 completed");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 使用 anyOf 等待任意一个任务完成
CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(task1, task2);
// 等待第一个完成的任务执行
anyOfFuture.thenRun(() -> System.out.println("At least one task completed"));
// 阻塞主线程,等待任务完成
anyOfFuture.join();
}
}
解释:
anyOf()
方法会等待任意一个任务完成,并在完成时执行后续操作。- 在这个例子中,
task2
会更快完成,因此anyOfFuture
会在task2
完成时触发回调。
这种方式适合用于只关心哪个任务先完成的场景,例如,多个备用任务中的一个先完成即满足条件。
3. 异常处理与 handle
和 exceptionally
CompletableFuture
提供了非常丰富的异常处理能力。你可以使用 handle()
或 exceptionally()
方法来捕捉异步任务中的异常。
示例:使用 exceptionally
处理异常
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExceptionExample {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
if (true) {
throw new RuntimeException("Something went wrong!");
}
return 42;
});
// 使用 exceptionally 处理异常
future.exceptionally(ex -> {
System.out.println("Exception occurred: " + ex.getMessage());
return -1; // 返回默认值
}).thenAccept(result -> System.out.println("Result: " + result));
}
}
解释:
exceptionally()
方法用于捕捉异步任务中的异常,并返回一个默认值。在这个例子中,任务抛出异常后,我们返回了一个默认值-1
,并输出异常信息。
示例:使用 handle
处理异常并返回结果
import java.util.concurrent.CompletableFuture;
public class CompletableFutureHandleExample {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
if (true) {
throw new RuntimeException("Something went wrong!");
}
return 42;
});
// 使用 handle 处理异常
future.handle((result, ex) -> {
if (ex != null) {
System.out.println("Handled exception: " + ex.getMessage());
return -1; // 返回默认值
}
return result;
}).thenAccept(result -> System.out.println("Result: " + result));
}
}
解释:
handle()
方法不仅能处理异常,还可以处理正常结果。如果任务完成并没有抛出异常,它会返回正常的结果;如果抛出异常,它会返回一个默认值。
通过 handle()
和 exceptionally()
,你可以灵活地应对异步任务中的异常。
4. 使用 join
与 get
获取结果
CompletableFuture
提供了 join()
和 get()
方法来获取异步任务的结果,它们的主要区别是异常处理方式。
join()
会在出现异常时抛出CompletionException
。get()
会在出现异常时抛出原始的异常类型。
示例:使用 join
获取结果
import java.util.concurrent.CompletableFuture;
public class CompletableFutureJoinExample {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
return 42;
});
// 使用 join 获取结果
Integer result = future.join();
System.out.println("Result: " + result);
}
}
解释:
- 使用
join()
获取异步任务的结果,阻塞直到任务完成并返回结果。
这种方式适用于你确定任务会完成并且希望等待结果的场景。
总结
通过 CompletableFuture
,你可以轻松地实现并发任务的组合、异常处理和任务同步。它不仅支持多个任务的并行执行,还能在任务完成时自动执行后续操作,避免了回调地狱的出现。了解并熟练掌握 CompletableFuture
的高级用法,将让你在处理复杂的异步任务时得心应手,提升代码的可读性和性能。
希望这篇文章帮助你更好地理解 CompletableFuture
的高级用法,提升你的编
程技巧!