CompletableFuture异步和线程池

news2025/2/26 6:24:25

一、线程回顾

1、初始化线程的 4 种方式

1)、继承 Thread

2)、实现 Runnable 接口

3)、实现 Callable 接口 + FutureTask (可以拿到返回结果,可以处理异常)

4)、线程池

方式 1 和方式 2:主进程无法获取线程的运算结果。不适合当前场景

方式 3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致

服务器资源耗尽。

方式 4:通过如下两种方式初始化线程池

Executors.newFiexedThreadPool(3);

//或者

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit,

workQueue, threadFactory, handler);

通过线程池性能稳定,也可以获取执行结果,并捕获异常。但是,在业务复杂情况下,一

个异步调用可能会依赖于另一个异步调用的执行结果。

2、线程池的七大参数

corePoolSize:池中一直保持的线程的数量,即使线程空闲。除非设置了 allowCoreThreadTimeOut

maximumPoolSize:池中允许的最大的线程数

keepAliveTime:当线程数大于核心线程数的时候,线程在最大多长时间没有接到新任务就会终止释放,

最终线程池维持在 corePoolSize 大小

unit:时间单位

workQueue:阻塞队列,用来存储等待执行的任务,如果当前对线程的需求超过了 corePoolSize

大小,就会放在这里等待空闲线程执行。

threadFactory:创建线程的工厂,比如指定线程名等

handler:拒绝策略,如果线程满了,线程池就会使用拒绝策略。

运行流程:

1、线程池创建,准备好 core 数量的核心线程,准备接受任务

2、新的任务进来,用 core 准备好的空闲线程执行。

(1) 、core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队

列获取任务执行

(2) 、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量

(3) 、max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自

动销毁。最终保持到 core 大小

(4) 、如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策

略进行处理

3、所有的线程创建都是由指定的 factory 创建的。

3、常见的 4 种线程池

newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。

newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

4、开发中为什么使用线程池

降低资源的消耗

通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗

提高响应速度

因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行

提高线程的可管理性

线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配

回到顶部

二、CompletableFuture 异步编排

业务场景:

查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间。

假如商品详情页的每个查询,需要如下标注的时间才能完成

那么,用户需要 5.5s 后才能看到商品详情页的内容。很显然是不能接受的。

如果有多个线程同时完成这 6 步操作,也许只需要 1.5s 即可完成响应。

Future 是 Java 5 添加的类,用来描述一个异步计算的结果。你可以使用`isDone`方法检查计算是否完成,或者使用`get`阻塞住调用线程,直到计算完成返回结果,你也可以使用`cancel`方法停止任务的执行。

CompletableFuture 和 FutureTask 同属于 Future 接口的实现类,都可以获取线程的执行结果。

1、创建异步对象

CompletableFuture 提供了四个静态方法来创建一个异步操作。

runXxxx 都是没有返回结果的,supplyXxx 都是可以获取返回结果的

可以传入自定义的线程池,否则就用默认的线程池;

CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        System.out.println("当前线程:" + Thread.currentThread().getId());
        int i = 10 / 2;
        System.out.println("运行结果:" + i);
        }, executor);

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        System.out.println("当前线程:" + Thread.currentThread().getId());
        int i = 10 / 0;
        System.out.println("运行结果:" + i);
             return i;
         }, executor);
Integer result = future.get();

2、计算完成时回调方法

whenComplete 可以处理正常和异常的计算结果,exceptionally 处理异常情况。

whenComplete 和 whenCompleteAsync 的区别:

whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。

whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池

来进行执行。

方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程

执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
             System.out.println("当前线程:" + Thread.currentThread().getId());
             int i = 10 / 0;
             System.out.println("运行结果:" + i);
             return i;
         }, executor).whenComplete((res,exception) -> {
             //虽然能得到异常信息,但是没法修改返回数据
             System.out.println("异步任务成功完成了...结果是:" + res + "异常是:" + exception);
         }).exceptionally(throwable -> {
             //可以感知异常,同时返回默认值
             return 10;
         });

3、handle 方法

和 complete 一样,可对结果做最后的处理(可处理异常),可改变返回值。

/**
         * 方法执行完后端处理
         */
         CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
             System.out.println("当前线程:" + Thread.currentThread().getId());
             int i = 10 / 2;
             System.out.println("运行结果:" + i);
             return i;
         }, executor).handle((result,thr) -> {
             if (result != null) {
                 return result * 2;
             }
             if (thr != null) {
                 System.out.println("异步任务成功完成了...结果是:" + result + "异常是:" + thr);
                 return 0;
             }
             return 0;
         });

4、线程串行化方法

thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。

thenAccept 方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。

thenRun 方法:只要上面的任务执行完成,就开始执行 thenRun,只是处理完任务后,执行thenRun 的后续操作

带有 Async 默认是异步执行的。同之前。

以上都要前置任务成功完成。

Function<? super T,? extends U>

T:上一个任务返回结果的类型U:当前任务的返回值类型

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
            return i;
        }, executor).thenApplyAsync(res -> {
            System.out.println("任务2启动了..." + res);
            return "Hello" + res;
        }, executor);
        System.out.println("main......end....." + future.get());

5、两任务组合 - 都要完成

 

两个任务必须都完成,触发该任务。

thenCombine:组合两个 future,获取两个 future 的返回结果,并返回当前任务的返回值

thenAcceptBoth:组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有返回值。

runAfterBoth:组合两个 future,不需要获取 future 的结果,只需两个 future 处理完任务后,

处理该任务。

6、两任务组合 - 一个完成

 

当两个任务中,任意一个 future 任务完成的时候,执行任务。

applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。

acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。

runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返回值。

7、多任务组合

allOf:等待所有任务完成

anyOf:只要有一个任务完成

回到顶部

三.案例

1.MyThreadConfig.java

//@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {


    @Bean
    public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) {
        return new ThreadPoolExecutor(
                pool.getCoreSize(),
                pool.getMaxSize(),
                pool.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }

}

2.ThreadPoolConfigProperties.java

@ConfigurationProperties(prefix = "gulimall.thread")
@Component
@Data
public class ThreadPoolConfigProperties {

    private Integer coreSize;

    private Integer maxSize;

    private Integer keepAliveTime;


}

3.使用异步线程

@Service("skuInfoService")
public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoDao, SkuInfoEntity> implements SkuInfoService {
    @Resource
    private ThreadPoolExecutor executor;

    
    @Override
    public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {

        SkuItemVo skuItemVo = new SkuItemVo();

        CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
            //1、sku基本信息的获取  pms_sku_info
            SkuInfoEntity info = this.getById(skuId);
            skuItemVo.setInfo(info);
            return info;
        }, executor);


        CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {
            //3、获取spu的销售属性组合
            List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrBySpuId(res.getSpuId());
            skuItemVo.setSaleAttr(saleAttrVos);
        }, executor);


        CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((res) -> {
            //4、获取spu的介绍    pms_spu_info_desc
            SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
            skuItemVo.setDesc(spuInfoDescEntity);
        }, executor);


        CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync((res) -> {
            //5、获取spu的规格参数信息
            List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());
            skuItemVo.setGroupAttrs(attrGroupVos);
        }, executor);


        // Long spuId = info.getSpuId();
        // Long catalogId = info.getCatalogId();

        //2、sku的图片信息    pms_sku_images
        CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
            List<SkuImagesEntity> imagesEntities = skuImagesService.getImagesBySkuId(skuId);
            skuItemVo.setImages(imagesEntities);
        }, executor);

        CompletableFuture<Void> seckillFuture = CompletableFuture.runAsync(() -> {
            //3、远程调用查询当前sku是否参与秒杀优惠活动
            R skuSeckilInfo = seckillFeignService.getSkuSeckilInfo(skuId);
            if (skuSeckilInfo.getCode() == 0) {
                //查询成功
                SeckillSkuVo seckilInfoData = skuSeckilInfo.getData("data", new TypeReference<SeckillSkuVo>() {
                });
                skuItemVo.setSeckillSkuVo(seckilInfoData);

                if (seckilInfoData != null) {
                    long currentTime = System.currentTimeMillis();
                    if (currentTime > seckilInfoData.getEndTime()) {
                        skuItemVo.setSeckillSkuVo(null);
                    }
                }
            }
        }, executor);


        //等到所有任务都完成
        CompletableFuture.allOf(saleAttrFuture,descFuture,baseAttrFuture,imageFuture,seckillFuture).get();

        return skuItemVo;
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/618854.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《精通特征工程》学习笔记(1):数值特征处理

不进行多余的解释&#xff0c;想看原文直接下载pdf查看&#xff0c;本文是精简提炼了重要的方法写进来。 1.二值化 在百万歌曲数据集中&#xff0c;原始的收听次数并不是衡量用户喜好的强壮指标。&#xff08;在统计学术语 中&#xff0c;“强壮”意味着该方法适用于各种情况…

【2611. 老鼠和奶酪】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 有两只老鼠和 n 块不同类型的奶酪&#xff0c;每块奶酪都只能被其中一只老鼠吃掉。 下标为 i 处的奶酪被吃掉的得分为&#xff1a; 如果第一只老鼠吃掉&#xff0c;则得分为 reward1[i] 。如果第二…

Hive之HPLSQL安装手册

软件版本信息&#xff1a; CDH&#xff1a; cdh5.14.0 Hive: 1.1.0 Impala&#xff1a;2.11.0一&#xff1a;下载地址 Hplsql官网&#xff1a; http:www.hplsql.org/download 下载的是&#xff1a;hplsql-0.3.31.tar.gz版本 二&#xff1a;安装步骤 解压下载的hplsql-0.3.…

kafka 02

4.API开发 准备&#xff1a; 创建项目 &#xff0c; 添加依赖 XML <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <depen…

如何评价编程语言 Reason?

Reason编程语言的设计目标是为开发人员提供一种易于理解和学习的工具&#xff0c;同时提供静态类型检查和高效的代码执行。Reason基于OCaml语言&#xff0c;并引入了JavaScript的语法和工具链&#xff0c;使得开发者能够在现有的JavaScript生态系统中更好地开发和维护代码。Rea…

2023,数据库国产替代走到哪了?

如今&#xff0c;战场不仅银行&#xff0c;参战者也不仅单独的一家。对中国的国产数据库而言&#xff0c;机会和挑战都在加速涌来。 作者|思杭 编辑|皮爷 出品|产业家 2023&#xff0c;数据库格局正在变化&#xff0c;愈演愈烈。 如果说哪个环节是如今国产替代的最火热…

chatgpt赋能python:Python中如何输出两个数

Python中如何输出两个数 对于任何一种编程语言来说&#xff0c;输出两个数都是非常基础的知识点&#xff0c;Python也不例外。在Python中&#xff0c;我们可以使用print()函数来输出两个数。本篇文章将会介绍如何在Python中输出两个数。 介绍 在Python中&#xff0c;输出两个…

Python让文档处理变得轻松:如何快速替换Word文档中的关键字

应用场景&#xff1a; Python自动化处理Word文档的功能可以应用于许多场景&#xff0c;以下是其中一些常见的应用场景&#xff1a; 批量处理文档&#xff1a;如果您需要处理大量的Word文档&#xff0c;例如替换文本、添加文本、修改格式等&#xff0c;手动完成这些任务将非常耗…

驱动LSM6DS3TR-C实现高效运动检测与数据采集(4)----上报匿名上位机实现可视化

概述 LSM6DS3TR-C是单芯片“3轴陀螺仪 3轴加速度计”的惯性 测量单元(IMU)&#xff0c; 五种种可选满量程的陀螺仪(125/250/500/1000/2000 dps)和加速度计(2/4/8/16 g)。 上述工程中选择的加速度和陀螺仪对应的量程为2g和2000dps&#xff0c;对应的灵敏度如下所示&#xff0c…

具有更多存储空间和带宽的亚马逊云科技Snowball Edge存储优化型设备

亚马逊云科技AWS Snow Family系列设备用于将数据经济高效地迁移到云端并在边缘进行处理。增强型Snowball Edge存储优化型设备专为PB级数据迁移项目而设计&#xff0c;具有210TB的NVMe存储空间&#xff0c;每秒可传输高达1.5千兆字节的数据。这些设备还包括10GBASE-T、SFP48和QS…

缩略图加密学习总结

一、缩略图加密概述 完全加密为噪声图像后&#xff0c;密文图像的文件扩展&#xff0c;传输存储消耗更多的资源。完全加密的噪声图像的可用性建立在对密文进行解密的基础上&#xff0c;耗费大量的计算代价。原始图像中精细的视觉信息被抹去以保护隐私,而粗略的视觉信息被保留以…

Vue+element UI实现列表全部数据排序

element ui 表格中的sortable属性只能实现当前页数据的排序&#xff0c;无法实现整张表全部数据的排序&#xff0c;所以需要采取自定义的排序方式重新触发接口&#xff0c;获取排序好的全部列表 Java后端的分页列表有这两个字段需要前端去传递&#xff1a; el-table 上加上so…

springboot 2.6.12 自定义解析 yaml 加密数据

文章目录 一、简介二、yaml 默认解析简单说明三、自定义 yaml 解密解析器四、配置 PropertySourceLoader五、简单测试 一、简介 为了保证项目的配置文件的安全性&#xff0c;需要对第三方组件 mysql、redis、es、mq 等用户名密码进行加密处理&#xff0c;可以使用现成的三方包…

【springboot项目开发】文件上传与下载

目录 总体介绍 文件上传 介绍 文件上传的前端需求 文件上传的前端代码 文件上传的后端需求 文件上传的后端代码 文件下载 介绍 前端需求 前端代码 后端需求 后端代码 总体介绍 文件的上传和下载功能&#xff0c;是项目开发过程中比较常见的业务需求&#xff0c;我们…

开源 Golang 微服务入门二:RPC 框架 Kitex

前言 前一篇笔记介绍了字节跳动的开源 Golang 微服务 HTTP 框架 Hertz&#xff0c; 如下&#xff1a; 开源 Golang 微服务入门一&#xff1a; HTTP 框架 Hertz 本文将要介绍同样是字节跳动开源的 Golang 微服务 RPC 框架 Kitex。 Kitex 简介 Kitex 字节跳动内部的 Golang 微…

nacos高级

一、什么是配置中心 在微服务架构中&#xff0c;当系统从一个单体应用被拆分成分布式系统上的一个个服务节点后&#xff0c;配置文件也必须跟着迁移&#xff08;分割&#xff09;&#xff0c;这样配置就分散了。不仅配置会分散&#xff0c;分散中还会包含着冗余。 配置中心将…

Linux进程、用户、权限命令

进程管理命令 进程和程序的区别 1 程序是静态概念&#xff0c;本身作为一种软件资源长期保存&#xff1b;而进程是程序的执行过程&#xff0c;它是动态概念&#xff0c;有一定的生命期&#xff0c;是动态产生和消亡的。 2 程序和进程无一一对应关系。一个进程在活动中可有顺序…

顺序表和链表的比较

本文主要内容&#xff1a;从存取方式、逻辑/物理结构、查找/插入/删除操作和空间分配的角度比较顺序表和链表&#xff0c;并从存储、运算、环境的角度对比应如何选取存储结构。 目录 一、顺序表和链表的比较1、存取&#xff08;读写&#xff09;方式2、逻辑/物理结构3、查找、…

AI人工智能领域精美绘图模板分享

1 人工智能的发展历程 如今人工智能的应用渗透了我们生活的方方面面&#xff0c;我们都知道人工智能的前景十分光明&#xff0c;在未来对于推进人类发展进程也是非常重要的&#xff0c;但其实人工智能的发展道路是极其曲折的&#xff0c;下面就将人工智能的发展历程分为如下六…

arcgis for javascript TileLayer 自定义高德地图图层

效果如图&#xff1a; 一、创建自定义切片层 要创建自定义图块层&#xff0c;您必须调用BaseTileLayer类的createSubclass()方法。命名自定义层为TintLayer 由于这一层需要知道在哪里访问预定义的图块&#xff0c;我们将创建一个属性。应用程序将为图层提供值&#xff0c;图…