关于常用线程池CompletableFuture和Future的介绍

目录

1、Future

1.1、定义

1.2、常用方法:

2、CompletableFuture

2.1、设计背景

2.2、常用方法

3、区别

3.1、行为上的具体差别

3.2、阻塞 vs 非阻塞

3.3、线程池使用差别

3.4、异常处理的差别

3.5、使用对比


前言

        它们都是异步执行任务,但设计目标、能力范围和编程体验有很大不同,我帮你详细拆开来看,对比到“为什么很多人新项目更倾向于 CompletableFuture”。

本质上是在对比两种提交异步任务的方式:

  •  ExecutorService.submit(...)(返回 Future)
  •  CompletableFuture.supplyAsync(...)(返回 CompletableFuture)

一句话记忆

Future 是“我去等你做完再干别的”,
CompletableFuture 是“你做好了自动告诉我,然后我继续做下去”。


1、Future

1.1、定义

        Future是Java5新加的一个接口,它提供了一种异步并行计算的功能。如果主线程需要执行一个很耗时的计算任务,就可以通过Future把这个任务放到异步线程中执行

        主线程继续处理其他任务或者先行结束,再通过Future获取计算结果。future+线程池异步多线程任务配合,能显著提高程序的执行效率。

1.2、常用方法:

1、boolean cancel(boolean mayInterruptIfRunning)

        试图取消对此任务的执行。如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败。当调用 cancel 时,如果调用成功,而此任务尚未启动,则此任务将永不运行。如果任务已经启动,则 mayInterruptIfRunning 参数确定是否应该以试图停止任务的方式来中断执行此任务的线程。

①参数:

mayInterruptIfRunning - 如果应该中断执行此任务的线程,则为 true;允许正在运行的任务运行完成, 则为false

②返回:

如果无法取消任务,则返回 false,这通常是由于它已经正常完成;否则返回 true

2、V get()

如有必要,等待计算完成,然后获取其结果。

3、V get(long timeout, TimeUnit unit)

如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。

4、boolean isCancelled()

如果在任务正常完成前将其取消,则返回 true。

5、boolean isDone()

如果任务已完成,则返回 true。

代码示例:

public class FutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
//      1.获取线程池对象
        ExecutorService es = Executors.newCachedThreadPool();
//      2.创建Callable类型的任务对象
        Future<Integer> f = es.submit(new MyCall(1, 1));
//      3.判断任务是否已经完成
        boolean done = f.isDone();
        System.out.println("判断任务是否完成"+done);
//        4.任务执行的结果
        Integer v = f.get();
        System.out.println("任务执行的结果是"+v);
//        5.判断取消任务的结果
        boolean b = f.cancel(true);
        System.out.println("取消任务执行的结果"+b);
    } }

class MyCall implements Callable<Integer> {
    private int a;
    private int b;
    public MyCall(int a, int b) {
        this.a = a;
        this.b = b;}
    @Override
    public Integer call() throws Exception {
        String name = Thread.currentThread().getName();
        System.out.println(name + "准备开始计算...");
        Thread.sleep(2000);
        System.out.println("计算完成...");
        return a + b;
    } }

ExecutorService + Future

ExecutorService threadPool = Executors.newFixedThreadPool(3);
Future<?> future = threadPool.submit(() -> {
    System.out.println("任务执行");
});

// Future 只能阻塞式获取结果
Object result = future.get(); // 阻塞直到结束

        Future接口作为Java中处理异步任务的一种方式,提供了获取结果、取消任务等方法,但存在阻塞和轮询的缺点。


2、CompletableFuture

2.1、设计背景

         get()方法在Future计算完成之前会一直处在阻塞状态下,阻塞的方式和异步编程的设计理念相违背;而isDone()轮询的方式会消耗无谓的CPU资源,而且也不见的能及时地得到计算结果。

        对于真正的异步处理我们希望是可以通过传入回调参数,在future结束时自动调用该回调函数,这样我们就不用等待结果。因此,JDK8设计出了CompletableFuture。

        CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方。

       

2.2、常用方法

CompletableFuture.supplyAsync(...)

CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
    System.out.println("任务执行");
    return "任务结果";
});

// 可以链式异步处理,不阻塞主线程
cf.thenAccept(result -> System.out.println("结果: " + result));

        CompletableFuture为了解决Future的不足,提供了非阻塞的解决方案,通过CompletionStage和静态方法如runAsync和supplyAsync支持回调和链式操作,提高了异步编程的灵活性。


3、区别

如下所示:

3.1、行为上的具体差别

1、Future(ExecutorService方式)

  • submit()提交任务 → 返回 Future
  • get() 会阻塞直到任务结束(同步等待结果)
  • 如果你不调用get(),就相当于只是异步跑了个任务,但没法在任务结束时主动触发后续逻辑
  • 适合**"跑完就拿结果"**的场景

例子:

ExecutorService pool = Executors.newFixedThreadPool(3);
Future<Integer> f1 = pool.submit(() -> 100);
System.out.println(f1.get()); // 阻塞拿结果

2、CompletableFuture 方式

  • supplyAsync() 提交任务 → 返回 CompletableFuture
  • 你可以在 future 完成后自动触发回调,而且线程切换和数据传递内置支持
  • 基于函数式 API,可以写成任务编排、组合的风格

例子:

CompletableFuture.supplyAsync(() -> 100)
                 .thenApply(res -> res * 2) // 链式处理
                 .thenAccept(System.out::println); // 最终消费

输出:
200

整个过程不用自己管理阻塞和线程切换,比 Future 灵活得多。


3.2、阻塞 vs 非阻塞

Future

获取结果必须 get()(阻塞当前线程)

CompletableFuture

可以用 thenXXX 在另一个线程里异步接收结果,不一定阻塞主线程

代码示例如下:

// Future 阻塞示例
Future<Integer> f = pool.submit(() -> 1+1);
System.out.println(f.get()); // 当前线程卡着等结果

// CompletableFuture 非阻塞示例
CompletableFuture.supplyAsync(() -> 1+1)
    .thenAccept(System.out::println); // 主线程可继续做别的


3.3、线程池使用差别

1、Future

必须自己传一个 ExecutorService(比如 Executors.newFixedThreadPool)。

2、CompletableFuture

如果不指定线程池,默认用 ForkJoinPool.commonPool()

也可以这样自定义:

CompletableFuture.supplyAsync(() -> 123, myExecutor);

3.4、异常处理的差别

Future

try {
    future.get();
} catch (ExecutionException e) {
    e.printStackTrace();
}

只能用 try-catch,在 get() 时集中处理。

CompletableFuture

CompletableFuture.supplyAsync(() -> {
    if (true) throw new RuntimeException("出错了");
    return 123;
}).exceptionally(ex -> {
    System.out.println("异常: " + ex.getMessage());
    return -1;
});

3.5、使用对比

你的代码:

ExecutorService threadPool = Executors.newFixedThreadPool(3);
Future<?> future = threadPool.submit(() -> {
    System.out.println("111");
});



CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    System.out.println("111");
});

区别

1、submit

返回一个 Future,不能直接链式加后续逻辑,要等它完成用 get() 再处理。

2、CompletableFuture)

可以直接 .thenRun(...)、.thenApply(...)接后续逻辑,避免阻塞,也更适合任务组合。


总结

1、ExecutorService + Future

偏底层;适合“一次提交一次取结果”的简单异步;

必须手动拿结果、处理异常;

任务之间的依赖、组合要自己写控制逻辑

2、CompletableFuture

偏高层 & 响应式;

支持链式任务编排(组合、依赖、异常处理);

适合复杂异步流程;对比 Future 减少了阻塞等待;


参考文章

1、Future、CompletableFuture概述_completablefuture和future-CSDN博客文章浏览阅读2.7k次,点赞4次,收藏5次。文章介绍了同步和异步的概念,强调了异步执行在处理耗时任务时的重要性。Future接口作为Java中处理异步任务的一种方式,提供了获取结果、取消任务等方法,但存在阻塞和轮询的缺点。CompletableFuture为了解决Future的不足,提供了非阻塞的解决方案,通过CompletionStage和静态方法如runAsync和supplyAsync支持回调和链式操作,提高了异步编程的灵活性。 https://blog.csdn.net/y516369/article/details/128639717?ops_request_misc=%257B%2522request%255Fid%2522%253A%252238f0912cc8025c2f436ac4d4d3c3918e%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=38f0912cc8025c2f436ac4d4d3c3918e&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-128639717-null-null.142^v102^control&utm_term=Future%E5%92%8CCompletableFuture&spm=1018.2226.3001.4187

<think>好的,用户想了解CompletableFuture常用方法及使用示例。我需要根据提供的引用内容来整理这些方法,并给出对应的示例。首先,我应该先确认用户提供的引用内容里提到的哪些方法,然后按照逻辑顺序组织这些方法,确保每个方法都有清晰的说明示例代码。 首先,用户提供的引用中提到了thenCombine、allOf、get()方法、thenAccept等。可能需要将这些方法分门别类,比如创建异步任务、结果处理、组合多个Future、异常处理等。不过根据引用内容,可能重点放在常用的方法如thenCombine、allOf、thenAccept以及异步执行阻塞获取结果的方法上。 接下来,我需要确保每个方法的说明准确,并引用对应的引用来源。例如,thenCombine方法用于合并两个CompletableFuture的结果,这在引用[1]中提到了。然后allOf方法在引用[1]可能的其他引用中有示例。get()方法在引用[2][3]中有说明,需要注意阻塞的问题以及添加超时的建议。thenAccept方法在引用[4]中详细说明了消费结果的用法。 然后,我需要生成相关的示例代码,确保代码符合Java语法,并正确展示每个方法的使用场景。例如,thenCombine的示例可能需要两个CompletableFuture,然后合并它们的结果。allOf的示例需要展示如何等待多个Future完成。对于thenAccept,需要展示如何处理前一个阶段的结果。 另外,用户可能对异步执行的方法感兴趣,比如supplyAsyncrunAsync,这在引用[2]中提到了静态方法用于异步执行。需要补充这些创建阶段的常用方法。 还需要注意异常处理的方法,比如exceptionally,但用户提供的引用中没有提到,但作为常用方法可能需要简要提及,或者如果引用中没有相关内容,可能不包含。不过根据用户提供的引用,可能需要专注于提到的那些方法。 最后,生成相关问题的时候,要围绕CompletableFuture的其他方法、性能优化、应用场景等,比如如何处理异常、如何组合多个Future的不同方式,或者比较不同方法的区别等。 需要确保回答结构清晰,分点列出每个方法,每个方法都有说明、示例对应的引用标注。同时,示例代码要正确,并符合Java语法,特别是涉及lambda表达式CompletableFuture链式调用的部分。 可能还需要注意用户提到的使用中文回答,所以所有的说明示例注释都要用中文。另外,注意使用正确的LaTeX格式,但根据用户的问题,可能数学公式较少,主要是在代码块文本说明上。 现在开始组织内容,先介绍CompletableFuture的基本用途,然后分点列出常用方法,每个方法下给出说明示例代码,并标注引用来源。最后生成相关问题。</think>### CompletableFuture常用方法及使用示例 #### 一、创建异步任务 1. **supplyAsync** 用于创建带返回值的异步任务,默认使用`ForkJoinPool`执行。 ```java CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { return "异步任务结果"; }); System.out.println(future.get()); // 输出:异步任务结果 ``` 2. **runAsync** 用于执行无返回值的异步任务。 ```java CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { System.out.println("执行无返回值的任务"); }); future.get(); // 等待任务完成 ``` --- #### 二、结果处理 1. **thenApply** 对前序任务结果进行同步转换。 ```java CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 10) .thenApply(result -> result * 2); System.out.println(future.get()); // 输出:20 ``` 2. **thenAccept** 消费前序任务结果,无返回值。 ```java CompletableFuture.supplyAsync(() -> "Hello") .thenAccept(result -> System.out.println("消费结果:" + result)); // 输出:消费结果:Hello ``` 引用自结果消费方法[^4]。 3. **exceptionally** 捕获异常并返回默认值。 ```java CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { throw new RuntimeException("模拟异常"); }).exceptionally(ex -> { System.out.println("捕获异常:" + ex.getMessage()); return 0; }); System.out.println(future.get()); // 输出:0 ``` --- #### 三、组合多个任务 1. **thenCombine** 合并两个独立任务的结果。 ```java CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello"); CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World"); CompletableFuture<String> combined = future1.thenCombine(future2, (a, b) -> a + " " + b); System.out.println(combined.get()); // 输出:Hello World ``` 引用自结果合并场景[^1]。 2. **allOf** 等待多个任务全部完成。 ```java CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Task1"); CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Task2"); CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2); all.get(); // 阻塞直到所有任务完成 System.out.println(future1.get() + ", " + future2.get()); // 输出:Task1, Task2 ``` 引用自多任务合并方法[^1]。 --- #### 四、阻塞获取结果 使用`get()`方法时需注意阻塞问题,建议添加超时: ```java CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Data"); String result = future.get(5, TimeUnit.SECONDS); // 添加超时避免无限阻塞 ``` 引用自阻塞获取结果的注意事项[^3]。 --- #### 五、线程池配置 指定自定义线程池避免资源竞争: ```java ExecutorService executor = Executors.newFixedThreadPool(2); CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { return "使用自定义线程池"; }, executor); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值