Java CompletableFuture 了解多少?

Java 8 引入的 CompletableFuture 类,通过其强大的功能和灵活的接口,为我们提供了处理异步编程和并行计算的有力工具。本文将深入探讨 CompletableFuture 的设计精妙之处,并详细介绍其在实际应用中的几个典型场景。

CompletableFuture 设计的精妙之处

1. 非阻塞异步计算

CompletableFuture 通过非阻塞的方式实现异步计算,这意味着主线程不会因为等待异步任务的完成而被阻塞。

java

CompletableFuture.supplyAsync(() -> {
    // 模拟耗时操作
    return "Result";
});

上述代码中,supplyAsync 方法会在一个独立的线程中执行耗时操作,主线程可以继续执行其它任务。

java

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
    return asyncSupplyStage(asyncPool, supplier);
}

private static <U> CompletableFuture<U> asyncSupplyStage(Executor e, Supplier<U> f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture<U> d = new CompletableFuture<U>();
    e.execute(new AsyncSupply<U>(d, f));
    return d;
}

可以看到,supplyAsync 方法通过在一个独立的线程池(asyncPool)中执行给定的 Supplier,实现了非阻塞的异步计算。这里的 Executor 接口使得线程池的实现可以灵活配置。

2. 链式任务编排

CompletableFuture 提供了丰富的链式任务编排方法,如 thenApplythenAcceptthenRun 等,使得多个异步任务可以串联起来组成一个完整的计算流程。

java

CompletableFuture.supplyAsync(() -> "Step1")
    .thenApply(result -> "Step2: " + result)
    .thenAccept(result -> System.out.println(result));

上述代码中,多个异步任务通过链式调用被串联起来,形成一个完整的计算流程。

java

public <U> CompletableFuture<U> thenApply(Function<? super T, ? extends U> fn) {
    return uniApplyStage(null, fn);
}

private <U> CompletableFuture<U> uniApplyStage(Executor e, Function<? super T, ? extends U> f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture<U> d = new CompletableFuture<U>();
    if (e != null || !d.uniApply(this, f, null)) {
        UniApply<T,U> c = new UniApply<T,U>(e, d, this, f);
        push(c);
        c.tryFire(SYNC);
    }
    return d;
}

thenApply 方法通过内部的 uniApplyStage 方法实现了链式调用。可以看到,新的 CompletableFuture 被创建并返回,形成了链条的一部分。

3. 灵活的异常处理机制

CompletableFuture 提供了灵活的异常处理机制,如 exceptionallyhandle 等方法,可以在出现异常时进行处理。

java

CompletableFuture.supplyAsync(() -> {
    if (Math.random() > 0.5) {
        throw new RuntimeException("Failed");
    }
    return "Success";
}).exceptionally(ex -> "Recovered").thenAccept(System.out::println);

上述代码中,如果异步任务抛出异常,会通过 exceptionally 方法进行处理,并返回一个默认值。

java

public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn) {
    return uniExceptionallyStage(null, fn);
}

private CompletableFuture<T> uniExceptionallyStage(Executor e, Function<Throwable, ? extends T> f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture<T> d = new CompletableFuture<T>();
    if (e != null || !d.uniExceptionally(this, f, null)) {
        UniExceptionally<T> c = new UniExceptionally<T>(e, d, this, f);
        push(c);
        c.tryFire(SYNC);
    }
    return d;
}

exceptionally 方法通过内部的 uniExceptionallyStage 方法实现了异常处理。新的 CompletableFuture 被创建并返回,用于处理异常情况。

4. 并行任务的组合

CompletableFuture 提供了多种方法用于组合并行任务,如 thenCombineallOfanyOf 等,可以将多个异步任务的结果进行组合。

java

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Result1");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Result2");

CompletableFuture<String> combinedFuture = future1.thenCombine(future2, (result1, result2) -> result1 + " & " + result2);
combinedFuture.thenAccept(System.out::println);

上述代码中,两个并行任务的结果通过 thenCombine 方法进行组合,并在最终输出。

java

public <U, V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T, ? super U, ? extends V> fn) {
    return biApplyStage(null, other, fn);
}

private <U, V> CompletableFuture<V> biApplyStage(Executor e, CompletionStage<? extends U> o, BiFunction<? super T, ? super U, ? extends V> f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture<U> b = o.toCompletableFuture();
    CompletableFuture<V> d = new CompletableFuture<V>();
    if (e != null || !d.biApply(this, b, f, null)) {
        BiApply<T, U, V> c = new BiApply<T, U, V>(e, d, this, b, f);
        push(c);
        b.push(c);
        c.tryFire(SYNC);
    }
    return d;
}

thenCombine 方法通过内部的 biApplyStage 方法实现了并行任务的组合。可以看到,两个 CompletableFuture 被组合成一个新的 CompletableFuture,形成了任务的组合。

5. 函数式编程

CompletableFuture 大量使用了 Java 8 引入的函数式编程接口(如 FunctionConsumerBiFunction 等),使得代码更加简洁和易于理解。

java

CompletableFuture.supplyAsync(() -> "Hello")
    .thenApply(String::toUpperCase)
    .thenAccept(System.out::println);

上述代码中,函数式接口 Function 和 Consumer 被用于链式调用,使得代码更加简洁。

CompletableFuture 的应用场景

1. 并行任务处理

在现代应用中,很多任务可以并行执行以提高效率。例如,从多个数据源获取数据、并行处理多个请求等。在这种场景下,CompletableFuture 可以显著提高系统的吞吐量和响应速度。

示例:并行获取数据

假设我们需要从两个独立的服务获取数据,并将结果合并。

java

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class ParallelDataFetch {

    public static CompletableFuture<String> fetchDataFromService1() {
        return CompletableFuture.supplyAsync(() -> {
            try {
                // 模拟耗时操作
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Data from Service 1";
        });
    }

    public static CompletableFuture<String> fetchDataFromService2() {
        return CompletableFuture.supplyAsync(() -> {
            try {
                // 模拟耗时操作
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Data from Service 2";
        });
    }

    public static void main(String[] args) {
        CompletableFuture<String> future1 = fetchDataFromService1();
        CompletableFuture<String> future2 = fetchDataFromService2();

        // 使用 thenCombine 将两个异步任务的结果组合起来
        CompletableFuture<String> combinedFuture = future1.thenCombine(future2, (data1, data2) -> {
            return data1 + " & " + data2;
        });

        try {
            // 获取最终结果
            String result = combinedFuture.get();
            System.out.println("Combined Result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

2. 异步请求处理

在 Web 应用中,我们常常需要处理大量的异步请求,比如异步读取数据库、调用外部 API 等。CompletableFuture 可以帮助我们在不阻塞主线程的情况下处理这些异步请求。

示例:异步处理HTTP请求

假设我们需要向两个不同的API发送HTTP请求,并将结果合并。

java

import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.util.concurrent.CompletableFuture;

public class AsyncHttpRequest {

    private static HttpClient client = HttpClient.newHttpClient();

    public static CompletableFuture<String> sendRequest1() {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.example.com/data1"))
            .build();

        return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
            .thenApply(HttpResponse::body);
    }

    public static CompletableFuture<String> sendRequest2() {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.example.com/data2"))
            .build();

        return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
            .thenApply(HttpResponse::body);
    }

    public static void main(String[] args) {
        CompletableFuture<String> future1 = sendRequest1();
        CompletableFuture<String> future2 = sendRequest2();

        CompletableFuture<String> combinedFuture = future1.thenCombine(future2, (data1, data2) -> {
            return "Combined Data: " + data1 + " & " + data2;
        });

        combinedFuture.thenAccept(System.out::println);

        // 阻塞主线程,等待异步任务完成
        combinedFuture.join();
    }
}

3. 异常处理

在异步编程中,异常处理一直是一个难点。CompletableFuture 提供了灵活的异常处理机制,使得我们可以优雅地处理异步任务中的异常。

示例:异步任务异常处理

假设我们有一个异步任务,它可能会抛出异常,我们希望在异常发生时提供一个默认值。

java

import java.util.concurrent.CompletableFuture;

public class AsyncExceptionHandling {

    public static CompletableFuture<String> asyncTask() {
        return CompletableFuture.supplyAsync(() -> {
            if (Math.random() > 0.5) {
                throw new RuntimeException("Something went wrong");
            }
            return "Success";
        });
    }

    public static void main(String[] args) {
        CompletableFuture<String> future = asyncTask();

        future.exceptionally(ex -> {
            System.out.println("Exception: " + ex.getMessage());
            return "Default Value";
        }).thenAccept(System.out::println);

        // 阻塞主线程,等待异步任务完成
        future.join();
    }
}

4. 任务流水线

在某些场景下,我们需要将多个异步任务串联起来,形成一个任务流水线。CompletableFuture 提供了丰富的链式调用方法,使得我们可以轻松实现任务的流水线处理。

示例:任务流水线

假设我们有三个任务,它们需要依次执行,并且每个任务的输出是下一个任务的输入。

java

import java.util.concurrent.CompletableFuture;

public class TaskPipeline {

    public static CompletableFuture<String> task1() {
        return CompletableFuture.supplyAsync(() -> "Task 1 Result");
    }

    public static CompletableFuture<String> task2(String input) {
        return CompletableFuture.supplyAsync(() -> input + " -> Task 2 Result");
    }

    public static CompletableFuture<String> task3(String input) {
        return CompletableFuture.supplyAsync(() -> input + " -> Task 3 Result");
    }

    public static void main(String[] args) {
        CompletableFuture<String> pipeline = task1()
            .thenCompose(result1 -> task2(result1))
            .thenCompose(result2 -> task3(result2));

        pipeline.thenAccept(System.out::println);

        // 阻塞主线程,等待异步任务完成
        pipeline.join();
    }
}

5. 响应式编程

在响应式编程中,我们需要对事件进行实时响应。CompletableFuture 可以帮助我们实现响应式编程,确保系统能够实时响应用户请求。

示例:响应式编程

假设我们有一个用户注册系统,需要在用户注册成功后发送欢迎邮件和更新用户统计信息。

java

import java.util.concurrent.CompletableFuture;

public class ReactiveProgramming {

    public static CompletableFuture<Void> registerUser(String username) {
        return CompletableFuture.runAsync(() -> {
            // 模拟用户注册
            System.out.println("User registered: " + username);
        });
    }

    public static CompletableFuture<Void> sendWelcomeEmail(String username) {
        return CompletableFuture.runAsync(() -> {
            // 模拟发送欢迎邮件
            System.out.println("Welcome email sent to: " + username);
        });
    }

    public static CompletableFuture<Void> updateUserStats(String username) {
        return CompletableFuture.runAsync(() -> {
            // 模拟更新用户统计信息
            System.out.println("User stats updated for: " + username);
        });
    }

    public static void main(String[] args) {
        String username = "john_doe";

        CompletableFuture<Void> future = registerUser(username)
            .thenCompose(result -> sendWelcomeEmail(username))
            .thenCompose(result -> updateUserStats(username));

        // 阻塞主线程,等待异步任务完成
        future.join();
    }
}

总结

CompletableFuture 在以下几个场景中非常有用:

  1. 并行任务处理:例如,从多个数据源获取数据、并行处理多个请求等。
  2. 异步请求处理:例如,异步读取数据库、调用外部 API 等。
  3. 异常处理:提供灵活的异常处理机制,确保异步任务的健壮性。
  4. 任务流水线:将多个异步任务串联起来,形成任务流水线。
  5. 响应式编程:对事件进行实时响应,确保系统能够实时响应用户请求。

通过上述示例和分析,可以看出 CompletableFuture 在实际应用中的强大功能和广泛应用场景。在实际开发中,合理使用 CompletableFuture 可以显著提高系统的性能和响应速度,提升代码的可读性和维护性。

  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值