JUC CompletableFuture

文章目录

  • CompletableFuture^1.8+^
    • CompletionStage 接口
      • thenApply 系列
      • thenAccept 系列
      • thenRun 系列
      • thenCombine 系列
      • thenAcceptBoth
      • runAfterBoth
      • applyToEither
      • acceptEither
      • runAfterEither
      • thenCompose
      • whenComplete
      • handle
      • 其他
    • CompletionStage 的方法总结
  • CompletableFuture 实例化
    • CompletableFuture 其他 API 方法
      • complete 和 completeExceptionally
  • CompletableFuture 示例

CompletableFuture1.8+

在这里插入图片描述

CompletionStage 接口

thenApply 系列

    /*** 当该阶段正常完成时,该阶段将以该阶段的结果作为所提供函数的参数来执行,并返回新的 CompletionStage 对象*** @param fn Function 函数,函数的入参是当前阶段的结果* @param <U> Function 函数的返回值类型* @return 新的 CompletionStage 对象 */public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);/*** 当该阶段正常完成时,该阶段将以该阶段的结果作为所提供函数的参数来执行,并返回新的 CompletionStage 对象* 与 thenApply 的不同在于,异步执行** @param fn Function 函数,函数的入参是当前阶段的结果* @param <U> Function 函数的返回值类型* @return 新的 CompletionStage 对象*/public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn);/*** 当该阶段正常完成时,该阶段将以该阶段的结果作为所提供函数的参数来执行,并返回新的 CompletionStage 对象* 与 thenApply 的不同在于,异步执行** @param fn Function 函数,函数的入参是当前阶段的结果* @param <U> Function 函数的返回值类型* @param executor 执行阶段任务的线程池对象* (不传,默认使用 CompletableFuture 实例化时传入的线程池,CompletableFuture 实例化时,默认为:ForkJoinPool.commonPool())* @return 新的 CompletionStage 对象*/public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn,Executor executor);

thenAccept 系列

    /*** 与 thenApply 一样,只是接受的参数为 Consumer* 返回的 CompletionStage 泛型为 Void *** @param action 返回 CompletionStage 之前要执行的操作* @return the new CompletionStage*/public CompletionStage<Void> thenAccept(Consumer<? super T> action);/*** 异步执行*/public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);/*** 自定义线程池*/public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);

thenRun 系列

    /*** 与 thenApply 一样,只是接受 Runnable 类型的参数** @param action Runnable 类型,表示在该阶段返回新的 CompletionStage<Void> 对象之前执行的任务*/public CompletionStage<Void> thenRun(Runnable action);/*** 异步执行public CompletionStage<Void> thenRunAsync(Runnable action);/*** 自定义线程池*/public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);

thenCombine 系列

    /*** 返回一个新的 CompletionStage<V>* 用于结合两个 CompletionStage 对象的返回值** @param other 另一个 CompletionStage * @param fn BiFunction 类型的函数式接口(当前阶段的结果 + other 对象的结果作为函数的参数)* @param <U> other CompletionStage 的返回值类型* @param <V> BiFunction 类型的函数式接口的返回值类型* @return 新的 CompletionStage 对象*/public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);

thenAcceptBoth

    /*** 执行的函数式接口为:BiConsumer*/public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action,Executor executor);

runAfterBoth

    /*** 执行的接口为 Runnable*/public CompletionStage<Void> runAfterBoth(CompletionStage<?> other,Runnable action);public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action);public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor);

applyToEither

    /*** 等待两个阶段正常完成,则将当前阶段的结果传入 Function 函数执行*/public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other,Function<? super T, U> fn);public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn);public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn,Executor executor);

acceptEither

    /*** 同 ApplyEither ,执行函数接口为 Consumer*/public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other,Consumer<? super T> action);public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action);public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action,Executor executor);

runAfterEither

    /*** 同 ApplyEither ,执行接口为 Runnable*/public CompletionStage<Void> runAfterEither(CompletionStage<?> other,Runnable action);public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action);public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor);

thenCompose

    /**** @param fn 函数接口,该接口返回新的 CompletionStage* @param <U> 返回的 CompletionStage 对象的泛型* @return 新的CompletionStage*/public <U> CompletionStage<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn);public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,Executor executor);

whenComplete

    /*** 无论当前解决正常还是异常结束,都会执行 BiConsumer 函数接口,* BiConsumer 函数接口第一个参数为当前阶段的结果(如果没有则为 null)* 第二个参数为当前阶段抛出的异常(如果没有则为 null)*/public CompletionStage<T> whenComplete(BiConsumer<? super T, ? super Throwable> action);public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action);public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action,Executor executor);

handle

    /*** 与 whenComplete 一样,但执行的函数接口为 BiFunction*/public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,Executor executor);

其他

/*** 当当前阶段计算异常完成时,执行该方法,并将异常作为参数传入函数接口* * @param fn Function 类型的函数接口* @return 返回新的 CompletionStage 对象*/public CompletionStage<T> exceptionally(Function<Throwable, ? extends T> fn);/*** CompletionStage 转换为 CompletableFuture 对象*/public CompletableFuture<T> toCompletableFuture();

如果某个阶段执行过程中出现异常,我们又没有通过 exceptionally 方法处理的话,CompletableFuture 将抛出 CompletionException 并停止运行。所以在某些情况下(某些异常不能影响之后的程序运行),我们需要使用 exceptionally 方法来处理阶段异常。

CompletionStage 的方法总结

  • 几乎所有的方法都返回了 CompletionStage ,这是链式调用的特性
  • 所有的方法参数都使用了函数式接口,一是可以 Lambda 表达式,二是根据不同的函数式接口的特性达到不同的执行要求
  • 不带 Async 的方法为同步方法,带 Async 的方法为异步方法
  • 这些方法分为 3 大类
    1. 阶段成功才执行函数式接口(函数式接口的参数为当前阶段的结果)
    2. 阶段失败才执行函数式接口(函数式接口的参数为当前阶段出现的异常)
    3. 无论阶段是否执行成功都会执行函数式接口(此类方法的函数式接口参数第一个参数为当前阶段返回值,第二个参数为当前阶段出现的异常)
  • 各个类型的方法都可以传入一个自定义的线程池。这可以避免所有的异步执行都使用同一个线程池,造成线程的争用。

CompletableFuture 实例化

CompletableFuture 有一个无参构造函数,但使用 CompletableFuture 时,不要通过该无参构造函数实例化 CompletableFuture 对象。我们正常应该使用如下几个静态方法获得 CompletableFuture 实例化对象

// 当此值为真,则默认使用 ForkJoinPool.commonPool(),否则是 ThreadPerTaskExecutorprivate static final boolean useCommonPool =(ForkJoinPool.getCommonPoolParallelism() > 1);/*** 默认线程池 -- ForkJoinPool.commonPool() * ThreadPerTaskExecutor 是每次提交 new Thread 的方式* 正常情况下,默认线程池就是 ForkJoinPool.commonPool() */private static final Executor asyncPool = useCommonPool ?ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {// asyncPool 线程池,默认就是 ForkJoinPool.commonPool()return asyncSupplyStage(asyncPool, supplier);}public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor) {return asyncSupplyStage(screenExecutor(executor), supplier);}public static CompletableFuture<Void> runAsync(Runnable runnable) {return asyncRunStage(asyncPool, runnable);}public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor) {return asyncRunStage(screenExecutor(executor), runnable);}public static <U> CompletableFuture<U> completedFuture(U value) {return new CompletableFuture<U>((value == null) ? NIL : value);}

CompletableFuture 其他 API 方法

// 等待计算完成获得结果
public T get() throws InterruptedException, ExecutionException// 等待计算完成获得结果,有最大等待时间
public T get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException// 等待计算完成获得结果 抛出 RuntimeException
public T join()// 直接获得结果,如果调用时计算没有完成,则使用参数值直接返回
public T getNow(T valueIfAbsent)// 如果调用时,计算阶段没有完成,则将get的返回值设置为 value
// 调用时计算已经完成,返回 true 否则,返回 false
public boolean complete(T value)// 如果调用时,计算阶段没有完成,则抛出给定的异常
// 调用时计算已经完成,返回 true 否则,返回 false
public boolean completeExceptionally(Throwable ex)// complete 和 completeExceptionally 如果同时调用,那么只有先调用的方法有效

complete 和 completeExceptionally

        CompletableFuture<Integer> cf = CompletableFuture.supplyAsync(()->{try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}return 1;});boolean success = cf.complete(2);boolean exSuccess = cf.completeExceptionally(new RuntimeException("测试异常"));log.debug("{}",success);log.debug("{}",exSuccess);log.debug("{}",cf.join());

执行结果:

11:42:07.657 [main] DEBUG com.yyoo.thread.future.Test1CompletableFuture - true
11:42:07.659 [main] DEBUG com.yyoo.thread.future.Test1CompletableFuture - false
11:42:07.660 [main] DEBUG com.yyoo.thread.future.Test1CompletableFuture - 2

CompletableFuture 示例

我们再用前面 FutureTask 实现的示例,造一台跑车需要5个任务,1.造骨架(1s);2. 造发动机(5s);3. 造轮胎(1s); 4. 组装(2s);用 CompletableFuture 实现如下:

import lombok.Data;
import lombok.extern.slf4j.Slf4j;import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;@Slf4j
public class TestCompletableFuture {public static void main(String[] args) {// 造骨架CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {log.debug("制造骨架");try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}return "无梁式骨架";});// 造发动机CompletableFuture<String> task2 =  CompletableFuture.supplyAsync(() -> {log.debug("制造发动机");try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}return "2.0T 引擎";});// 造轮胎CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> {log.debug("制造轮胎");try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}return "米其林轮胎";});System.out.println("前 3 个任务都已经启动,等待他们完成");// 以上三个任务都是异步执行的,且没有先后顺序// 此时我们需要使用 CompletableFuture 的静态方法 allOf,来确定以上3个任务已经执行完毕CompletableFuture<Void> d = CompletableFuture.allOf(task1,task2,task3);// 等待前3个任务执行完成CompletableFuture<Car> rsTask = d.thenApplyAsync((v) ->{log.debug("v 的值应该是 null:{}",v);log.debug("开始组装汽车");Car car = Car.assemble(task1.join(),task2.join(),task3.join());return car;});log.debug("造车完毕:" + rsTask.join());}@Datastatic class Car{/*** 骨架*/private String skeleton;/*** 发动机*/private String engine;/*** 轮胎*/private String tire;private Car(){}/*** 组装方法*/public final static Car assemble(String skeleton, String engine, String tire){Car car = new Car();car.setSkeleton(skeleton);car.setEngine(engine);car.setTire(tire);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}return car;}}}

CompletableFuture 的优势在于,使用起来是真的简单,而且可以很简单的组织各个任务之间的关系。比如我们新增一个 喷漆(1s) 的任务,它在 组装之前运行,但需要在骨架造好之后才能执行。此时我们只需要在 task1 的基础上添加一个 喷漆的任务就行

import lombok.Data;
import lombok.extern.slf4j.Slf4j;import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;@Slf4j
public class TestCompletableFuture {public static void main(String[] args) {// 造骨架CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {log.debug("制造骨架");try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}return "无梁式骨架";}).thenApply((skeleton) -> {log.debug("新增的喷漆操作");return "红色的" + skeleton;});// 造发动机CompletableFuture<String> task2 =  CompletableFuture.supplyAsync(() -> {log.debug("制造发动机");try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}return "2.0T 引擎";});// 造轮胎CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> {log.debug("制造轮胎");try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}return "米其林轮胎";});System.out.println("前 3 个任务都已经启动,等待他们完成");// 以上三个任务都是异步执行的,且没有先后顺序// 此时我们需要使用 CompletableFuture 的静态方法 allOf,来确定以上3个任务已经执行完毕CompletableFuture<Void> d = CompletableFuture.allOf(task1,task2,task3);// 等待前3个任务执行完成CompletableFuture<Car> rsTask = d.thenApplyAsync((v) ->{log.debug("v 的值应该是 null:{}",v);log.debug("开始组装汽车");Car car = Car.assemble(task1.join(),task2.join(),task3.join());return car;});log.debug("造车完毕:" + rsTask.join());}@Datastatic class Car{/*** 骨架*/private String skeleton;/*** 发动机*/private String engine;/*** 轮胎*/private String tire;private Car(){}/*** 组装方法*/public final static Car assemble(String skeleton, String engine, String tire){Car car = new Car();car.setSkeleton(skeleton);car.setEngine(engine);car.setTire(tire);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}return car;}}}

类似这样的工序组合,FutureTask 实现起来就比较困难了,但是我们使用 CompletableFuture 相对比较轻松,这也是 JDK 1.8 为什么又添加 CompletableFuture 的原因。

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

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

相关文章

excel中提取一串数字中的某几个数字

excel中提取一串数字中的某几个数字 提取一串数字中的某几个数字&#xff0c;使用公式函数截取数据 LEFT函数&#xff1a;用于截取单元格左边的字符&#xff0c;例如“LEFT(A1,5)”会返回A1单元格中的前5个字符。RIGHT函数&#xff1a;用于截取单元格右边的字符&#xff0c;例…

软件工程知识梳理6-运行和维护

软件维护需要的工作量很大&#xff0c;大型软件的维护成本高达开发成本的4倍左右。所以&#xff0c;软件工程的主要目的就是要提高软件的可维护性&#xff0c;减少软件维护所需要的工作量&#xff0c;降低软件系统的总成本。 定义&#xff1a;软件已经交付使用之后&#xff0c;…

真机调试,微信小程序,uniapp项目在微信开发者工具中真机调试,手机和电脑要连同一个wifi,先清空缓存,页面从登录页进入,再点真机调试,这样就不会报错了

微信小程序如何本地进行真机调试&#xff1f;_unity生成的微信小程序怎么在电脑上真机测试-CSDN博客 微信小程序 真机调试 注意事项 uniapp项目在微信开发者工具中真机调试&#xff0c;手机和电脑要连同一个wifi&#xff0c;先清空缓存&#xff0c;页面从登录页进入&#xf…

Flask 入门2:路由

1. 前言 在上一节中&#xff0c;我们使用到了静态路由&#xff0c;即一个路由规则对应一个 URL。而在实际应用中&#xff0c;更多使用的则是动态路由&#xff0c;它的 URL是可变的。 2. 定义一个很常见的路由地址 app.route(/user/<username>) def user(username):ret…

安装 vant-ui 实现底部导航栏 Tabbar

本例子使用vue3 介绍 vant-ui 地址&#xff1a;介绍 - Vant 4 (vant-ui.github.io) Vant 是一个轻量、可定制的移动端组件库 安装 通过 npm 安装&#xff1a; # Vue 3 项目&#xff0c;安装最新版 Vant npm i vant # Vue 2 项目&#xff0c;安装 Vant 2 npm i vantlatest-v…

选型 之 工业相机篇

一、概述 23年24年行情不会好&#xff0c;公司各种想办法裁员&#xff0c;在大陆这个大熔炉中只能不断地提炼。我个人主要是在工业领域做2D图像算法和3D算法&#xff0c;但是现在出去都需要全能人才 方案、算法、运动控制等&#xff0c;我目前最大的短板就是方案&#xff0c;在…

【Javaweb程序设计】【C00165】基于SSM的高考志愿辅助填报系统(论文+PPT)

基于SSM的高考志愿辅助填报系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的高考志愿辅助填报系统 本系统分为前台系统模块、后台管理员模块以及后台学生模块 前台系统模块&#xff1a;当游客打开系统的网址后&…

springboot中获取配置文件中属性值的几种方式

目录 第一章、使用Value注解第二章、使用PropertySource注解第三章、使用Configurationproperties注解第四章、使用Java Properties类第五章、使用Environment接口 友情提醒: 先看文章目录&#xff0c;大致了解文章知识点结构&#xff0c;点击文章目录可直接跳转到文章指定位置…

Coremail启动鸿蒙原生应用开发,打造全场景邮件办公新体验

1月18日&#xff0c;华为在深圳举行鸿蒙生态千帆启航仪式&#xff0c;Coremail出席仪式并与华为签署鸿蒙合作协议&#xff0c;宣布正式启动鸿蒙原生应用开发。作为首批拥抱鸿蒙的邮件领域伙伴&#xff0c;Coremail的加入标志着鸿蒙生态版图进一步完善。 Coremail是国内自建邮件…

Unity异步加载场景

前言 Unity中常见的加载场景就是异步加载场景&#xff0c;此博客对异步加载场景进行详细介绍 简单易懂好用。含有加载进度&#xff0c;加载动画等。&#xff08;文末附工程&#xff09; 代码分析 主要脚本MaskPanel &#xff0c;作为单例存在于场景中&#xff0c;下面对此脚…

Angular组件(二) 分割面板ShrinkSplitter

Angular组件(二) 分割面板ShrinkSplitter 前言 在Angular组件(一) 分割面板ShrinkSplitter文章中我们实现了Splitter组件&#xff0c;后来在业务场景中发现在开关右侧容器和底部容器时&#xff0c;使用起来不方便&#xff0c;ngModel绑定的值始终是左侧容器和顶部容器的大小&…

Flutter App 生命周期观察监听

前言 本文主要讲解两种 Flutter生命周期观察监听 方式一&#xff1a;Flutter SDK 3.13 之前的方式&#xff0c;WidgetsBindingObserver&#xff1b; 方式二&#xff1a;Flutter SDK 3.13 开始的新方式&#xff0c;AppLifecycleListener&#xff1b; 测试平台&#xff1a;IO…

走进水稻种植教学基地可视化:科技与农业知识的完美结合

随着科技的不断发展&#xff0c;农业领域也在不断创新和进步。水稻种植教学基地可视化系统是一种基于现代信息技术手段的教学方式&#xff0c;通过虚拟现实、3D建模等技术&#xff0c;将水稻种植的全过程进行模拟和展示。这种教学方式打破了传统农业教学的局限性&#xff0c;使…

idea中yml文件没有提示解决办法

两步解决yml文件不显示提示&#xff0c;yaml文件显示提示问题 1、在插件中心&#xff0c;先下载下图两个插件 2、在Editor》File Types新增文件类型&#xff0c;文件名匹配规则需要将yaml和yml的都加上&#xff0c;加好之后&#xff0c;重启idea&#xff0c;即刻生效。

C++进阶--继承

概念 继承&#xff0c;允许一个类&#xff08;称为子类或派生类&#xff09;从另一个类&#xff08;称为父类或基类&#xff09;继承属性和方法。 继承的主要目的是实现代码的重用和构建类之间的层次关系。通过继承&#xff0c;子类可以获得父类的特性&#xff0c;包括数据成员…

嵌合抗体介绍-泰克生物

一&#xff0e;嵌合抗体简介 人-鼠嵌合抗体&#xff0c;即抗体的可变区来自鼠单克隆抗体&#xff0c;而恒定区则来自人的抗体。它是通过从杂交瘤细胞分离出功能性可变区基因&#xff0c;与人Ig恒定区基因连接,插入适当表达载体&#xff0c;转染宿主细胞表达产生。嵌合抗体既保留…

centOS+nodejs+mysql阿里云部署前后端个人网站

centOSnodejsmysql阿里云部署前后端个人网站 参考&#xff1a; 部署NodeExpressMySQL项目到阿里云轻量应用服务器 阿里云轻量应用服务器部署Node.jsReactMongoDB前后端分离项目 参考&#xff1a;在阿里云上部署nodejs服务 https 部署的原理就是你在本地测试的时候在地址栏&am…

给零基础朋友的编程课12 代码

给零基础朋友的编程课12 下 - 仿制品7 案例_哔哩哔哩_bilibili 源代码&#xff1a; // 色表 // 桃红 254,181,167 // 粉红 255,208,199void setup() {size(1000,750);background(254,181,167); }void draw() {// 绘制画框stroke(255,208,199);strokeWeight(28);noFill();rect…

Cmake编译Opencv3.3.1遇到有些文件无法下载的错误解决:

前言&#xff1a; 对于&#xff0c;opencv有些配置文件错误并未致命&#xff0c;所以&#xff0c;有错误也不影响后续的编译&#xff1a;但是&#xff0c;后引用如果要用&#xff0c;在回过头来还是要解决的。 问题表述&#xff1a; 比如&#xff0c;有些文件下载的错误&am…