CompletableFuture详细介绍及方法使用示例(一)
CompletableFuture 相比于 Future的优缺点
优点:
-
异步执行:CompletableFuture 可以异步执行任务,并在任务完成后返回结果,减少线程等待时间,提高了程序的并发性能。
-
异常处理:CompletableFuture 提供了更全面的异常处理机制,可以通过 exceptionally() 方法和 handle() 方法来处理任务执行过程中可能出现的异常情况。
-
结果合并:CompletableFuture 支持多个任务的结果合并,可以调用 thenCombine()、thenAcceptBoth() 和 runAfterBoth() 等方法来实现不同类型的结果合并。
-
并发控制:CompletableFuture 提供了一些方法来控制任务的执行流程,例如 allOf() 和 anyOf() 方法可以实现多个任务的并发执行、thenCompose() 和 thenCombine() 方法可以实现任务之间的依赖关系等。
-
支持回调函数:CompletableFuture 支持使用 thenApply()、thenAccept()、thenRun() 等方法注册回调函数,当任务完成时会自动触发回调函数执行。
缺点:
-
学习成本较高:相对于 Future,CompletableFuture 的操作较为复杂,需要较长的学习曲线。
-
内存占用:因为 CompletableFuture 内部维护了多个任务的状态信息,所以它的内存占用量可能比 Future 更大。
-
调试困难:CompletableFuture 的链式调用可能会使代码变得难以调试。
总的来说,CompletableFuture 比 Future 更加灵活、功能更强大,但同时也需要投入更多的学习成本和注意内存占用量等问题。在实际开发中,应该根据具体需求选择合适的方式。
任务完成时触发的回调函数注册方法
CompletableFuture.thenApply(Function fn)
thenApply(Function<? super T,? extends U> fn)
是 CompletableFuture
类的一个转换方法,它能够对 CompletableFuture 返回的结果进行转换,并返回一个新的 CompletableFuture<U>
对象,其中 U
表示转换后的结果类型。其方法签名如下:
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn);
其中,fn
表示需要对结果进行转换的函数,它是一个 Function
对象,接受一个参数(即 CompletableFuture 的结果值),执行转换操作,并返回转换后的结果。
使用示例:
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = future1.thenApply(str -> str + " world");
String result = future2.get(); // 阻塞等待任务完成
System.out.println(result); // 输出 "Hello world"
在上面的示例中,首先通过 supplyAsync
方法创建了一个 CompletableFuture 对象 future1
,表示字符串 "Hello"。然后通过 thenApply
方法对这个 CompletableFuture 的结果进行转换,将原来的字符串加上一个空格和单词 "world",并返回一个新的 CompletableFuture 对象 future2
。最后通过 get
方法获取结果。
需要注意的是,thenApply
方法返回的 CompletableFuture 对象是一个新对象,与原来的 CompletableFuture 对象没有任何关系,因此可以在任意位置、任意次数地调用该方法对结果进行转换。如果需要对多个 CompletableFuture 对象的结果进行组合或者进行一系列连续的操作,可以通过前面讲解过的 thenCombine
、thenCompose
等方法来实现。
CompletableFuture.thenApplyAsync(Function fn, Executor executor)
thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
是 CompletableFuture
类的一个异步转换方法,它和 thenApply
方法的作用相同,都能够对 CompletableFuture 返回的结果进行转换,并返回一个新的 CompletableFuture<U>
对象,其中 U
表示转换后的结果类型。不同的是,这个方法会使用指定的线程池来执行转换操作,可以使转换操作在另外一个线程上异步执行,避免当前线程阻塞等待转换完成。其方法签名如下:
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor);
其中,fn
表示需要对结果进行转换的函数,它是一个 Function
对象,接受一个参数(即 CompletableFuture 的结果值),执行转换操作,并返回转换后的结果;executor
表示执行转换操作的线程池。
使用示例:
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = future1.thenApplyAsync(str -> str + " world", Executors.newFixedThreadPool(1));
String result = future2.get(); // 阻塞等待任务完成
System.out.println(result); // 输出 "Hello world"
在上面的示例中,首先通过 supplyAsync
方法创建了一个 CompletableFuture 对象 future1
,表示字符串 "Hello"。然后通过 thenApplyAsync
方法对这个 CompletableFuture 的结果进行异步转换,将原来的字符串加上一个空格和单词 "world",并使用一个新的线程池来执行转换操作。最后通过 get
方法获取结果。
需要注意的是,thenApplyAsync
方法返回的 CompletableFuture 对象是一个新对象,并且转换操作可能在另外一个线程上异步执行,因此不能保证转换的顺序和执行的时间。如果需要对多个 CompletableFuture 对象的结果进行组合或者进行一系列连续的操作,可以通过前面讲解过的 thenCombineAsync
、thenComposeAsync
等方法来实现。
CompletableFuture.thenAccept(Consumer action)
thenAccept(Consumer<? super T> action)
是 CompletableFuture
类的一个转换方法,它能够在 CompletableFuture 返回的结果上进行一些操作,但不会返回任何结果。其方法签名如下:
public CompletableFuture<Void> thenAccept(Consumer<? super T> action);
其中,action
表示需要对 CompletableFuture 返回的结果进行的操作,它是一个 Consumer
对象,接受一个参数(即 CompletableFuture 的结果值),执行相应的操作。因为该方法不返回任何结果,所以返回类型为 CompletableFuture<Void>
。
使用示例:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Void> future2 = future1.thenAccept(value -> System.out.println("Result: " + value));
future2.get(); // 阻塞等待任务完成
在上面的示例中,首先通过 supplyAsync
方法创建了一个 CompletableFuture 对象 future1
,表示整数值 10。然后通过 thenAccept
方法对这个 CompletableFuture 进行操作,将结果值与字符串 "Result: " 拼接后输出。因为该方法不返回任何结果,所以在最后调用了 get
方法来阻塞等待任务完成。
需要注意的是,thenAccept
方法返回的 CompletableFuture 对象是一个新对象,且不包含任何结果,只是在原有 CompletableFuture 的结果上进行操作。如果需要对多个 CompletableFuture 对象的结果进行组合或者进行一系列连续的操作,可以通过前面讲解过的 thenCombine
、thenCompose
等方法来实现。
CompletableFuture.thenAcceptAsync(Consumer action, Executor executor)
thenAcceptAsync(Consumer<? super T> action, Executor executor)
方法是 CompletableFuture
类的一个异步转换方法,它和 thenAccept
方法的作用相同,都能够在 CompletableFuture 返回的结果上进行一些操作,但不会返回任何结果。不同的是,这个方法会使用指定的线程池来执行操作,可以使操作在另外一个线程上异步执行,避免当前线程阻塞等待操作完成。其方法签名如下:
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor);
其中,action
表示需要对 CompletableFuture 返回的结果进行的操作,它是一个 Consumer
对象,接受一个参数(即 CompletableFuture 的结果值),执行相应的操作;executor
表示执行操作的线程池。
使用示例:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Void> future2 = future1.thenAcceptAsync(value -> System.out.println("Result: " + value), Executors.newFixedThreadPool(1));
future2.get(); // 阻塞等待任务完成
在上面的示例中,首先通过 supplyAsync
方法创建了一个 CompletableFuture 对象 future1
,表示整数值 10。然后通过 thenAcceptAsync
方法对这个 CompletableFuture 进行操作,将结果值与字符串 "Result: " 拼接后输出,并使用一个新的线程池来执行操作。因为该方法不返回任何结果,所以在最后调用了 get
方法来阻塞等待任务完成。
需要注意的是,thenAcceptAsync
方法返回的 CompletableFuture 对象是一个新对象,且不包含任何结果,只是在原有 CompletableFuture 的结果上进行操作。如果需要对多个 CompletableFuture 对象的结果进行组合或者进行一系列连续的操作,可以通过前面讲解过的 thenCombineAsync
、thenComposeAsync
等方法来实现。
CompletableFuture.thenRun(Runnable action)
thenRun(Runnable action)
是 CompletableFuture
类的一个转换方法,它可以在 CompletableFuture 完成后执行一些操作,但不关心 CompletableFuture 的返回值。其方法签名如下:
public CompletableFuture<Void> thenRun(Runnable action);
其中,action
表示需要执行的操作,它是一个 Runnable
对象,不接受任何参数,执行相应的操作。因为该方法不需要使用 CompletableFuture 的返回值,所以返回类型为 CompletableFuture<Void>
。
使用示例:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Void> future2 = future1.thenRun(() -> System.out.println("Done"));
future2.get(); // 阻塞等待任务完成
在上面的示例中,首先通过 supplyAsync
方法创建了一个 CompletableFuture 对象 future1
,表示整数值 10。然后通过 thenRun
方法在该 CompletableFuture 完成后执行一个简单的操作,输出字符串 "Done"。因为该方法不需要返回任何结果,所以在最后调用了 get
方法来阻塞等待任务完成。
需要注意的是,thenRun
方法返回的 CompletableFuture 对象是一个新对象,且不包含任何结果。如果需要对多个 CompletableFuture 对象的结果进行组合或者进行一系列连续的操作,可以通过前面讲解过的 thenCombine
、thenCompose
等方法来实现。
CompletableFuture.thenRunAsync(Runnable action, Executor executor)
thenRunAsync(Runnable action, Executor executor)
是 CompletableFuture
类的一个异步转换方法,它可以在 CompletableFuture 完成后执行一些操作,但不关心 CompletableFuture 的返回值,而且可以使用指定的线程池来执行操作。其方法签名如下:
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor);
其中,action
表示需要执行的操作,它是一个 Runnable
对象,不接受任何参数,执行相应的操作;executor
表示执行操作的线程池。
使用示例:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Void> future2 = future1.thenRunAsync(() -> System.out.println("Done"), Executors.newFixedThreadPool(1));
future2.get(); // 阻塞等待任务完成
在上面的示例中,首先通过 supplyAsync
方法创建了一个 CompletableFuture 对象 future1
,表示整数值 10。然后通过 thenRunAsync
方法在该 CompletableFuture 完成后使用一个新的线程池执行一个简单的操作,输出字符串 "Done"。因为该方法不需要返回任何结果,所以在最后调用了 get
方法来阻塞等待任务完成。
需要注意的是,thenRunAsync
方法返回的 CompletableFuture 对象是一个新对象,且不包含任何结果。如果需要对多个 CompletableFuture 对象的结果进行组合或者进行一系列连续的操作,可以通过前面讲解过的 thenCombineAsync
、thenComposeAsync
等方法来实现。
多个 CompletableFuture 对象中任意一个完成时触发的回调函数注册方法
CompletableFuture.acceptEither()
acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
是 CompletableFuture
类的一个转换方法,它可以在当前 CompletableFuture 或者另外一个 CompletableFuture 完成后执行一些操作,接受一个结果值。其方法签名如下:
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action);
其中,other
表示另外一个 CompletableFuture 对象;action
表示需要执行的操作,它是一个 Consumer
对象,接受一个参数(即两个 CompletableFuture 中最先完成的 CompletableFuture 的结果值),执行相应的操作。因为该方法不需要返回任何结果,所以返回类型为 CompletableFuture<Void>
。
使用示例:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 10;
});
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 20;
});
CompletableFuture<Void> future3 = future1.acceptEither(future2, result -> System.out.println("Done: " + result));
future3.get(); // 阻塞等待任务完成
在上面的示例中,首先通过 supplyAsync
方法创建了两个 CompletableFuture 对象 future1
和 future2
,分别表示整数值 10 和 20,并且模拟了两个 CompletableFuture 分别需要 3 秒和 1 秒才能完成。然后通过 acceptEither
方法等待其中一个 CompletableFuture 完成,并对完成的 CompletableFuture 的结果进行操作,输出字符串 "Done:" 和结果值。因为该方法不需要返回任何结果,所以在最后调用了 get
方法来阻塞等待任务完成。
需要注意的是,如果有多个 CompletableFuture 对象,可以使用 acceptEitherAsync
或 acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor)
方法来实现异步执行和使用指定的线程池。另外,也可以使用 applyToEither
、applyToEitherAsync
等方法来返回操作后的结果。
CompletableFuture.acceptEitherAsync()
acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
是 CompletableFuture
类的一个转换方法,它可以在当前 CompletableFuture 或者另外一个 CompletableFuture 完成后执行一些操作,接受一个结果值。其方法签名如下:
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor);
其中,other
表示另外一个 CompletableFuture 对象;action
表示需要执行的操作,它是一个 Consumer
对象,接受一个参数(即两个 CompletableFuture 中最先完成的 CompletableFuture 的结果值),执行相应的操作;executor
表示执行操作的线程池。
使用示例:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 10;
});
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 20;
});
CompletableFuture<Void> future3 = future1.acceptEitherAsync(future2, result -> System.out.println("Done: " + result), Executors.newFixedThreadPool(1));
future3.get(); // 阻塞等待任务完成
在上面的示例中,首先通过 supplyAsync
方法创建了两个 CompletableFuture 对象 future1
和 future2
,分别表示整数值 10 和 20,并且模拟了两个 CompletableFuture 分别需要 3 秒和 1 秒才能完成。然后通过 acceptEitherAsync
方法等待其中一个 CompletableFuture 完成,并使用一个新的线程池执行操作,输出字符串 "Done:" 和结果值。因为该方法不需要返回任何结果,所以在最后调用了 get
方法来阻塞等待任务完成。
需要注意的是,如果有多个 CompletableFuture 对象,可以使用 acceptEither
、acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
或 acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor)
方法来选择执行其中一个 CompletableFuture 的结果并对其进行操作。另外,也可以使用 applyToEither
、applyToEitherAsync
等方法来返回操作后的结果。
异常处理方法
CompletableFuture.exceptionally()
exceptionally(Function<Throwable, ? extends T> fn)
是 CompletableFuture
类的一个转换方法,它可以在 CompletableFuture 抛出异常时执行一些操作,并返回一个新的 CompletableFuture 对象。其方法签名如下:
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn);
其中,fn
表示需要执行的操作,它是一个 Function
对象,接受一个参数(即抛出的异常),根据异常返回一个结果值或者抛出一个新的异常。如果原始的 CompletableFuture 完成时没有抛出异常,则返回原始的 CompletableFuture 对象;否则,会返回一个新的 CompletableFuture 对象,并且使用指定的操作处理异常。新的 CompletableFuture 可能是已经完成的(如果操作返回一个非空结果)或者未完成的(如果操作抛出了一个新的异常)。
使用示例:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("Error occurred");
});
CompletableFuture<Integer> future2 = future1.exceptionally(exception -> {
System.out.println("Error occurred: " + exception.getMessage());
return 0;
});
System.out.println(future2.get()); // 输出 0
在上面的示例中,首先通过 supplyAsync
方法创建了一个 CompletableFuture 对象 future1
,并且模拟了该 CompletableFuture 抛出了一个运行时异常。然后通过 exceptionally
方法处理异常,输出错误信息并返回整数值 0。因为该方法返回一个新的 CompletableFuture 对象,所以在最后调用了 get
方法来阻塞等待任务完成,并输出新的 CompletableFuture 的结果。
需要注意的是,如果要对多个异常进行处理,可以使用 handle
或 handleAsync
方法。另外,也可以使用 thenApply
、thenApplyAsync
等方法来返回操作后的结果。
CompletableFuture.handle()
handle(BiFunction<? super T, Throwable, ? extends U> fn)
是 CompletableFuture
类的一个转换方法,它可以对 CompletableFuture 的结果或者异常进行处理,并返回一个新的 CompletableFuture 对象。其方法签名如下:
public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
其中,fn
表示需要执行的操作,它是一个 BiFunction
对象,接受两个参数:第一个参数是原始的 CompletableFuture 的结果值,可能为 null;第二个参数是原始的 CompletableFuture 抛出的异常,可能为 null。根据结果值和异常返回一个结果对象,结果对象可以是任何类型。如果原始的 CompletableFuture 完成时没有抛出异常,则 fn 函数仅接收第一个参数;否则,fn 函数仅接收第二个参数。
使用示例:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
return 10;
});
CompletableFuture<String> future2 = future1.handle((result, exception) -> {
if (exception == null) {
return "Result: " + result;
} else {
return "Error occurred: " + exception.getMessage();
}
});
System.out.println(future2.get()); // 输出 "Result: 10"
在上面的示例中,首先通过 supplyAsync
方法创建了一个 CompletableFuture 对象 future1
,表示整数值 10。然后通过 handle
方法对结果值进行处理,如果原始的 CompletableFuture 成功完成,则返回字符串 "Result: " 和结果值;否则,返回字符串 "Error occurred: " 和异常信息。因为该方法返回一个新的 CompletableFuture 对象,所以在最后调用了 get
方法来阻塞等待任务完成,并输出新的 CompletableFuture 的结果。
需要注意的是,如果要对多个 CompletableFuture 的结果进行处理,可以使用 thenCombine
、thenCombineAsync
或者 allOf
、anyOf
等方法。另外,也可以使用 applyToEither
、applyToEitherAsync
、acceptEither
、acceptEitherAsync
等方法来选择执行其中一个 CompletableFuture 的结果并对其进行操作。
CompletableFuture.handleAsync()
handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)
是 CompletableFuture
类的一个转换方法,它可以对 CompletableFuture 的结果或者异常进行异步处理,并返回一个新的 CompletableFuture 对象。其方法签名如下:
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor);
其中,fn
表示需要执行的操作,它是一个 BiFunction
对象,接受两个参数:第一个参数是原始的 CompletableFuture 的结果值,可能为 null;第二个参数是原始的 CompletableFuture 抛出的异常,可能为 null。根据结果值和异常返回一个结果对象,结果对象可以是任何类型。如果原始的 CompletableFuture 完成时没有抛出异常,则 fn 函数仅接收第一个参数;否则,fn 函数仅接收第二个参数。而 executor
参数表示用于执行 fn 函数的线程池。
使用示例:
Executor executor = Executors.newSingleThreadExecutor();
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
return 10;
});
CompletableFuture<String> future2 = future1.handleAsync((result, exception) -> {
if (exception == null) {
return "Result: " + result;
} else {
return "Error occurred: " + exception.getMessage();
}
}, executor);
System.out.println(future2.get()); // 输出 "Result: 10"
在上面的示例中,创建了一个单线程的线程池 executor
。然后通过 supplyAsync
方法创建了一个 CompletableFuture 对象 future1
,表示整数值 10。接着通过 handleAsync
方法对结果值进行异步处理,如果原始的 CompletableFuture 成功完成,则返回字符串 "Result: " 和结果值;否则,返回字符串 "Error occurred: " 和异常信息。因为该方法返回一个新的 CompletableFuture 对象,所以在最后调用了 get
方法来阻塞等待任务完成,并输出新的 CompletableFuture 的结果。
需要注意的是,如果要对多个 CompletableFuture 的结果进行异步处理,可以使用 thenCombineAsync
、allOf
、anyOf
等方法,并可以指定线程池来执行。另外,也可以使用 applyToEitherAsync
、acceptEitherAsync
等方法来异步选择执行其中一个 CompletableFuture 的结果并对其进行操作。