CompletableFuture Java8 异步编程工具

Java的CompletableFuture是Java 8引入的一个非常强大的异步编程工具,它实现了Future和CompletionStage接口,提供了丰富的方法来组合、转换和处理异步计算的结果。下面我将从创建、转换、组合、错误处理等方面详细讲解CompletableFuture的使用。

1. 创建CompletableFuture

1.1 使用runAsync和supplyAsync

  • runAsync(Runnable runnable): 执行一个Runnable任务,不返回结果,返回一个CompletableFuture<Void>。

  • supplyAsync(Supplier<U> supplier): 执行一个Supplier任务,并返回一个CompletableFuture<U>,其中包含Supplier返回的值。

示例:

java
// 无返回值的异步任务
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
    System.out.println("执行无返回值的异步任务");
});

// 有返回值的异步任务
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    return "Hello, CompletableFuture!";
});

1.2 使用completedFuture创建一个已经完成并带有结果的Future

java
CompletableFuture<String> future = CompletableFuture.completedFuture("立即完成的结果");

1.3 使用不完整的CompletableFuture

可以通过new关键字创建一个不完整的CompletableFuture,然后在某个时刻通过complete方法手动完成它。

java
CompletableFuture<String> future = new CompletableFuture<>();
// 在某个时刻,比如另一个线程中,完成这个Future
future.complete("手动完成的结果");

2. 获取结果

2.1 get() 和 get(long timeout, TimeUnit unit)

  • get(): 阻塞直到任务完成,然后返回结果。

  • get(long timeout, TimeUnit unit): 在指定时间内阻塞等待结果,超时则抛出TimeoutException。

2.2 join()

与get()类似,但不会抛出受检异常(ExecutionException和InterruptedException),而是抛出未经检查的CompletionException。

2.3 getNow(T valueIfAbsent)

如果任务已经完成,返回结果;否则返回指定的valueIfAbsent。

3. 转换和组合

3.1 thenApply 和 thenApplyAsync

  • thenApply(Function<? super T,? extends U> fn): 当当前阶段完成后,对结果应用一个函数,返回一个新的CompletableFuture。

  • thenApplyAsync是在另一个线程中执行函数。

示例:

java
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
        .thenApply(s -> s + " World")
        .thenApply(String::toUpperCase);
System.out.println(future.get()); // 输出: HELLO WORLD

3.2 thenAccept 和 thenRun

  • thenAccept(Consumer<? super T> action): 对结果进行消费,不返回新的值。

  • thenRun(Runnable action): 不关心结果,只是在前一个阶段完成后执行一个动作。

3.3 thenCompose 和 thenCombine

  • thenCompose(Function<? super T, ? extends CompletionStage<U>> fn): 用于连接两个有依赖关系的CompletableFuture,第二个CompletableFuture依赖于第一个的结果。

  • thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn): 当两个不相关的CompletableFuture都完成后,对两个结果进行组合。

示例:

java
// thenCompose
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = future1.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World"));

// thenCombine
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future4 = CompletableFuture.supplyAsync(() -> " World");
CompletableFuture<String> combined = future3.thenCombine(future4, (s1, s2) -> s1 + s2);

4. 并行执行多个任务

4.1 allOf 和 anyOf

  • allOf(CompletableFuture<?>... cfs): 当所有的CompletableFuture都完成时,返回一个新的CompletableFuture。

  • anyOf(CompletableFuture<?>... cfs): 当任意一个CompletableFuture完成时,返回一个新的CompletableFuture。

示例:

java
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Task1");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Task2");
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Task3");

CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2, future3);
all.thenRun(() -> {
    // 所有任务都完成后的操作
    try {
        String result1 = future1.get();
        String result2 = future2.get();
        String result3 = future3.get();
        System.out.println(result1 + ", " + result2 + ", " + result3);
    } catch (Exception e) {
        e.printStackTrace();
    }
});

5. 异常处理

5.1 exceptionally

类似于try-catch中的catch,当阶段中抛出异常时,可以通过exceptionally方法返回一个备选值。

示例:

java
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    if (true) throw new RuntimeException("异常发生");
    return "正常结果";
}).exceptionally(ex -> {
    System.out.println("捕获异常: " + ex.getMessage());
    return "备选结果";
});

5.2 handle 和 handleAsync

无论阶段是否正常完成,都会执行给定的函数。函数接收两个参数:结果和异常。如果阶段正常完成,则异常参数为null;如果异常完成,则结果参数为null。

示例:

java
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 可能正常执行,也可能抛出异常
    return "结果";
}).handle((result, ex) -> {
    if (ex != null) {
        return "异常处理";
    }
    return result;
});

5.3 whenComplete

与handle类似,但whenComplete不改变结果,只是对结果或异常进行消费,返回的CompletableFuture的结果与原阶段相同。

6. 其他方法

  • completeExceptionally(Throwable ex): 手动将CompletableFuture设置为异常完成。

  • isDone(): 判断是否完成(正常完成或异常完成)。

  • isCompletedExceptionally(): 判断是否异常完成。

7. 使用线程池

默认情况下,runAsync和supplyAsync使用ForkJoinPool.commonPool()。你也可以指定自定义的线程池。

示例:

java
ExecutorService executor = Executors.newFixedThreadPool(10);
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 任务逻辑
    return "结果";
}, executor);

8. 注意事项

  • 避免在CompletableFuture的回调方法(如thenApply)中执行长时间阻塞的操作,因为这会阻塞后续任务的执行(除非使用Async后缀的方法,它们会在新的线程中执行)。

  • 合理使用异常处理,避免异常被吞没。

  • 注意线程池的选择,避免资源耗尽。

CompletableFuture提供了非常灵活的异步编程模型,通过组合各种方法,可以构建复杂的异步工作流。希望以上详解能帮助你更好地使用CompletableFuture。

 

CompletableFuture 是 Java 8 引入的强大的异步编程工具,它实现了 Future 和 CompletionStage 接口,提供了丰富的异步编程能力。下面我将从基础使用到高级特性进行全面详解。

📚 核心概念

1. 创建 CompletableFuture

 
创建方式方法签名说明
直接创建 CompletableFuture<Void> runAsync(Runnable runnable) 执行无返回值的异步任务
  CompletableFuture<U> supplyAsync(Supplier<U> supplier) 执行有返回值的异步任务
已完成 CompletableFuture<U> completedFuture(U value) 创建已完成的 Future
手动控制 new CompletableFuture<>() 手动控制的 Future
java
// 1. 无返回值的异步任务
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
    System.out.println("异步执行无返回值任务");
});

// 2. 有返回值的异步任务
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    sleep(1000);
    return "Hello, CompletableFuture!";
});

// 3. 使用自定义线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
    return "使用自定义线程池";
}, executor);

🔄 结果处理与转换

2. 结果转换 (thenApply)

java
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello");

// thenApply - 同步转换
CompletableFuture<String> upperFuture = future.thenApply(String::toUpperCase);
System.out.println(upperFuture.get()); // 输出: HELLO

// thenApplyAsync - 异步转换
CompletableFuture<String> asyncFuture = future.thenApplyAsync(s -> {
    sleep(1000);
    return s + " world";
});
System.out.println(asyncFuture.get()); // 输出: hello world

3. 结果消费 (thenAccept / thenRun)

java
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "result");

// thenAccept - 消费结果
future.thenAccept(result -> {
    System.out.println("接收到结果: " + result);
});

// thenRun - 不关心结果,只执行操作
future.thenRun(() -> {
    System.out.println("任务完成,执行清理操作");
});

// 异步版本
future.thenAcceptAsync(result -> {
    System.out.println("异步消费: " + result);
});

🔗 任务组合

4. 任务链 (thenCompose)

用于连接两个有依赖关系的异步任务:

java
// 模拟异步获取用户ID
CompletableFuture<String> getUserById(int id) {
    return CompletableFuture.supplyAsync(() -> "user_" + id);
}

// 模拟异步获取用户详情
CompletableFuture<String> getUserDetail(String user) {
    return CompletableFuture.supplyAsync(() -> user + "_details");
}

// 使用 thenCompose 连接两个依赖的任务
CompletableFuture<String> result = getUserById(123)
    .thenCompose(this::getUserDetail);

System.out.println(result.get()); // 输出: user_123_details

5. 任务合并 (thenCombine)

合并两个独立的异步任务结果:

java
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    sleep(1000);
    return "Hello";
});

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    sleep(500);
    return "World";
});

// 合并两个任务的结果
CompletableFuture<String> combined = future1.thenCombine(future2, (r1, r2) -> {
    return r1 + " " + r2;
});

System.out.println(combined.get()); // 输出: Hello World

🎯 多任务处理

6. 所有任务完成 (allOf)

java
List<CompletableFuture<String>> futures = Arrays.asList(
    CompletableFuture.supplyAsync(() -> "Task1"),
    CompletableFuture.supplyAsync(() -> "Task2"),
    CompletableFuture.supplyAsync(() -> "Task3")
);

CompletableFuture<Void> allFutures = CompletableFuture.allOf(
    futures.toArray(new CompletableFuture[0])
);

// 等待所有任务完成,然后处理结果
CompletableFuture<List<String>> allResults = allFutures.thenApply(v -> 
    futures.stream()
        .map(CompletableFuture::join)
        .collect(Collectors.toList())
);

List<String> results = allResults.get();
System.out.println(results); // 输出: [Task1, Task2, Task3]

7. 任一任务完成 (anyOf)

java
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    sleep(2000);
    return "Slow Task";
});

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    sleep(500);
    return "Fast Task";
});

CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(future1, future2);
System.out.println(anyFuture.get()); // 输出: Fast Task (先完成的任务)

🚨 异常处理

8. 异常处理方式

java
// 1. exceptionally - 异常时提供默认值
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    if (true) throw new RuntimeException("出错啦!");
    return "Success";
}).exceptionally(ex -> {
    System.out.println("异常处理: " + ex.getMessage());
    return "Default Value";
});

// 2. handle - 无论成功失败都会执行
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    return "Success";
}).handle((result, ex) -> {
    if (ex != null) {
        return "Error: " + ex.getMessage();
    }
    return "Result: " + result;
});

// 3. whenComplete - 类似 handle 但不改变结果
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
    return "Data";
}).whenComplete((result, ex) -> {
    if (ex != null) {
        System.out.println("任务失败: " + ex.getMessage());
    } else {
        System.out.println("任务成功: " + result);
    }
});

⏱️ 超时控制

9. 超时处理 (Java 9+)

java
// Java 9+ 的 orTimeout 方法
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    sleep(3000);
    return "Slow Result";
}).orTimeout(1000, TimeUnit.MILLISECONDS) // 1秒超时
  .exceptionally(ex -> {
    if (ex instanceof TimeoutException) {
        return "Timeout Fallback";
    }
    return "Other Error";
});

System.out.println(future.get()); // 输出: Timeout Fallback

// completeOnTimeout - 超时时提供默认值
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    sleep(3000);
    return "Slow Result";
}).completeOnTimeout("Default Value", 1000, TimeUnit.MILLISECONDS);

System.out.println(future2.get()); // 输出: Default Value

🏗️ 实际应用示例

10. 电商订单处理示例

java
public class OrderService {
    
    public CompletableFuture<OrderResult> processOrder(Order order) {
        // 并行执行多个任务
        CompletableFuture<Boolean> inventoryCheck = checkInventory(order);
        CompletableFuture<Boolean> paymentProcess = processPayment(order);
        CompletableFuture<Boolean> fraudCheck = checkFraud(order);
        
        // 等待所有检查通过
        return CompletableFuture.allOf(inventoryCheck, paymentProcess, fraudCheck)
            .thenCompose(v -> {
                // 验证所有检查结果
                if (inventoryCheck.join() && paymentProcess.join() && fraudCheck.join()) {
                    return shipOrder(order)
                        .thenApply(trackingNumber -> 
                            new OrderResult(true, "订单处理成功", trackingNumber));
                } else {
                    return CompletableFuture.completedFuture(
                        new OrderResult(false, "订单检查未通过", null));
                }
            })
            .exceptionally(ex -> 
                new OrderResult(false, "处理过程中出错: " + ex.getMessage(), null));
    }
    
    private CompletableFuture<Boolean> checkInventory(Order order) {
        return CompletableFuture.supplyAsync(() -> {
            sleep(200);
            return true; // 模拟库存检查
        });
    }
    
    private CompletableFuture<Boolean> processPayment(Order order) {
        return CompletableFuture.supplyAsync(() -> {
            sleep(300);
            return true; // 模拟支付处理
        });
    }
    
    private CompletableFuture<Boolean> checkFraud(Order order) {
        return CompletableFuture.supplyAsync(() -> {
            sleep(150);
            return true; // 模拟欺诈检查
        });
    }
    
    private CompletableFuture<String> shipOrder(Order order) {
        return CompletableFuture.supplyAsync(() -> {
            sleep(1000);
            return "TRK_" + System.currentTimeMillis(); // 生成物流单号
        });
    }
    
    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

record OrderResult(boolean success, String message, String trackingNumber) {}

📋 最佳实践

11. 使用建议

java
public class CompletableFutureBestPractices {
    
    // 1. 始终指定异常处理
    public CompletableFuture<String> safeAsyncOperation() {
        return CompletableFuture.supplyAsync(() -> riskyOperation())
            .exceptionally(ex -> {
                log.error("操作失败", ex);
                return "fallback";
            });
    }
    
    // 2. 使用自定义线程池避免阻塞公共线程池
    private final ExecutorService customExecutor = 
        Executors.newFixedThreadPool(10, r -> {
            Thread t = new Thread(r, "custom-async-" + r.hashCode());
            t.setDaemon(true);
            return t;
        });
    
    public CompletableFuture<String> withCustomExecutor() {
        return CompletableFuture.supplyAsync(() -> "task", customExecutor);
    }
    
    // 3. 避免在回调中阻塞
    public CompletableFuture<String> nonBlockingCallback() {
        return CompletableFuture.supplyAsync(() -> "data")
            .thenApplyAsync(result -> {
                // 异步处理,不阻塞当前线程
                return processResult(result);
            });
    }
    
    // 4. 合理使用 join() 和 get()
    public void properJoinUsage() {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "result");
        
        // 在已知已完成或可接受阻塞时使用
        String result = future.join();
        
        // 需要超时控制时使用
        try {
            result = future.get(5, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            // 处理超时
        }
    }
}

💡 核心要点总结

 
特性方法示例适用场景
创建 supplyAsync()runAsync() 启动异步任务
转换 thenApply()thenApplyAsync() 结果转换
组合 thenCompose()thenCombine() 任务依赖和合并
消费 thenAccept()thenRun() 副作用操作
多任务 allOf()anyOf() 并行处理
异常 exceptionally()handle() 错误处理
超时 orTimeout()completeOnTimeout() 超时控制

CompletableFuture 提供了强大的异步编程能力,合理使用可以显著提升应用程序的性能和响应性。关键是要理解各种操作符的语义,并妥善处理异常和资源管理。

posted @ 2025-11-12 13:13  dirgo  阅读(40)  评论(0)    收藏  举报