线程并行控制CompletableFuture

并行执行两个任务A和B。主线程等待时间最长为3s。所以A和B,单独运行的时长最长也是3s。

且如果A和B都没有超时,那么优先取A的值。否者谁不超时,就取谁。

线程池单个提交

比如下面的代码,先提交了两个异步任务,此时都已经开始执行了。主线程此时等待taskA任务结束。任务A用时时间为a。那么任务B此时也执行了a时间,所以taskB剩余时间为3s - a。

dart 复制代码
    public static void main(String[] args) {
        long configTime = 3000;

        ExecutorService executorService = Executors.newFixedThreadPool(5);

        Future<String> taskA = executorService.submit(() -> {
            try {
                Thread.sleep(new Random().nextInt(5000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "taskA";
        });

        Future<String> taskB = executorService.submit(() -> {
            try {
                Thread.sleep(new Random().nextInt(5000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "taskB";
        });
        String result = null;
        long timeStart = System.currentTimeMillis();
        try {
            result = taskA.get(configTime, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException e) {
            throw new RuntimeException(e);
        }
        long timeEnd = System.currentTimeMillis();

        try {
            result = taskB.get(configTime - (timeEnd - timeStart), TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException e) {
            throw new RuntimeException(e);
        }
        System.out.println(result);

    }

invokeAll

invoekAll 同时执行一批任务,设置整体的超时时间。

这批任务,要么执行完成,要么超时了(未完成的任务将被取消)。所以每一个任务的状态都是done。

dart 复制代码
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        long configTime = 3000;

        ExecutorService executorService = Executors.newFixedThreadPool(5);

        Callable<String> taskA = () -> {
            int time = 0;
            try {
                time = new Random().nextInt(5000);
                Thread.sleep(time);
                // Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "taskA" + time;
        };

        Callable<String> taskB = () -> {
            int time = 0;
            try {
                time = new Random().nextInt(5000);
                Thread.sleep(time);
                // Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "taskB " +  time;
        };
        long start = System.currentTimeMillis();
        List<Future<String>> futures = executorService.invokeAll(Arrays.asList(taskA, taskB), configTime, TimeUnit.MILLISECONDS);

        // 3. 遍历获取结果
        Future<String> futureA = futures.get(0);
        Future<String> futureB = futures.get(1);
        long end = System.currentTimeMillis();
        if (!futureA.isCancelled()) {
            System.out.println(futureA.get());
        } else {
            System.out.println("任务A超时被取消");
        }

        if (!futureB.isCancelled()) {
            System.out.println(futureB.get());
        } else {
            System.out.println("任务B超时被取消");
        }
        System.out.println("耗时:"+(end-start));

    }

CompletableFuture

dart 复制代码
  public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        // 超时时间配置(和原代码一致)
        long configTime = 3000;
        // 保持和原代码一致的线程池配置(避免使用默认ForkJoinPool)
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        // 定义任务A(替换原Callable)
        CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
                    int time = new Random().nextInt(5000);
                    try {
                        Thread.sleep(time);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        return "任务A被中断";
                    }
                    return "taskA" + time;
                }, executorService)
                // 设置超时时间,超时抛出TimeoutException
                .orTimeout(configTime, TimeUnit.MILLISECONDS)
                // 捕获超时/异常,返回超时提示
                .exceptionally(ex -> {
                    if (ex instanceof TimeoutException) {
                        return "任务A超时被取消";
                    } else {
                        return "任务A执行异常:" + ex.getMessage();
                    }
                });

        // 定义任务B(替换原Callable)
        CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> {
                    int time = new Random().nextInt(5000);
                    try {
                        Thread.sleep(time);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        return "任务B被中断";
                    }
                    return "taskB " + time;
                }, executorService)
                // 设置超时时间
                .orTimeout(configTime, TimeUnit.MILLISECONDS)
                // 捕获超时/异常
                .exceptionally(ex -> {
                    if (ex instanceof TimeoutException) {
                        return "任务B超时被取消";
                    } else {
                        return "任务B执行异常:" + ex.getMessage();
                    }
                });

        // 记录开始时间
        long start = System.currentTimeMillis();

        // 等待所有任务完成(无论成功/超时/异常)
        CompletableFuture.allOf(futureA, futureB).join();

        // 记录结束时间
        long end = System.currentTimeMillis();

        // 获取并输出结果
        System.out.println(futureA.join());
        System.out.println(futureB.join());
        System.out.println("耗时:" + (end - start));

    }

CompletableFuture 与传统线程池的核心区别解析

你作为Java新手,想搞清楚CompletableFuture和传统线程池(如ThreadPoolExecutor/Executors创建的线程池)的核心区别,我会从定位、用法、核心能力 三个维度,用通俗的语言+实战示例帮你理清------核心结论先抛给你:传统线程池是"执行任务的线程容器",而CompletableFuture是"基于线程池的异步编程工具",前者解决"线程管理"问题,后者解决"异步流程编排"问题。

一、核心定位与设计目标(最根本的区别)

特性 传统线程池(ThreadPoolExecutor) CompletableFuture
核心定位 线程的"管理者":创建、复用、销毁线程 异步流程的"编排者":基于线程池实现异步编程
设计目标 解决线程创建/销毁的开销问题,提高线程复用率 解决传统Future的阻塞、无法链式调用、无法组合任务的痛点
依赖关系 独立的基础组件 依赖线程池(可使用默认/commonPool或自定义线程池)
简单说:传统线程池只负责"找个线程执行你的任务",而CompletableFuture帮你"优雅地处理任务的异步结果、组合多个异步任务"。

二、核心能力对比(附实战示例)

1. 结果获取方式:阻塞 vs 异步回调(最直观的区别)

传统线程池提交任务后,只能通过Future.get()阻塞式 获取结果(或轮询isDone()),主线程会被卡住;而CompletableFuture支持非阻塞的异步回调,任务完成后自动触发后续逻辑。

示例1:传统线程池的痛点(阻塞获取结果)
Java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TraditionalThreadPoolDemo {
    public static void main(String[] args) throws Exception {
        // 1. 创建传统线程池
        ExecutorService pool = Executors.newFixedThreadPool(1);

        // 2. 提交任务,获取Future
        System.out.println("主线程:提交任务,开始等待结果...");
        Future<String> future = pool.submit(() -> {
            Thread.sleep(2000); // 模拟耗时操作
            return "传统线程池的结果";
        });

        // 3. 必须阻塞等待结果(get()),主线程卡2秒
        String result = future.get(); // 阻塞!主线程停在这里
        System.out.println("主线程:获取到结果:" + result);

        pool.shutdown();
    }
}

执行结果 :主线程会卡在get()处,2秒后才打印结果,期间主线程无法做任何事。

示例2:CompletableFuture的异步回调(非阻塞)
Java 复制代码
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CompletableFutureDemo {
    public static void main(String[] args) throws InterruptedException {
        // 1. 自定义线程池(复用传统线程池)
        ExecutorService pool = Executors.newFixedThreadPool(1);

        // 2. 提交任务,异步回调(主线程不阻塞)
        System.out.println("主线程:提交任务,继续执行其他逻辑...");
        CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "CompletableFuture的结果";
        }, pool) // 指定自定义线程池执行
        .thenAccept(result -> {
            // 任务完成后自动执行(异步回调,不阻塞主线程)
            System.out.println("回调线程:获取到结果:" + result);
        });

        // 3. 主线程不阻塞,立即执行这行代码
        System.out.println("主线程:我在做其他事,比如打印日志、处理请求...");

        // 等待回调执行完(仅示例用,实际业务无需手动等)
        Thread.sleep(2500);
        pool.shutdown();
    }
}

执行结果

Plain 复制代码
主线程:提交任务,继续执行其他逻辑...
主线程:我在做其他事,比如打印日志、处理请求...
(2秒后)
回调线程:获取到结果:CompletableFuture的结果
2. 任务组合能力:无 vs 强大

传统线程池无法直接组合多个任务(比如"等3个任务都完成后汇总结果"),需要手动写逻辑判断,代码繁琐;而CompletableFuture内置allOf/anyOf等方法,轻松实现多任务组合。

示例:多任务组合(传统线程池 vs CompletableFuture)
  • 传统线程池:需要手动创建多个Future,循环判断是否全部完成,代码量大且易出错;

  • CompletableFuture:一行allOf()就能等待所有任务完成,代码简洁。

Java 复制代码
// CompletableFuture组合多任务(传统线程池做不到这么优雅)
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "任务1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "任务2");
// 等待所有任务完成
CompletableFuture<Void> allTasks = CompletableFuture.allOf(task1, task2);
allTasks.thenRun(() -> {
    System.out.println("所有任务完成,汇总结果:" + task1.join() + "," + task2.join());
});
3. 异常处理:繁琐 vs 优雅
  • 传统线程池:任务异常只能在get()时抛出ExecutionException,必须手动try-catch,且只能在获取结果时处理;

  • CompletableFuture:内置exceptionally()/handle()方法,能在异步流程中直接处理异常,不用等到获取结果时才处理。

Java 复制代码
// CompletableFuture优雅处理异常
CompletableFuture.supplyAsync(() -> {
    throw new RuntimeException("任务执行失败");
}).exceptionally(ex -> {
    // 异常时返回兜底值
    System.out.println("捕获异常:" + ex.getMessage());
    return "兜底结果";
}).thenAccept(result -> System.out.println("最终结果:" + result)); // 输出"兜底结果"

总结(核心区别)

  1. 定位不同 :传统线程池是"线程管理工具",负责执行任务;CompletableFuture是"异步编程工具",基于线程池实现异步流程编排(回调、组合、异常处理)。

  2. 结果处理不同 :传统线程池只能阻塞获取结果;CompletableFuture支持非阻塞的异步回调,主线程不被卡。

  3. 能力边界不同 :传统线程池无任务组合、优雅异常处理能力;CompletableFuture内置这些能力,是对传统线程池+Future的增强。

新手小贴士 :实际开发中,不会用CompletableFuture替代传统线程池,而是结合使用 ------用自定义线程池管理线程(避免默认池耗尽),用CompletableFuture编排异步任务(回调、组合)。

Java CompletableFuture 快速入门:从基础到组合

你作为Java新手,想要快速掌握CompletableFuture的核心用法,我会用最易懂的方式,结合实战代码帮你"速成"------重点聚焦日常开发中最常用的核心功能,避开复杂的底层原理,让你能马上上手。

一、先搞懂:CompletableFuture是什么?

CompletableFuture是Java 8新增的异步编程工具,解决了传统Future只能阻塞获取结果、无法链式调用的痛点。简单说:

  • 它能异步执行任务,不阻塞主线程;

  • 支持链式回调,任务执行完自动触发后续逻辑;

  • 组合多个异步任务(比如"同时执行多个任务,全部完成后汇总结果");

  • 自带异常处理机制,不用手动try-catch。

二、核心用法(速成重点,附可运行代码)

前置说明

所有示例基于Java 8+,无需额外依赖;默认使用JDK的ForkJoinPool.commonPool()线程池(也可自定义线程池,文末会提)。

1. 基础:创建异步任务

分两种核心场景:无返回值有返回值

Java 复制代码
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureQuickStart {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // ========== 场景1:无返回值的异步任务(runAsync) ==========
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            // 异步执行的逻辑(比如耗时操作:读写文件、调用接口)
            try {
                Thread.sleep(1000); // 模拟耗时1秒
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("任务1执行完成(无返回值)");
        });
        // 阻塞等待任务完成(新手调试用,实际开发尽量用回调,避免阻塞)
        future1.get();

        // ========== 场景2:有返回值的异步任务(supplyAsync) ==========
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("任务2执行完成(有返回值)");
            return "任务2的返回结果"; // 返回任意类型(String/对象/基本类型)
        });
        // 获取返回值(阻塞式)
        String result2 = future2.get();
        System.out.println("任务2返回值:" + result2);
    }
}

关键解释

  • runAsync(Runnable):适合执行"只做事、不返回结果"的异步任务,返回CompletableFuture<Void>

  • supplyAsync(Supplier<T>):适合执行"需要返回结果"的异步任务,返回CompletableFuture<T>(T是返回值类型);

  • get():阻塞当前线程,直到任务完成并获取结果(新手慎用!实际优先用回调)。

2. 核心:异步回调(避免阻塞)

不用get()阻塞,而是让任务执行完自动触发后续逻辑,这是CompletableFuture的核心价值。

方法 作用 输入 输出 示例场景
thenApply 接收上一步结果,返回新值 有输入 有输出 对异步结果做加工处理
thenApplyAsync 接收上一步结果,返回新值 有输入 有输出 与thenApply不同点:重启另外一个线程执行
applyToEither 接收上一步结果,返回新值 有输入 有输出 二者取快的那一个(和anyOf类似)
thenAccept 接收上一步结果,无返回值 有输入 无输出 消费结果(比如打印、入库)
thenAcceptAsync 接收上一步结果,无返回值 有输入 无输出 与thenAccept不同点:重启另外一个线程执行
acceptEither 接收上一步结果,无返回值 有输入 无输出 二者取快的那一个(和anyOf类似)
thenRun 不接收结果,无返回值 无输入 无输出 结果处理完后执行收尾操作
Java 复制代码
public class CompletableFutureCallback {
    public static void main(String[] args) throws InterruptedException {
        // 1. thenApply:加工结果(有输入有输出)
        CompletableFuture<Integer> futureApply = CompletableFuture.supplyAsync(() -> 10)
                .thenApply(num -> num * 2) // 接收10,返回20
                .thenApply(num -> num + 5); // 接收20,返回25
        futureApply.thenAccept(result -> System.out.println("thenApply最终结果:" + result)); // 输出25

        // 2. thenAccept:消费结果(有输入无输出)
        CompletableFuture.supplyAsync(() -> "Hello")
                .thenAccept(str -> System.out.println("thenAccept消费:" + str + " World")); // 输出Hello World

        // 3. thenRun:无输入无输出
        CompletableFuture.runAsync(() -> System.out.println("任务执行中..."))
                .thenRun(() -> System.out.println("thenRun:任务执行完,做收尾")); // 任务结束后执行

        // 等待所有回调执行完(仅示例用,实际不用手动等)
        Thread.sleep(1000);
    }
}
3. 必学:异常处理

异步任务抛异常时,用exceptionallyhandle处理,避免程序崩溃。

Java 复制代码
public class CompletableFutureException {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 场景1:exceptionally(仅处理异常,正常结果透传)
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            if (true) {
                throw new RuntimeException("任务执行失败!");
            }
            return 100;
        }).exceptionally(ex -> {
            // 异常时返回默认值
            System.out.println("捕获异常:" + ex.getMessage());
            return 0; // 兜底值
        });
        System.out.println("exceptionally结果:" + future1.get()); // 输出0

        // 场景2:handle(同时处理正常结果和异常)
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 200)
                .handle((result, ex) -> {
                    if (ex != null) {
                        System.out.println("handle捕获异常:" + ex.getMessage());
                        return -1;
                    }
                    return result * 2; // 正常时加工结果
                });
        System.out.println("handle结果:" + future2.get()); // 输出400
    }
}
4. 常用:任务组合

实际开发中常需要"并行执行多个任务,汇总结果",核心用allOf(全部完成)、anyOf(任意一个完成)。

Java 复制代码
public class CompletableFutureCombine {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1. allOf:等待所有任务完成(无返回值,需手动获取每个任务结果)
        CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "任务1结果");
        CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "任务2结果");
        CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> "任务3结果");

        CompletableFuture<Void> allFuture = CompletableFuture.allOf(task1, task2, task3);
        allFuture.get(); // 等待所有任务完成

        // 获取每个任务的结果
        String result1 = task1.get();
        String result2 = task2.get();
        String result3 = task3.get();
        System.out.println("allOf汇总结果:" + result1 + "," + result2 + "," + result3);

        // 2. anyOf:任意一个任务完成就返回(返回第一个完成的结果)
        CompletableFuture<String> fastTask = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(500); // 最快完成
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "快任务结果";
        });
        CompletableFuture<String> slowTask = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "慢任务结果";
        });

        CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(fastTask, slowTask);
        Object anyResult = anyFuture.get();
        System.out.println("anyOf结果:" + anyResult); // 输出"快任务结果"
    }
}
5. 进阶:自定义线程池(避免共用线程池耗尽)

默认的commonPool是所有异步任务共用的,高并发下可能耗尽,建议自定义线程池:

Java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CompletableFutureCustomPool {
    // 自定义线程池(核心数=CPU核心数,新手先这么配)
    private static final ExecutorService CUSTOM_POOL = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    public static void main(String[] args) {
        // 创建任务时指定自定义线程池
        CompletableFuture.supplyAsync(() -> {
            System.out.println("自定义线程池执行:" + Thread.currentThread().getName());
            return "自定义线程池结果";
        }, CUSTOM_POOL)
        .thenAccept(System.out::println);

        // 程序结束前关闭线程池(实际项目中线程池通常是单例,不用手动关)
        CUSTOM_POOL.shutdown();
    }
}

三、总结

作为Java新手,掌握以下3个核心点就能应对80%的CompletableFuture使用场景:

  1. 创建任务runAsync(无返回值)、supplyAsync(有返回值),优先用自定义线程池;

  2. 回调处理thenApply(加工结果)、thenAccept(消费结果),替代get()避免阻塞;

  3. 异常与组合 :用exceptionally兜底异常,用allOf/anyOf组合多任务。

不用死记所有API,先把上面的示例跑通、理解,遇到具体场景再查对应方法即可------这就是最适合新手的"速成"方式。

相关推荐
九皇叔叔2 小时前
application.yml 文件无 Spring 图标 + 无自动提示
java·spring boot·spring
飞机和胖和黄2 小时前
考研之C语言第二周作业
c语言·开发语言·考研
输出输入2 小时前
MT4 EA 设计一次一单方法
开发语言
一起养小猫2 小时前
OpenHarmony 实战中的 Flutter:深入理解 Widget 核心概念与底层原理
开发语言·flutter
盐真卿2 小时前
python第四部分:模块(每日更新)
开发语言·python
马猴烧酒.2 小时前
JAVA后端用户登录与鉴权详解
java·数据库·sql
乐之者v2 小时前
软件开发常规流程的版本缩写
java
猪八戒1.02 小时前
L C D
开发语言·stm32
DN20202 小时前
靠谱的AI销售机器人哪家好
java·人工智能·机器人