首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >04-CompletableFuture异步线程 性能

04-CompletableFuture异步线程 性能

作者头像
彼岸舞
发布于 2022-05-10 03:14:52
发布于 2022-05-10 03:14:52
4030
举报

场景

场景

方法

任务少, 不频繁

直接使用线程

任务数稳定,频繁

使用线程池

线程池

优点

  • 不用频繁的创建和销毁线程
  • 不需要担心OOM
  • 直接往任务队列添加任务即可
  • 核心线程忙不过来,可以自动增加到最大线程数

构造参数

  • 核心线程数
  • 最大线程数
  • 空闲活跃时长
  • 时长单位
  • 阻塞队列
  • 线程工厂
  • 拒绝策略
    • 直接丢弃
    • 替换最后一个
    • 抛异常
    • 谁提交的任务谁执行
    • --- 自行 扩展

工具类

Executors

方法

描述

newSingleThreadExecutor

创建一个单线程的线程池

newCachedThreadPool

创建一个无上限的线程池(Integer.MAX)

newFixedThreadPool

创建一个固定线程数的线程池

需求

小白和他的朋友门,连续输了10几把游戏, 决定去餐厅吃饭了,3个人,直接点了10盘菜,决定化悲愤为食量

实现

编写代码

先将之前的公共方法抽成一个工具类

代码语言:javascript
AI代码解释
复制
package com.dance;

import java.util.StringJoiner;

public class SmallTool {
    /**
     * 休眠方法
     * @param millis 毫秒
     */
    public static void sleep(long millis){
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 打印方法
     * @param text 文本
     */
    public static void print(String text){
        String str = new StringJoiner("\t|\t")
                .add(String.valueOf(System.currentTimeMillis()))
                .add(String.valueOf(Thread.currentThread().getId()))
                .add(Thread.currentThread().getName())
                .add(text)
                .toString();
        System.out.println(str);
    }
}

创建菜类

代码语言:javascript
AI代码解释
复制
package com.dance;

import java.util.concurrent.TimeUnit;

/**
 * 菜
 */
public class Dish {
    /**
     * 菜名
     */
    private final String name;
    /**
     * 用时(秒)
     */
    private final Integer productionTime;

    public Dish(String name, Integer productionTime) {
        this.name = name;
        this.productionTime = productionTime;
    }

    /**
     * 做菜
     */
    public void make(){
        SmallTool.sleep(TimeUnit.SECONDS.toMillis(productionTime));
        SmallTool.print(name + "制作完毕 来吃我吧!");
    }
}

编写过程

代码语言:javascript
AI代码解释
复制
@Test
public void testOne(){

    SmallTool.print("小白和小伙伴门 进餐厅点菜");

    long startTime = System.currentTimeMillis();

    ArrayList<Dish> dishes = new ArrayList<>();

    // 点菜
    for (int i = 1; i <= 10; i++) {
        dishes.add(new Dish("菜" + i, 1));
    }
    // 做菜
    dishes.forEach(dish -> CompletableFuture.runAsync(dish::make).join());

    SmallTool.print("菜都做好了, 上桌 " + (System.currentTimeMillis() - startTime));

}

执行结果

代码语言:javascript
AI代码解释
复制
1649519841265    |    1    |    main    |    小白和小伙伴门 进餐厅点菜
1649519842281    |    24    |    ForkJoinPool.commonPool-worker-19    |1制作完毕 来吃我吧!
1649519843286    |    24    |    ForkJoinPool.commonPool-worker-19    |2制作完毕 来吃我吧!
1649519844294    |    24    |    ForkJoinPool.commonPool-worker-19    |3制作完毕 来吃我吧!
1649519845300    |    24    |    ForkJoinPool.commonPool-worker-19    |4制作完毕 来吃我吧!
1649519846307    |    24    |    ForkJoinPool.commonPool-worker-19    |5制作完毕 来吃我吧!
1649519847313    |    24    |    ForkJoinPool.commonPool-worker-19    |6制作完毕 来吃我吧!
1649519848319    |    24    |    ForkJoinPool.commonPool-worker-19    |7制作完毕 来吃我吧!
1649519849326    |    24    |    ForkJoinPool.commonPool-worker-19    |8制作完毕 来吃我吧!
1649519850333    |    24    |    ForkJoinPool.commonPool-worker-19    |9制作完毕 来吃我吧!
1649519851339    |    24    |    ForkJoinPool.commonPool-worker-19    |10制作完毕 来吃我吧!
1649519851343    |    1    |    main    |    菜都做好了, 上桌 10075

好像没什么问题, 但是这样的话, 一个一个调用join,硬是把多线程玩成了单线程~

代码改造

代码语言:javascript
AI代码解释
复制
@Test
public void testTwo(){

    SmallTool.print("小白和小伙伴门 进餐厅点菜");

    long startTime = System.currentTimeMillis();

    ArrayList<Dish> dishes = new ArrayList<>();

    // 点菜
    for (int i = 1; i <= 10; i++) {
        dishes.add(new Dish("菜" + i, 1));
    }

    ArrayList<CompletableFuture<Void>> completableFutures = new ArrayList<>();

    // 做菜 将所有线程引用收集
    dishes.forEach(dish -> completableFutures.add(CompletableFuture.runAsync(dish::make)));

    // 将所有线程统一join
    CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).join();

    SmallTool.print("菜都做好了, 上桌 " + (System.currentTimeMillis() - startTime));

}

执行结果

代码语言:javascript
AI代码解释
复制
1649520172283    |    1    |    main    |    小白和小伙伴门 进餐厅点菜
1649520173305    |    30    |    ForkJoinPool.commonPool-worker-31    |7制作完毕 来吃我吧!
1649520173305    |    27    |    ForkJoinPool.commonPool-worker-23    |3制作完毕 来吃我吧!
1649520173305    |    32    |    ForkJoinPool.commonPool-worker-3    |9制作完毕 来吃我吧!
1649520173305    |    28    |    ForkJoinPool.commonPool-worker-27    |5制作完毕 来吃我吧!
1649520173305    |    26    |    ForkJoinPool.commonPool-worker-9    |4制作完毕 来吃我吧!
1649520173305    |    24    |    ForkJoinPool.commonPool-worker-19    |2制作完毕 来吃我吧!
1649520173305    |    25    |    ForkJoinPool.commonPool-worker-5    |1制作完毕 来吃我吧!
1649520173305    |    29    |    ForkJoinPool.commonPool-worker-13    |6制作完毕 来吃我吧!
1649520173305    |    33    |    ForkJoinPool.commonPool-worker-21    |10制作完毕 来吃我吧!
1649520173305    |    31    |    ForkJoinPool.commonPool-worker-17    |8制作完毕 来吃我吧!
1649520173335    |    1    |    main    |    菜都做好了, 上桌 1049

哇咔咔, 不得了呀, 原本10秒的事情, 居然只用了一秒

使用Stream优化代码

代码语言:javascript
AI代码解释
复制
@Test
public void testTwo(){

    SmallTool.print("小白和小伙伴门 进餐厅点菜");

    long startTime = System.currentTimeMillis();

    /*
        1: 生成1 - 10 的数字
        2: 创建10盘菜
        3: 提交runAsync 并且执行make
        4: 转换为数组
        5: 执行allOf
        6: 执行统一join
     */
    CompletableFuture.allOf(IntStream.range(1, 10)
            .mapToObj(i -> new Dish("菜" + i, 1))
            .map(dish -> CompletableFuture.runAsync(dish::make))
            .toArray(CompletableFuture[]::new)).join();

    SmallTool.print("菜都做好了, 上桌 " + (System.currentTimeMillis() - startTime));

}

瞬间一大片代码变成了一句, emmm, 我则么没有这么吊,当然执行结果是一样的

需求进化

如果小白现在突然想点20盘菜呢?

需求点:

  • 任务巨多, 如何保证性能
  • 如何观察任务, 调度情况
  • 线程复用问题

实现

编写代码

其实就是将上一个例子的10改为20而已

执行结果

代码语言:javascript
AI代码解释
复制
1649521040000    |    1    |    main    |    小白和小伙伴门 进餐厅点菜
1649521041025    |    37    |    ForkJoinPool.commonPool-worker-29    |14制作完毕 来吃我吧!
1649521041025    |    30    |    ForkJoinPool.commonPool-worker-31    |6制作完毕 来吃我吧!
1649521041025    |    36    |    ForkJoinPool.commonPool-worker-11    |13制作完毕 来吃我吧!
1649521041025    |    27    |    ForkJoinPool.commonPool-worker-9    |4制作完毕 来吃我吧!
1649521041025    |    34    |    ForkJoinPool.commonPool-worker-7    |11制作完毕 来吃我吧!
1649521041025    |    31    |    ForkJoinPool.commonPool-worker-17    |9制作完毕 来吃我吧!
1649521041025    |    35    |    ForkJoinPool.commonPool-worker-25    |12制作完毕 来吃我吧!
1649521041025    |    24    |    ForkJoinPool.commonPool-worker-19    |2制作完毕 来吃我吧!
1649521041025    |    28    |    ForkJoinPool.commonPool-worker-27    |5制作完毕 来吃我吧!
1649521041025    |    25    |    ForkJoinPool.commonPool-worker-5    |1制作完毕 来吃我吧!
1649521041025    |    26    |    ForkJoinPool.commonPool-worker-23    |3制作完毕 来吃我吧!
1649521041025    |    29    |    ForkJoinPool.commonPool-worker-13    |7制作完毕 来吃我吧!
1649521041025    |    38    |    ForkJoinPool.commonPool-worker-15    |15制作完毕 来吃我吧!
1649521041025    |    33    |    ForkJoinPool.commonPool-worker-21    |10制作完毕 来吃我吧!
1649521041025    |    32    |    ForkJoinPool.commonPool-worker-3    |8制作完毕 来吃我吧!
1649521042040    |    30    |    ForkJoinPool.commonPool-worker-31    |16制作完毕 来吃我吧!
1649521042040    |    28    |    ForkJoinPool.commonPool-worker-27    |18制作完毕 来吃我吧!
1649521042040    |    26    |    ForkJoinPool.commonPool-worker-23    |19制作完毕 来吃我吧!
1649521042040    |    25    |    ForkJoinPool.commonPool-worker-5    |17制作完毕 来吃我吧!
1649521042048    |    1    |    main    |    菜都做好了, 上桌 2040

可以看的出来, 执行的线程重复了, 用时2ms, 为什么呢? 核心池的最大是15, 应为这个和你电脑的CPU核心数有关, 我电脑是8核16线程的, ForkJoinPool的最大线程数 默认应该是最大线程数-1

我们看一下

代码语言:javascript
AI代码解释
复制
@Test
public void testForkJoinPool(){
    // 电脑支持的最大线程数
    System.out.println(Runtime.getRuntime().availableProcessors());
    // 通用池 当前大小
    System.out.println(ForkJoinPool.commonPool().getPoolSize());
    // 通用池最大线程数
    System.out.println(ForkJoinPool.getCommonPoolParallelism());
}

结果

代码语言:javascript
AI代码解释
复制
16
0
15

这个时候就需要用到线程池来解决了,之前CompeletableFuture的方法总结中带Async的后缀的方法, 其实都是可以多传入一个参数的,那就是指定线程池, 如果不指定,默认使用的线程池就是ForkJoinPool.commonPool从名字也可以看出,这是ForkJoin的池

改进一

将通用池的线程数设置为合适大小

代码语言:javascript
AI代码解释
复制
@Test
public void testThree(){

    // -Djava.util.concurrent.ForkJoinPool.common.parallelism=20
    System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "20");

    SmallTool.print("小白和小伙伴门 进餐厅点菜");

    long startTime = System.currentTimeMillis();

    CompletableFuture.allOf(IntStream.range(1, 20)
            .mapToObj(i -> new Dish("菜" + i, 1))
            .map(dish -> CompletableFuture.runAsync(dish::make))
            .toArray(CompletableFuture[]::new)).join();

    SmallTool.print("菜都做好了, 上桌 " + (System.currentTimeMillis() - startTime));

}

执行结果

代码语言:javascript
AI代码解释
复制
1649522125623    |    1    |    main    |    小白和小伙伴门 进餐厅点菜
1649522126645    |    35    |    ForkJoinPool.commonPool-worker-25    |12制作完毕 来吃我吧!
1649522126645    |    34    |    ForkJoinPool.commonPool-worker-39    |11制作完毕 来吃我吧!
1649522126645    |    28    |    ForkJoinPool.commonPool-worker-59    |5制作完毕 来吃我吧!
1649522126645    |    41    |    ForkJoinPool.commonPool-worker-5    |18制作完毕 来吃我吧!
1649522126645    |    29    |    ForkJoinPool.commonPool-worker-45    |6制作完毕 来吃我吧!
1649522126645    |    40    |    ForkJoinPool.commonPool-worker-19    |17制作完毕 来吃我吧!
1649522126645    |    24    |    ForkJoinPool.commonPool-worker-51    |2制作完毕 来吃我吧!
1649522126645    |    26    |    ForkJoinPool.commonPool-worker-9    |4制作完毕 来吃我吧!
1649522126645    |    25    |    ForkJoinPool.commonPool-worker-37    |1制作完毕 来吃我吧!
1649522126645    |    37    |    ForkJoinPool.commonPool-worker-61    |14制作完毕 来吃我吧!
1649522126645    |    27    |    ForkJoinPool.commonPool-worker-23    |3制作完毕 来吃我吧!
1649522126645    |    33    |    ForkJoinPool.commonPool-worker-53    |10制作完毕 来吃我吧!
1649522126645    |    32    |    ForkJoinPool.commonPool-worker-3    |9制作完毕 来吃我吧!
1649522126645    |    42    |    ForkJoinPool.commonPool-worker-55    |19制作完毕 来吃我吧!
1649522126645    |    36    |    ForkJoinPool.commonPool-worker-11    |13制作完毕 来吃我吧!
1649522126645    |    30    |    ForkJoinPool.commonPool-worker-31    |7制作完毕 来吃我吧!
1649522126645    |    38    |    ForkJoinPool.commonPool-worker-47    |15制作完毕 来吃我吧!
1649522126645    |    39    |    ForkJoinPool.commonPool-worker-33    |16制作完毕 来吃我吧!
1649522126645    |    31    |    ForkJoinPool.commonPool-worker-17    |8制作完毕 来吃我吧!
1649522126649    |    1    |    main    |    菜都做好了, 上桌 1023

可以看到,又回到1ms了, 但是这个值到底要设置为多少才合适呢?

答案是都不合适

原因

  • 从名字可以看出ForkJoinPool, 显然这个池并不只为CompeletableFuture服务
  • 只有在启动之前,初始化的时候才可以设置
  • 需要从项目的长期使用量才可以得出

改进二(推荐)

自定义线程池, 这个时候,就又说会上面的第二个参数了,没错 ,那就是Executor

为什么推荐呢?

原因

  • 隔离, 防止影响其他使用ForkJoinPool的代码
  • 方便控制, ForkJoinPool在初始化后, 不可以修改, 但是自定义的线程池可以在任务数量来之后, 通过计算得出线程的数量

这里采用无上限线程池演示

代码语言:javascript
AI代码解释
复制
@Test
public void testFour(){

    SmallTool.print("小白和小伙伴门 进餐厅点菜");

    long startTime = System.currentTimeMillis();

    // 创建线程池
    final ExecutorService threadPool = Executors.newCachedThreadPool();

    CompletableFuture.allOf(IntStream.range(1, 20)
            .mapToObj(i -> new Dish("菜" + i, 1))
            .map(dish -> CompletableFuture.runAsync(dish::make, threadPool))
            .toArray(CompletableFuture[]::new)).join();

    // 销毁
    threadPool.shutdown();

    SmallTool.print("菜都做好了, 上桌 " + (System.currentTimeMillis() - startTime));

}

执行结果

代码语言:javascript
AI代码解释
复制
1649522607939    |    1    |    main    |    小白和小伙伴门 进餐厅点菜
1649522608962    |    42    |    pool-1-thread-19    |19制作完毕 来吃我吧!
1649522608963    |    26    |    pool-1-thread-3    |3制作完毕 来吃我吧!
1649522608963    |    25    |    pool-1-thread-2    |2制作完毕 来吃我吧!
1649522608963    |    29    |    pool-1-thread-6    |6制作完毕 来吃我吧!
1649522608963    |    31    |    pool-1-thread-8    |8制作完毕 来吃我吧!
1649522608963    |    24    |    pool-1-thread-1    |1制作完毕 来吃我吧!
1649522608963    |    30    |    pool-1-thread-7    |7制作完毕 来吃我吧!
1649522608963    |    39    |    pool-1-thread-16    |16制作完毕 来吃我吧!
1649522608962    |    40    |    pool-1-thread-17    |17制作完毕 来吃我吧!
1649522608963    |    28    |    pool-1-thread-5    |5制作完毕 来吃我吧!
1649522608962    |    27    |    pool-1-thread-4    |4制作完毕 来吃我吧!
1649522608962    |    38    |    pool-1-thread-15    |15制作完毕 来吃我吧!
1649522608962    |    34    |    pool-1-thread-11    |11制作完毕 来吃我吧!
1649522608962    |    32    |    pool-1-thread-9    |9制作完毕 来吃我吧!
1649522608962    |    37    |    pool-1-thread-14    |14制作完毕 来吃我吧!
1649522608962    |    33    |    pool-1-thread-10    |10制作完毕 来吃我吧!
1649522608962    |    36    |    pool-1-thread-13    |13制作完毕 来吃我吧!
1649522608962    |    41    |    pool-1-thread-18    |18制作完毕 来吃我吧!
1649522608962    |    35    |    pool-1-thread-12    |12制作完毕 来吃我吧!
1649522608973    |    1    |    main    |    菜都做好了, 上桌 1028

没错,还是1ms, 但是通过名称可以看出, 使用了我们自定义的线程池

作者:彼岸舞

时间:2022\04\11

内容关于:CompeletableFuture

本文来源于网络,只做技术分享,一概不负任何责任

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-04-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
01-CompletableFuture异步线程 入门
前言 emmm, 有好长一段时间没有发布新的文章了,最后一篇发布还是1月21日,到现在已经快3个月了,这段时间我去干嘛了呢? 1: 学习数据结构与算法, 但是还没有学完,打算等学习完毕后再给大家分享
彼岸舞
2022/05/10
2780
01-CompletableFuture异步线程 入门
CompletableFuture介绍
一个Future类是显示的完成,而且能被用作一个完成等级,通过它的完成触发支持的依赖函数和行为。当两个或多个线程要执行完成或取消操作时,只有一个能够成功。
HLee
2021/07/12
2.2K0
CompletableFuture介绍
异步编程利器:CompletableFuture详解
最近刚好使用CompeletableFuture优化了项目中的代码,所以跟大家一起学习CompletableFuture。
捡田螺的小男孩
2021/06/15
7.3K0
异步编程利器:CompletableFuture详解
异步编程利器 CompletableFuture 玩法详解
在上篇文章中,我们介绍了Future相关的用法,使用它可以获取异步任务执行的返回值。
Java极客技术
2024/01/17
4840
异步编程利器 CompletableFuture 玩法详解
异步编程好帮手之CompletableFuture详解
我们异步执行一个任务时,一般是用线程池Executor去创建。如果不需要有返回值, 任务实现Runnable接口;如果需要有返回值,任务实现Callable接口,调用Executor的submit方法,再使用Future获取即可。如果多个线程存在依赖组合的话,我们怎么处理呢?可使用同步组件CountDownLatch、CyclicBarrier等,但是比较麻烦。其实有简单的方法,就是用CompeletableFuture。最近刚好使用CompeletableFuture优化了项目中的代码,所以跟大家一起学习CompletableFuture。
翎野君
2023/08/10
5470
CompletableFuture 异步多线程,那叫一个优雅
虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,我们必须使用Future.get()的方式阻塞调用线程,或者使用轮询方式判断 Future.isDone 任务是否结束,再获取结果。
程序员大彬
2023/03/01
1.9K0
CompletableFuture 异步多线程,那叫一个优雅
CompletableFuture详解
因为CompletableFuture实现了Future接口所以先看一下Future
用户10136162
2022/11/15
1.2K0
CompletableFuture详解
02-CompletableFuture异步线程 进阶
小白吃完饭后要求服务员开发票, 这个时候小白接到了,回家开黑的电话,服务员开好发票后,小白拿着回家了
彼岸舞
2022/05/10
2870
JAVA使用CompletableFuture实现流水线并行处理,加速你的接口响应
在项目开发中,后端服务对外提供API接口一般都会关注响应时长。但是某些情况下,由于业务规划逻辑的原因,我们的接口可能会是一个聚合信息处理类的处理逻辑,比如我们从多个不同的地方获取数据,然后汇总处理为最终的结果再返回给调用方,这种情况下,往往会导致我们的接口响应特别的慢。
是Vzn呀
2022/08/05
2.1K0
JAVA使用CompletableFuture实现流水线并行处理,加速你的接口响应
JUC并发—13.Future模式和异步编程简介
Callable接口需要与Future和ExecutorService结合使用:通过ExecutorService的submit()方法提交一个实现Callable接口的任务,然后ExecutorService的submit()方法会返回一个实现Future接口的对象,接着调用Future接口的get()方法就可以获取异步任务的结果。
东阳马生架构
2025/05/06
1620
Reactor深度探索 顶
在Reactor官方的网站上,指出了现有编程的一些不足https://projectreactor.io/docs/core/release/reference/index.html#_blocking_can_be_wasteful
算法之名
2020/02/11
1K0
Reactor深度探索
                                                                            顶
CompletableFuture:supplyAsync与runAsync
CompletableFuture是Java 8中引入的一个类,用于简化异步编程和并发操作。它提供了一种方便的方式来处理异步任务的结果,以及将多个异步任务组合在一起执行。CompletableFuture支持链式操作,使得异步编程更加直观和灵活。
不惑
2023/11/14
1.6K0
【JAVA 的异步编程】CompletableFuture的介绍
缘起: 一、Future java5引入了Future模式。Future接口是Java多线程Future模式的实现,在java.util.concurrent包中,可以来进行异步计算。 Future模式是多线程设计常用的一种设计模式。Future模式可以理解成:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。 Future的接口的五个方法。
用户5640963
2021/06/17
9310
【JAVA 的异步编程】CompletableFuture的介绍
【小家Java】Future、FutureTask、CompletionService、CompletableFuture解决多线程并发中归集问题的效率对比
开启线程执行任务,不管是使用Runnable(无返回值不支持上报异常)还是Callable(有返回值支持上报异常)接口,都可以轻松实现。那么如果是开启线程池并需要获取结果归集的情况下,如何实现,以及优劣?
YourBatman
2019/09/03
2.9K1
【小家Java】Future、FutureTask、CompletionService、CompletableFuture解决多线程并发中归集问题的效率对比
异步编程 - 05 基于JDK中的Future实现异步编程(中)_CompletableFuture
Java8 - 使用工厂方法 supplyAsync创建 CompletableFuture
小小工匠
2023/09/09
4780
CompletableFuture常用用法及踩坑
作为常用的并发类,CompletableFuture在项目中会经常使用,其作用与Google的ListenableFuture类似;
benym
2022/07/14
5K0
理解Java8里面CompletableFuture异步编程
其中第三个特性,就是今天我们想要聊的话题,正是因为CompletableFuture的出现,才使得使用Java进行异步编程提供了可能。
我是攻城师
2018/11/23
16.8K0
有了Future为什么还要CompletableFuture?
cheese
2024/02/06
3870
有了Future为什么还要CompletableFuture?
CompletableFuture异步回调
  Future模式是高并发设计与开发过程中常见的设计模式,它的核心思想是异步调用。对于Future模式来说,它不是立即返回我们所需要的数据,但是它会返回一个契约(或异步任务),将来我们可以凭借这个契约(或异步任务)获取需要的结果。
别团等shy哥发育
2023/10/17
7760
CompletableFuture异步回调
阅读 Flink 源码前必会的知识 - Java 8 异步编程 CompletableFuture 全解析
通常来说,程序都是顺序执行,同一时刻只会发生一件事情。如果一个函数依赖于另一个函数的结果,它只能等待那个函数结束才能继续执行,从用户角度来说,整个程序才算执行完毕。
kk大数据
2021/03/13
1.2K0
推荐阅读
相关推荐
01-CompletableFuture异步线程 入门
更多 >
交个朋友
加入腾讯云官网粉丝站
双11活动抢先看 更有社群专属礼券掉落
加入讨论
的问答专区 >
全栈开发工程师擅长5个领域
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档