并发编程 - CompletableFuture

news2025/2/23 23:00:34

文章目录

  • Pre
  • 概述
  • Future
  • Future的缺陷
  • 类继承关系
  • 功能概述
  • API
    • 提交任务的相关API
    • 结果转换的相关API
      • thenApply
      • handle
      • thenRun
      • thenAccept
      • thenAcceptBoth
      • thenCombine
      • thenCompose
    • 回调方法的相关API
    • 异常处理的相关API
    • 获取结果的相关API
  • DEMO
  • 实战
  • 注意事项

在这里插入图片描述

Pre

每日一博 - Java 异步编程的 Promise 模式 CompletableFuture的前世今生 (上)

Java8 - 使用CompletableFuture 构建异步应用


概述

常见的线程创建方式有两种,一是直接继承Thread,另一种是实现Runnable接口。但这两种方式有个缺点,不支持获取线程执行结果。

所以在JDK1.5之后,提供了Callable和Future,可以在任务执行后获取执行结果。

Future

Future类位于java.util.concurrent包下,从下面的源码可以看出,Future主要提供了三种能力:

  • 关闭执行中的任务
  • 判断任务是否执行完成
  • 获取任务执行的结果
package java.util.concurrent;
public interface Future<V> {
    // 取消执行中的任务
    boolean cancel(boolean mayInterruptIfRunning);
    // 判断任务是否被取消成功
    boolean isCancelled();
    // 判断任务是否执行完成
    boolean isDone();
    // 获取任务执行结果
    V get() throws InterruptedException, ExecutionException;
    // 在规定时间内获取任务执行结果,若规定时间任务还没执行完,则返回null,而非抛异常
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

Future的缺陷

Future通过isDone()判断任务是否执行完成,get()获取任务执行的结果,解决了创建线程异步执行任务不能获取执行结果的问题。

在任务异步执行中,主线程在等待过程中可以做其他事,但其本身也存在一定的局限性

  • 并行执行多任务获取结果主线程长时间阻塞:当需要将多个模块的任务异步执行时,使用for循环遍历任务列表,通过isDone()轮询判断任务是否执行完成,通过get()方法获取任务执行结果,且结果获取依赖任务执行顺序。但因Future的get()方法是主线程阻塞等待获取执行结果,所以在结果返回前,主线程不能处理其他任务,长时间阻塞,可能会产生block,在使用时考虑用超时时间的get()方法。
  • 不能链式执行任务:如上述场景,希望在获取商品基础信息后执行获取优惠信息任务,Future没有提供这种能力,不能实现链式调用。
  • 不能将多个任务执行的结果组合:在上述场景中,希望获取商详所需的各个模块信息后,组合成调用方需要的结果,但Future不支持。
  • 不能处理异常:Future没有异常处理的能力。

综上所述,阻塞主线程获取结果的方式与异步编程的初衷相违背,轮询判断任务是否执行完成会耗费不必要的CPU资源,为优化上述问题,在JDK1.8时引入了CompletableFuture实现类,提供异步链式编程的能力。


类继承关系

CompletableFuture扩展了Future接口,实现了 CompletionStage,提供了函数式编程的能力,通过回调的方式处理计算结果,并提供了转换和组合CompletableFuture的方法,从而简化异步编程的复杂性

在这里插入图片描述
在这里插入图片描述

CompletableFuture对象是JDK1.8版本新引入的类,这个类实现了两个接口,

  • 一个是Future接口
  • 一个是CompletionStage接口

CompletionStage接口是JDK1.8版本提供的接口,用于异步执行中的阶段处理,CompletionStage定义了一组接口用于在一个阶段执行结束之后,要么继续执行下一个阶段,要么对结果进行转换产生新的结果等,一般来说要执行下一个阶段都需要上一个阶段正常完成,这个类也提供了对异常结果的处理接口。


功能概述

CompletableFuture 是一个实现了 Java 8 中 Completable 接口的类,它代表一个异步计算的结果。 它提供了异步编程的能力,可以让开发者在编写代码时更加方便地处理异步操作。

CompletableFuture具有以下主要特征:

  1. 异步编程能力
    可以通过supplyAsync、runAsync等方法异步执行任务,不会阻塞当前线程。

  2. 组合式编程
    支持thenApply、thenAccept、thenCompose等方法将多个CompletableFuture进行组合,实现复杂的异步流水线计算。

  3. 异常处理
    可以通过whenComplete、exceptionally等方法设置异常处理,方便异步任务链的异常传播与处理。

  4. 结果获取
    可以通过join()等待获取结果,也可以通过回调注册方式获取结果。

  5. 取消任务
    可以通过cancel()取消正在执行的CompletableFuture任务。

  6. 依赖管理
    可以通过thenCompose等方法明确定义任务间依赖关系。

综上,CompletableFuture为Java异步编程提供了强大支持,可以帮助构建高效、可靠的异步应用程序,是Java 8非常重要的新特性之一。它极大地简化并丰富了Java的异步编程模型。


API

提交任务的相关API

CompletableFuture提供了四种创建异步对象的方法

public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
  • supplyAsync提交的任务有返回值
  • runAsync提交的任务没有返回值

两个接口都有一个重载的方法,第二个入参为指定的线程池,如果不指定,则默认使用ForkJoinPool.commonPool()线程池。在使用的过程中尽量根据不同的业务来指定不同的线程池,方便对不同线程池进行监控,同时避免业务共用线程池相互影响。


结果转换的相关API

thenApply

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

入参是Function,意思是将上一个CompletableFuture执行结果作为入参,再次进行转换或者计算,重新返回一个新的值。


handle

public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)

入参是BiFunction,该函数式接口有两个入参一个返回值,意思是处理上一个CompletableFuture的处理结果,同时如果有异常,需要手动处理异常。


thenRun

public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)

入参是Runnable函数式接口,该接口无需入参和出参,这一组函数是在上一个CompletableFuture任务执行完成后,在执行另外一个接口,不需要上一个任务的结果,也不需要返回值,只需要在上一个任务执行完成后执行即可。


thenAccept

public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)

入参是Consumer,该函数式接口有一个入参,没有返回值,所以这一组接口的意思是处理上一个CompletableFuture的处理结果,但是不返回结果。


thenAcceptBoth

public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action, Executor executor)

入参包括CompletionStage以及BiConsumer,

  • CompletionStage是JDK1.8新增的接口,在JDK中只有一个实现类:CompletableFuture,所以第一个入参就是CompletableFuture,这一组函数是用来接受两个CompletableFuture的返回值,并将其组合到一起。

  • BiConsumer这个函数式接口有两个入参,并且没有返回值,BiConsumer的第一个入参就是调用方CompletableFuture的执行结果,第二个入参就是thenAcceptBoth接口入参的CompletableFuture的执行结果。

所以这一组函数意思是将两个CompletableFuture执行结果合并到一起。


thenCombine

public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor)

和thenAcceptBoth类似,入参都包含一个CompletionStage,也就是CompletableFuture对象,意思也是组合两个CompletableFuture的执行结果,不同的是thenCombine的第二个入参为BiFunction,该函数式接口有两个入参,同时有一个返回值。所以与thenAcceptBoth不同的是,thenCombine将两个任务结果合并后会返回一个全新的值作为出参。


thenCompose

public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor)

意思是将调用方的执行结果作为Function函数的入参,同时返回一个新的CompletableFuture对象。


回调方法的相关API

public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)

意思是当上一个CompletableFuture对象任务执行完成后执行该方法。BiConsumer函数式接口有两个入参没有返回值,这两个入参第一个是CompletableFuture任务的执行结果,第二个是异常信息。表示处理上一个任务的结果,如果有异常,则需要手动处理异常,与handle方法的区别在于,handle方法的BiFunction是有返回值的,而BiConsumer是没有返回值的。

以上方法都有一个带有Async的方法,带有Async的方法表示是异步执行的,会将该任务放到线程池中执行,同时该方法会有一个重载的方法,最后一个参数为Executor,表示异步执行可以指定线程池执行。为了方便进行控制,最好在使用CompletableFuture时手动指定我们的线程池。


异常处理的相关API

public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)

是用来处理异常的,当任务抛出异常后,可以通过exceptionally来进行处理,也可以选择使用handle来进行处理,不过两者有些不同,hand是用来处理上一个任务的结果,如果有异常情况,就处理异常。而exceptionally可以放在CompletableFuture处理的最后,作为兜底逻辑来处理未知异常。


获取结果的相关API

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)

allOf是需要入参中所有的CompletableFuture任务执行完成,才会进行下一步;

anyOf是入参中任何一个CompletableFuture任务执行完成都可以执行下一步。

public T get() throws InterruptedException, ExecutionException
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
public T getNow(T valueIfAbsent)
public T join()

get方法一个是不带超时时间的,一个是带有超时时间的。

getNow方法则是立即返回结果,如果还没有结果,则返回默认值,也就是该方法的入参。

join方法是不带超时时间的等待任务完成。


DEMO

通过CompletableFuture来实现一个多线程处理异步任务的例子。

创建10个任务提交到我们指定的线程池中执行,并等待这10个任务全部执行完毕。

每个任务的执行流程为第一次先执行加法,第二次执行乘法,如果发生异常则返回默认值,当10个任务执行完成后依次打印每个任务的结果。

public void demo() throws InterruptedException, ExecutionException, TimeoutException {
        // 1、自定义线程池
        ExecutorService executorService = new ThreadPoolExecutor(5, 10,
                60L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100));

        // 2、集合保存future对象
        List<CompletableFuture<Integer>> futures = new ArrayList<>(10);
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            CompletableFuture<Integer> future = CompletableFuture
                    // 提交任务到指定线程池
                    .supplyAsync(() -> this.addValue(finalI), executorService)
                    // 第一个任务执行结果在此处进行处理
                    .thenApplyAsync(k -> this.plusValue(finalI, k), executorService)
                    // 任务执行异常时处理异常并返回默认值
                    .exceptionally(e -> this.defaultValue(finalI, e));
            // future对象添加到集合中
            futures.add(future);
        }

        // 3、等待所有任务执行完成,此处最好加超时时间
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(5, TimeUnit.MINUTES);
        for (CompletableFuture<Integer> future : futures) {
            Integer num = future.get();
            System.out.println("任务执行结果为:" + num);
        }
        System.out.println("任务全部执行完成!");
    }

    private Integer addValue(Integer index) {
        System.out.println("第" + index + "个任务第一次执行");
        if (index == 3) {
            int value = index / 0;
        }
        return index + 3;
    }

    private Integer plusValue(Integer index, Integer num) {
        System.out.println("第" + index + "个任务第二次执行,上次执行结果:" + num);
        return num * 10;
    }

    private Integer defaultValue(Integer index, Throwable e) {
        System.out.println("第" + index + "个任务执行异常!" + e.getMessage());
        e.printStackTrace();
        return 10;
    }

实战

举个例子:

假设详页有以下几个模块,每个模块需要如下时间才能完成:

获取商品基础信息 0.5s
获取优惠信息 1s
获取门店信息 1s
获取验机评估报告信息 1s
获取标品参数信息 1s
组装返回商品信息 0.5s

串行执行每个模块,那么一共需要5s才能返回给调用方,如果接口产生超时等,会比5s还要长,显然是不能接受的
在这里插入图片描述

如果有多个线程并行完成各个模块,可能2s内就能返回信息。

在这里插入图片描述

可以将获取商品详情页的步骤分为三步,分别为:

  1. 获取商品基础信息、商品验机评估报告信息、商品标品参数信息、门店信息;
  2. 获取商品优惠信息,需要等1结束;
  3. 将上述信息RPC结果组装返回,需要等1,2结束。

因为多任务异步并行执行,最终耗时将取决于耗时最长的链路。如下图所示

在这里插入图片描述

代码示例:

 ExecutorService testThreadPool = Executors.newFixedThreadPool(10);
    ResultDTO resultDTO = new ResultDTO();
    //基础信息
    CompletableFuture<Void> productBaseInfoFuture = CompletableFuture.runAsync(() -> {
        BaseInfoDTO baseInfoDTO = rpcxx;
        resultDTO.setBaseInfoDTO(baseInfoDTO);
    }, testThreadPool);
    
    //优惠信息
    CompletableFuture<Void> couponInfoFuture = productBaseInfoFuture.thenAcceptAsync(() -> {
        CouponInfoDTO couponInfoDTO = rpcxx;
        resultDTO.setCouponInfoDTO(couponInfoDTO);
    }, testThreadPool);
    
    //验机评估报告信息
    CompletableFuture<Void> qcInfoFuture = CompletableFuture.runAsync(() -> {
        QcInfoDTO qcInfoDTO = rpcxx;
        resultDTO.setQcInfoDTO(qcInfoDTO);
    }, testThreadPool);
    
    //门店信息
    CompletableFuture<Void> storeInfoFuture = CompletableFuture.runAsync(() -> {
        StoreInfoDTO storeInfoDTO = rpcxx;
        resultDTO.setStoreInfoDTO(storeInfoDTO);
    }, testThreadPool);
    
    //标品参数信息
    CompletableFuture<Void> spuInfoFuture = CompletableFuture.runAsync(() -> {
        SpuInfoDTO spuInfoDTO = rpcxx;
        resultDTO.setSpuInfoDTO(spuInfoDTO);
    }, testThreadPool);

    //组装结果
    CompletableFuture<Void> allQuery = CompletableFuture.allOf(couponInfoFuture, qcInfoFuture, storeInfoFuture, spuInfoFuture);
    CompletableFuture<Void> buildFuture = allQuery.thenAcceptAsync((result) -> {
        //组装逻辑
        return null;
    }).join();

以上即为获取门店商品详情页异步编排的实现逻辑,但也发现,该方案创建多个异步任务,执行逻辑不一样但流程大致相同,类似的代码重复写多遍,不便于扩展和阅读。

在此基础上可以优化为使用CompletableFuture+简单工厂+策略模式,将上述步骤中的每个模块都作为策略handler,且策略之间有权重依赖关系,模块类型作为工厂类型,将模块类型放进列表中,使用CompletableFuture.allOf()异步执行列表中的任务。

伪代码如下:

  List<String> eventList = Arrays.asList("xx", "xxx");
        CompletableFuture.allOf(eventList.stream().map(event ->
            CompletableFuture.runAsync(() -> {
        //通过工厂类型获取策略实现handler
        if (Objects.nonNull(handler)) {
            //如果存在则执行
        }
    }, testThreadPool)).toArray(CompletableFuture[]::new)).join();

注意事项

  • 当有多个任务可以异步并行执行时,使用CompletableFuture,任务越多效果越明显;
  • 使用CompletableFuture可以将多个任务串联执行,也可以利用组合方式将任务排列由列表变成树结构;
  • 在使用集合接收多线程处理任务的结果时,需要考虑线程安全问题;
  • 当任务执行有相互依赖关系时,需考虑任务超时主动结束,避免系统block。

在这里插入图片描述

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

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

相关文章

DPN(Dual Path Network)网络结构详解

论文&#xff1a;Dual Path Networks 论文链接&#xff1a;https://arxiv.org/abs/1707.01629 代码&#xff1a;https://github.com/cypw/DPNs MXNet框架下可训练模型的DPN代码&#xff1a;https://github.com/miraclewkf/DPN 我们知道ResNet&#xff0c;ResNeXt&#xff0c;D…

面向对象【对象数组的使用与内存分析、方法重载、可变个数形参】

文章目录 对象数组实例对象内存分析 方法的重载重载方法调用打印方法的重载 可变个数形参的方法特点传递任意数量的参数与其他参数共存传递数组或多个参数 对象数组 存储对象引用的数组。它允许您在单个数组中存储多个对象&#xff0c;并通过索引访问和操作这些对象。 实例 创…

使用SpringBoot+SpringMVC+Mybatis+Redis实现个人博客管理平台

文章目录 前言1. 项目概述2. 项目需求2.1功能需求2.2 其他需求2.3 系统功能模块图 3. 开发环境4. 项目结构5. 部分功能介绍5.1 数据库密码密文存储5.2 统一数据格式返回5.3 登录拦截器 6. 项目展示 前言 在几个月前实现了一个servlet版本的博客系统&#xff0c;本项目则是在原…

JWT无状态理解

JSON Web Tokens (JWT) 被称为无状态&#xff0c;因为授权服务器不需要维护任何状态&#xff1b;令牌本身就是验证令牌持有者授权所需的全部内容。 JWTs都签订使用数字签名算法&#xff08;例如RSA&#xff09;不能被伪造。因此&#xff0c;任何信任签名者证书的人都可以放心地…

二维深度卷积网络模型下的轴承故障诊断

1.数据集 使用凯斯西储大学轴承数据集&#xff0c;一共有4种负载下采集的数据&#xff0c;每种负载下有10种 故障状态&#xff1a;三种不同尺寸下的内圈故障、三种不同尺寸下的外圈故障、三种不同尺寸下的滚动体故障和一种正常状态 2.模型&#xff08;二维CNN&#xff09; 使…

基于传统检测算法hog+svm实现图像多分类

直接上效果图&#xff1a; 代码仓库和视频演示b站视频005期&#xff1a; 到此一游7758258的个人空间-到此一游7758258个人主页-哔哩哔哩视频 代码展示&#xff1a; 数据集在datasets文件夹下 运行01train.py即可训练 训练结束后会保存模型在本地 运行02pyqt.py会有一个可视化…

iOS开发-自定义TabbarController与Tabbar按钮Badge角标

iOS开发-自定义Tabbar按钮Badge角标 Tabbar是放在APP底部的控件。UITabbarController是一个非常常见的一种展示模式了。比如微信、QQ都是使用tabbar来进行功能分类的管理。 一、实现自定义Tabbar 我这里Tabbar继承于系统的UITabBar&#xff0c;定义背景图、线条的颜色、tab…

Redis系列 2- Redis 的持久化

Redis系列 2- Redis 的持久化 1、关于 Redis 持久化2、RDB 持久化2.1、RDB 文件的创建与载入2.2、RDB 自动间隔性保存的配置2.3、Redis 快照2.4、RDB 重点总结 3、AOF 持久化3.1、命令追加(append)3.2、AOF 文件的写入与同步3.3、AOF 工作原理3.4、AOF 的文件载入与数据还原3.5…

QGIS3.28的二次开发一:编译工程

环境&#xff1a;VS2019OSGeo4WCMake_3.26Cygwin64QGIS_3.28 注意&#xff1a;一定要按照步骤顺序来&#xff01; 一、配置环境 &#xff08;一&#xff09;VS2019 VS2019下载链接https://my.visualstudio.com/Downloads?qvisual%20studio%202019&wt.mc_ido~msft~vsco…

day50-Insect Catch Game(捉虫游戏)

50 天学习 50 个项目 - HTMLCSS and JavaScript day50-Insect Catch Game&#xff08;捉虫游戏&#xff09; 效果 index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport"…

基于SpringBoot+Vue的大学生租房系统设计与实现(源码+LW+部署文档等)

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

第 356 场力扣周赛题解

A 满足目标工作时长的员工数目 签到题 class Solution { public:int numberOfEmployeesWhoMetTarget(vector<int> &hours, int target) {int res 0;for (auto x: hours)if (x > target)res;return res;} };B 统计完全子数组的数目 枚举子数组&#xff1a;枚举子数…

小研究 - 主动式微服务细粒度弹性缩放算法研究(四)

微服务架构已成为云数据中心的基本服务架构。但目前关于微服务系统弹性缩放的研究大多是基于服务或实例级别的水平缩放&#xff0c;忽略了能够充分利用单台服务器资源的细粒度垂直缩放&#xff0c;从而导致资源浪费。为此&#xff0c;本文设计了主动式微服务细粒度弹性缩放算法…

溟㠭篆刻艺术……“瀚1”

每个人生犹如一颗颗繁星&#xff0c;在时空交错中汇聚成一条星汉灿烂的银河&#xff0c;在静谧深邃的宇宙中清澈回响&#xff0c;熠熠生辉。妻晓蕾题注溟㠭刊…… 溟㠭 篆刻作品“瀚1” 溟㠭 篆刻作品“瀚1” 溟㠭 篆刻作品“瀚1” 文/晓蕾

质效卓越,科技前沿—QECon北京站线下盛会成功落幕

7月28日-29日&#xff0c;第八届QECon质量效能大会在北京成功召开&#xff0c;这是质量效能领域备受期待的一场盛会&#xff0c;从2020年第一届QECon开启以来&#xff0c;历经四年QECon北京站终于首次线下落地。本次大会的核心主旨为“数生智慧&#xff0c;高质量发展新引擎”&…

虚拟局域网VLAN

概述 广播域 使用一个或多个以太网交换机互连接起来的交互式以太网&#xff0c;其所有站点都属于同一个广播域&#xff0c;随着交换式以太网规模的扩大&#xff0c;广播域响应扩大&#xff0c;从而形成一个巨大的广播域。 但是巨大的广播域会带来很多的弊端&#xff1a; 广…

JUC中其他常用类

1.CopyOnWriteArrayList ArrayList是线程不安全的&#xff0c;Vector是线程安全的(方法被Synchronized修饰)&#xff0c;CopyOnWriterArrayList是在Vector的基础上再做优化&#xff0c;因为当读取操作较多时&#xff0c;Vector的效率不高。CopyOnWriterArrayList中读操作并没有…

C++ 类和对象篇(一) 类的引入

目录 一、类的概念 二、类的引入 三、类的定义 1.定义一个类 2.struct 和 class 的区别 3.类中成员函数的声明、实现分离 四、封装及类的访问限定符 1.封装 2.类的访问限定符 五、类的作用域和生命周期 六、类的实例化 七、类存储方法 八、计算类的大小 一、类的概念 1…

Gazebo打不开

问题&#xff1a;启动Gazebo半天打不开&#xff0c;无反应 原因&#xff1a;启动Gazebo&#xff0c;会优先从网络上下载模型 解决方案&#xff1a; 1.断网&#xff0c;然后再启动Gazebo 2.把模型下载下来 模型下载链接&#xff1a;GitHub - osrf/gazebo_models: Gazebo da…

简要理清计算机的发展(从硬件和软件)

1.计算机硬件和其他板块的关系 2.什么是计算机系统 可以简单理解计算机系统 硬件 软件。 3.硬件、软件的发展 3.1.硬件发展 逻辑元件的发展&#xff1a; 发展时间逻辑原件速度&#xff08;次/秒&#xff09;内存外存其他第一代1946-1957电子管几千-几万汞延迟线、磁鼓穿…