CompletableFuture详细介绍及方法使用示例(二)

文章详细介绍了Java中的CompletableFuture类,包括其相比Future的优点如异步执行、异常处理、结果合并和并发控制。同时,文章举例说明了thenApply、thenAccept、thenRun、thenAcceptAsync、thenRunAsync等回调函数的使用,以及acceptEither、acceptEitherAsync等处理多个CompletableFuture的方法。此外,还讨论了exceptionally和handle等异常处理方法。
摘要由CSDN通过智能技术生成

CompletableFuture详细介绍及方法使用示例(一)

CompletableFuture 相比于 Future的优缺点

优点:

  1. 异步执行:CompletableFuture 可以异步执行任务,并在任务完成后返回结果,减少线程等待时间,提高了程序的并发性能。

  2. 异常处理:CompletableFuture 提供了更全面的异常处理机制,可以通过 exceptionally() 方法和 handle() 方法来处理任务执行过程中可能出现的异常情况。

  3. 结果合并:CompletableFuture 支持多个任务的结果合并,可以调用 thenCombine()、thenAcceptBoth() 和 runAfterBoth() 等方法来实现不同类型的结果合并。

  4. 并发控制:CompletableFuture 提供了一些方法来控制任务的执行流程,例如 allOf() 和 anyOf() 方法可以实现多个任务的并发执行、thenCompose() 和 thenCombine() 方法可以实现任务之间的依赖关系等。

  5. 支持回调函数:CompletableFuture 支持使用 thenApply()、thenAccept()、thenRun() 等方法注册回调函数,当任务完成时会自动触发回调函数执行。

缺点:

  1. 学习成本较高:相对于 Future,CompletableFuture 的操作较为复杂,需要较长的学习曲线。

  2. 内存占用:因为 CompletableFuture 内部维护了多个任务的状态信息,所以它的内存占用量可能比 Future 更大。

  3. 调试困难: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 对象的结果进行组合或者进行一系列连续的操作,可以通过前面讲解过的 thenCombinethenCompose 等方法来实现。

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 对象的结果进行组合或者进行一系列连续的操作,可以通过前面讲解过的 thenCombineAsyncthenComposeAsync 等方法来实现。

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 对象的结果进行组合或者进行一系列连续的操作,可以通过前面讲解过的 thenCombinethenCompose 等方法来实现。

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 对象的结果进行组合或者进行一系列连续的操作,可以通过前面讲解过的 thenCombineAsyncthenComposeAsync 等方法来实现。

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 对象的结果进行组合或者进行一系列连续的操作,可以通过前面讲解过的 thenCombinethenCompose 等方法来实现。

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 对象的结果进行组合或者进行一系列连续的操作,可以通过前面讲解过的 thenCombineAsyncthenComposeAsync 等方法来实现。

多个 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 对象 future1future2,分别表示整数值 10 和 20,并且模拟了两个 CompletableFuture 分别需要 3 秒和 1 秒才能完成。然后通过 acceptEither 方法等待其中一个 CompletableFuture 完成,并对完成的 CompletableFuture 的结果进行操作,输出字符串 "Done:" 和结果值。因为该方法不需要返回任何结果,所以在最后调用了 get 方法来阻塞等待任务完成。

需要注意的是,如果有多个 CompletableFuture 对象,可以使用 acceptEitherAsyncacceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor) 方法来实现异步执行和使用指定的线程池。另外,也可以使用 applyToEitherapplyToEitherAsync 等方法来返回操作后的结果。

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 对象 future1future2,分别表示整数值 10 和 20,并且模拟了两个 CompletableFuture 分别需要 3 秒和 1 秒才能完成。然后通过 acceptEitherAsync 方法等待其中一个 CompletableFuture 完成,并使用一个新的线程池执行操作,输出字符串 "Done:" 和结果值。因为该方法不需要返回任何结果,所以在最后调用了 get 方法来阻塞等待任务完成。

需要注意的是,如果有多个 CompletableFuture 对象,可以使用 acceptEitheracceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor) 方法来选择执行其中一个 CompletableFuture 的结果并对其进行操作。另外,也可以使用 applyToEitherapplyToEitherAsync 等方法来返回操作后的结果。

异常处理方法

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 的结果。

需要注意的是,如果要对多个异常进行处理,可以使用 handlehandleAsync 方法。另外,也可以使用 thenApplythenApplyAsync 等方法来返回操作后的结果。

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 的结果进行处理,可以使用 thenCombinethenCombineAsync 或者 allOfanyOf 等方法。另外,也可以使用 applyToEitherapplyToEitherAsyncacceptEitheracceptEitherAsync 等方法来选择执行其中一个 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 的结果进行异步处理,可以使用 thenCombineAsyncallOfanyOf 等方法,并可以指定线程池来执行。另外,也可以使用 applyToEitherAsyncacceptEitherAsync 等方法来异步选择执行其中一个 CompletableFuture 的结果并对其进行操作。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值