Java CompletableFuture 用法

本文探讨了Java 8中CompletableFuture的使用,包括任务1、2和3的异步执行、流水线式异步任务示例,以及如何利用thenCompose、thenCombine和timeout/completeOnTimeout进行高级操作。通过 testCase1 和 testCase2 的案例,展示了CompletableFuture在并发编程中的高效应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CompletableFuture 是 java 8新增的,主要用来异步编程,使用时有一些需要注意到地方,下面的代码中已经备注

import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class FutureDemo {
    private static final int MAX_THREADS = 400;

    private final Random random = new Random();
    private final Executor executor;
    private final int taskCount;

    FutureDemo(int taskCount) {
        this.taskCount = taskCount;
        executor = Executors.newFixedThreadPool(Math.min(taskCount, MAX_THREADS), r -> {
            Thread thread = new Thread(r);
            thread.setDaemon(true);
            return thread;
        });
    }

    private void delay(int ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private double task1(int a) {
        delay(100);
        return Math.pow(a, 2);
    }

    private int task2(double a) {
//        delay();
        return (int) Math.sqrt(a);
    }

    private char task3(int i) {
        int ms = 10 + random.nextInt(90);
        delay(ms);
        return (char) (i - 1 + 'A');
    }

    private void testCase1() {
        measure("testCase1()", () -> {
            List<CompletableFuture<Double>> futureList = IntStream.rangeClosed(1, taskCount)
                    .mapToObj(i -> CompletableFuture.supplyAsync(() -> task1(i), executor))
                    .collect(Collectors.toList());
            List<Double> results = futureList.stream()
                    .map(CompletableFuture::join).//等待所有异步操作完成
                            collect(Collectors.toList());
            System.out.println(results);
        });
    }

    // Stream 结合 CompletableFuture 实现流水线式的异步任务
    private void testCase2() {
        measure("testCase2()", () -> {
            /*todo 知识点
             * thenCompose 后面的任务需要以前面的任务的输出作为输入
             * thenCombine 组合两个独立的任务,合并两个任务的输出
             * orTimeout 指定任务的超时时间,超时后会抛出 TimeoutException
             * completeOnTimeout 指定任务的超时时间,超时后用指定的默认值作为输出
             * */
            List<CompletableFuture<Character>> futureList = IntStream.rangeClosed(1, taskCount)
                    .mapToObj(i -> CompletableFuture.supplyAsync(() -> task1(i), executor))
                    .map(future -> future.thenApply(this::task2))// task2 不怎么耗时,采用同步操作
//                    .map(future -> future.thenCompose(i -> CompletableFuture.supplyAsync(() -> task3(i), executor)))
                    .map(future -> future.thenApplyAsync(this::task3, executor)
                            //.orTimeout(300, TimeUnit.MILLISECONDS)
                            .completeOnTimeout('*', 220, TimeUnit.MILLISECONDS)
                    )
                    .collect(Collectors.toList());

            /*List<Character> results = futureList.stream()
                    .map(CompletableFuture::join).//等待所有异步操作完成
                            collect(Collectors.toList());
            System.out.println(results);*/

            /*todo 知识点
             * thenAccept 每个任务完成时触发,有的时候不需要等所有任务都完成才执行某些操作(如分页加载,进度更新等)
             * 如果需要监听所有任务都已完成,可以使用 CompletableFuture.allOf(futures).join()
             * 有些时候只要有一个任务完成即可满足需求,可以使用 CompletableFuture.anyOf(futures)
             * 注:创建 futures 只是创建了一组待执行的任务,真正触发任务执行的是 join 方法
             * */
            long start = System.currentTimeMillis();
            CompletableFuture[] futures = futureList.stream()
                    .map(future -> future.thenAccept(character -> {
                        long time = System.currentTimeMillis() - start;
                        System.out.println("单个任务完成:" + character + ",耗时:" + time + "ms");
                    }))
                    .toArray(CompletableFuture[]::new);

            CompletableFuture.allOf(futures).join();//等待所有异步操作完成

            //如果 future 添加了 thenAccept 监听,返回的结果将是null
//            Object o = CompletableFuture.anyOf(futures).join();
//            System.out.println(o);
        });
    }

    public static void measure(String s, Runnable r) {
        long start = System.currentTimeMillis();
        r.run();
        long end = System.currentTimeMillis();
        System.out.println(s + " 用时 " + (end - start) + " ms");
    }

    public static void main(String[] args) {
        System.out.println("availableProcessors: " + Runtime.getRuntime().availableProcessors());

        final int taskCount = 26;
        FutureDemo futureDemo = new FutureDemo(taskCount);
//        futureDemo.testCase1();
        futureDemo.testCase2();
    }
}

<think>我们正在讨论Java中的CompletableFuture异步编程。根据用户的问题,他们想了解如何使用CompletableFuture进行异步编程。我们将参考提供的引用内容,并按照系统指令进行回答。首先,我们注意到引用[1]介绍了CompletableFutureJava8引入的用于异步编程的类,它提供了丰富的API来处理异步操作和组合多个异步任务。引用[2]和[3]则展示了一些具体的使用示例。 因此,我们的回答将包括:1.CompletableFuture的基本概念和创建异步任务的方法。 2.异步任务的结果处理(如回调)。3.多个异步任务的组合。4.异常处理。5.使用自定义线程池。 我们将按照这些点组织回答,并确保使用正确的LaTeX格式(行内公式用$...$,独立公式用$$...$$)以及中文回答。最后,我们还需要生成相关问题。 下面开始组织回答内容:</think>### Java CompletableFuture 异步编程指南 `CompletableFuture` 是 Java 8 引入的异步编程工具,它实现了 `Future` 和 `CompletionStage` 接口,支持非阻塞操作和任务组合。以下是核心使用方法: #### 1. **创建异步任务** ```java // 无返回值的异步任务 CompletableFuture.runAsync(() -> { System.out.println("异步任务执行中..."); // 模拟耗时操作 }); // 有返回值的异步任务 CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { return "计算结果"; }); ``` #### 2. **结果处理** ```java // 阻塞获取结果(不推荐) String result = future.get(); // 非阻塞回调 future.thenAccept(res -> System.out.println("结果: " + res)); // 结果转换(类似 map 操作) CompletableFuture<Integer> lengthFuture = future.thenApply(String::length); ``` #### 3. **任务组合** ```java // 合并两个任务结果 CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello"); CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World"); future1.thenCombine(future2, (s1, s2) -> s1 + " " + s2) .thenAccept(System.out::println); // 输出 "Hello World" // 任务链(上一个结果作为下一个输入) future.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "!")) .thenAccept(System.out::println); // 输出 "计算结果!" ``` #### 4. **异常处理** ```java future.exceptionally(ex -> { System.err.println("错误: " + ex.getMessage()); return "默认值"; // 提供降级结果 }); // 统一处理结果和异常 future.handle((res, ex) -> ex != null ? "错误恢复" : res); ``` #### 5. **多任务协同** ```java // 等待所有任务完成 CompletableFuture.allOf(future1, future2) .thenRun(() -> System.out.println("所有任务完成")); // 任一任务完成即响应 CompletableFuture.anyOf(future1, future2) .thenAccept(res -> System.out.println("首个结果: " + res)); ``` #### 6. **线程池控制** ```java ExecutorService customPool = Executors.newFixedThreadPool(2); // 指定自定义线程池 CompletableFuture.supplyAsync(() -> { // 使用自定义线程池执行 return "定制化任务"; }, customPool); ``` > **关键特性** > - **非阻塞**:通过回调避免线程等待 > - **组合式编程**:支持 $T_n = f(T_1, T_2, \dots, T_{n-1})$ 的任务依赖关系 > - **异常传播**:异常在任务链中自动传递 > - **超时控制**:`orTimeout()` 方法支持超时设置 #### 最佳实践 1. 优先使用 `thenApply`/`thenAccept` 替代阻塞的 `get()` 2. 耗时操作始终指定自定义线程池(避免占用公共池) 3. 对 I/O 密集型任务使用 `CompletableFuture.supplyAsync` 4. 复杂任务链添加超时控制:`future.orTimeout(3, TimeUnit.SECONDS)` [^1]: 深度解析:Java中的CompletableFuture异步编程 [^2]: Java异步编程利器:CompletableFuture实战 [^3]: 使用CompletableFuture实现异步操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值