当前位置: 首页 > news >正文

CompletableFuture 基本用法


一、 CompletableFuture简介

CompletableFuture 是 Java 8 引入的一个功能强大的类,用于异步编程和并发处理。它提供了丰富的 API 来处理异步任务的结果,支持函数式编程风格,并允许通过链式调用组合多个异步操作。

二、CompletableFuture中的方法

1. 创建 CompletableFuture 对象

  • CompletableFuture.supplyAsync(Supplier<U> supplier): 异步执行给定的 Supplier 函数,并返回一个新的 CompletableFuture,当函数完成时,该 CompletableFuture 将以函数的结果完成。

示例代码:

import java.util.concurrent.CompletableFuture;  
import java.util.concurrent.ExecutionException;  public class CompletableFutureExample {  public static void main(String[] args) throws ExecutionException, InterruptedException {  // 使用 supplyAsync 异步执行一个计算  CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  // 模拟一个耗时的计算  try {  Thread.sleep(2000); // 等待 2 秒  } catch (InterruptedException e) {  Thread.currentThread().interrupt();  throw new IllegalStateException(e);  }  return "Hello, CompletableFuture!";  });  // 主线程可以继续执行其他任务,而不需要等待上面的计算完成  // 当需要结果时,可以调用 get() 方法(这会阻塞,直到结果可用)  String result = future.get(); // 这里会等待上面的计算完成,然后返回结果  System.out.println(result); // 输出 "Hello, CompletableFuture!"  }  
}

 这里顺便讲一下get方法:

(1)get方法的作用是等待此 CompletableFuture 完成,然后返回其结果(或抛出异常)。

(2)get方法的返回值是CompletableFuture<T> 里面的泛型的类型;比如上面的例子中CompletableFuture<String> 泛型是String 所以这里future.get()的返回值是String类型

示例1:

import java.util.concurrent.CompletableFuture;  
import java.util.concurrent.ExecutionException;  public class CompletableFutureExample {  public static void main(String[] args) throws ExecutionException, InterruptedException {  CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  ...... });  String result = future.get(); // 因为 CompletableFuture<String> 泛型是String 所以这里future.get()的返回值是String类型}  
}

示例2:

import java.util.concurrent.CompletableFuture;  
import java.util.concurrent.ExecutionException;  public class CompletableFutureExample {  public static void main(String[] args) throws ExecutionException, InterruptedException {  CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {  ...... });  Integerresult = future.get(); // 因为 CompletableFuture<Integer> 泛型是Integer所以这里future.get()的返回值是Integer类型}  
}

另外,supplyAsync 方法还有一个重载版本,它接受一个 Executor 作为参数,允许你指定用于执行计算的线程池。这对于控制异步任务的执行环境非常有用。例如:

ExecutorService executor = Executors.newFixedThreadPool(10); // 创建一个固定大小的线程池  
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  // ... 耗时的计算 ...  
}, executor); // 使用指定的线程池执行计算

 

  • CompletableFuture.runAsync(Runnable runnable): 异用于异步地执行一个 Runnable 任务,并且不返回任何结果(返回类型为 CompletableFuture<Void>)。这在你只关心任务的执行而不关心其返回值时非常有用。
import java.util.concurrent.CompletableFuture;  public class CompletableFutureExample {  public static void main(String[] args) {  // 使用默认的 ForkJoinPool 异步执行任务  CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {  // 模拟一个耗时的任务  try {  Thread.sleep(2000); // 假设任务需要2秒来完成  } catch (InterruptedException e) {  Thread.currentThread().interrupt(); // 恢复中断状态  throw new RuntimeException(e); // 抛出运行时异常以便可以看到异常信息  }  System.out.println("任务执行完毕!");  });  // 在主线程中继续执行其他操作,不需要等待上面的任务完成  System.out.println("主线程继续执行...");  // 如果你想等待任务完成,可以调用 future.join() 或 future.get(),但请注意这可能会阻塞当前线程  // 这里我们只是打印出任务是否已经完成  System.out.println("任务是否完成: " + future.isDone());  // 注意:由于任务是异步执行的,所以上面的 isDone() 方法可能返回 false,因为任务可能还没有完成  // 你可以通过 future.thenRun(...) 来添加在任务完成后要执行的代码  future.thenRun(() -> System.out.println("任务完成后执行的代码"));  // 注意:thenRun 中的代码也是异步执行的,并且可能在主线程之后执行  // 为了确保主线程在异步任务完成后才结束,可以调用 future.join()  try {  future.join(); // 等待异步任务完成  } catch (Exception e) {  e.printStackTrace();  }  // 现在可以确定异步任务已经完成  System.out.println("主线程结束");  }  
}

 

2. 处理异步任务的结果

  • thenApply(Function<? super T,? extends U> fn): 当此 CompletableFuture 完成时,将结果应用于给定的函数,并返回一个新的 CompletableFuture,该 CompletableFuture 将以函数的结果完成。
  • thenAccept(Consumer<? super T> action): 当此 CompletableFuture 完成时,对结果执行给定的操作,然后返回 this
  • thenRun(Runnable action): 当此 CompletableFuture 完成时,执行给定的操作,然后返回 this

3. 组合多个 CompletableFuture

  • thenCombine(CompletableFuture<? extends U> other, BiFunction<? super T,? super U,? extends V> fn): 当此 CompletableFuture 和另一个给定的 CompletableFuture 都完成时,使用这两个结果作为参数应用给定的函数,并返回一个新的 CompletableFuture,该 CompletableFuture 将以函数的结果完成。

传统写法:

    public static void main(String[] args) {CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {// 模拟耗时计算,返回结果try {Thread.sleep(1000); // 等待1秒} catch (InterruptedException e) {e.printStackTrace();}return 42; // 假设这是第一个任务的结果});CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {// 模拟耗时计算,返回结果try {Thread.sleep(500); // 等待0.5秒} catch (InterruptedException e) {e.printStackTrace();}return 13; // 假设这是第二个任务的结果});// 使用 thenCombine 合并两个任务的结果CompletableFuture<Integer> resultFuture = future1.thenCombine(future2, (a, b) -> a + b);// 等待结果并打印Integer join = resultFuture.join();System.out.println(join);//55}

 链式调用:

   public static void main(String[] args) {CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {// 模拟耗时计算,返回结果try {Thread.sleep(1000); // 等待1秒} catch (InterruptedException e) {e.printStackTrace();}return 42; // 假设这是第一个任务的结果}).thenCombine(CompletableFuture.supplyAsync(() -> {// 模拟耗时计算,返回结果try {Thread.sleep(500); // 等待0.5秒} catch (InterruptedException e) {e.printStackTrace();}return 13; // 假设这是第二个任务的结果}),(res1,res2)->{int total = res1 + res2;return total;});// 等待结果并打印Integer total = future1.join();System.out.println(total); // 42+13=55}
  • thenCompose(Function<? super T,? extends CompletionStage<U>> fn): 当此 CompletableFuture 完成时,对其结果应用给定的函数,该函数返回一个新的 CompletionStage,然后返回表示该 CompletionStage 结果的 CompletableFuture

 thenCompose是 CompletableFuture 类中的一个方法,它允许你将一个 CompletableFuture 的结果用作另一个 CompletableFuture 计算的输入,从而链式地组合多个异步操作。

使用场景

thenCompose 适用于以下场景:

  1. 连续异步处理:当你需要对一个异步操作的结果进行另一个异步操作时,可以使用 thenCompose 将这两个操作连接在一起。
  2. 避免嵌套:使用 thenCompose 可以避免 Future 的嵌套,使得代码更加简洁和平坦。

传统写法:

package com.etime.test;import java.util.concurrent.CompletableFuture;/*** @Date 2024/6/22 20:08* @Author liukang**/
public class SupplyAsyncTest {public static void main(String[] args) {CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {// 模拟耗时计算,返回结果try {Thread.sleep(1000); // 等待1秒} catch (InterruptedException e) {e.printStackTrace();}return 42; // 假设这是计算的结果});CompletableFuture<Integer> resultFuture = future.thenCompose(value -> {// 使用前一个任务的结果(value)作为输入,创建并返回一个新的CompletableFuturereturn CompletableFuture.supplyAsync(() -> value * 2); // 将结果乘以2});// 等待结果并打印Integer join = resultFuture.join();System.out.println(join);//84}}

 链式调用:

  public static void main(String[] args) {CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {// 模拟耗时计算,返回结果try {Thread.sleep(1000); // 等待1秒} catch (InterruptedException e) {e.printStackTrace();}return 42; // 假设这是计算的结果}).thenCompose(value -> CompletableFuture.supplyAsync(()->{return value*2;}));Integer join = future.join();System.out.println(join);}
  • allOf(CompletableFuture<?>... cfs): 返回一个新的 CompletableFuture,该 CompletableFuture 在所有给定的 CompletableFuture 都完成时完成。
  • anyOf(CompletableFuture<?>... cfs): 返回一个新的 CompletableFuture,该 CompletableFuture 在任何一个给定的 CompletableFuture 完成时完成。

4. 异常处理

  • exceptionally(Function<Throwable,? extends T> fn): 当此 CompletableFuture 异常完成时,应用给定的函数到异常,并返回一个新的 CompletableFuture,该 CompletableFuture 将以函数的结果完成。

exceptionally是Java中CompletableFuture类的一个方法,用于处理异步操作中可能发生的异常。通过调用exceptionally方法并定义一个异常处理函数,你可以确保在异步操作出现异常时能够优雅地处理,并返回一个默认值或其他的值。

    public static void main(String[] args) {CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {// 模拟耗时计算,返回结果try {Thread.sleep(1000); // 等待1秒} catch (InterruptedException e) {e.printStackTrace();}return 42; // 假设这是计算的结果}).thenCompose(value -> CompletableFuture.supplyAsync(()->{return value/0;})).exceptionally((e -> {System.out.println("发生了异常");System.out.println("异常信息为:"+e.getMessage());return null;}));// 等待结果并打印Integer join = future.join();System.out.println(join);}

 

  • handle(BiFunction<? super T,Throwable,? extends U> fn): 当此 CompletableFuture 完成时,无论是正常完成还是异常完成,都将结果和异常(如果有)应用于给定的函数,并返回一个新的 CompletableFuture,该 CompletableFuture 将以函数的结果完成。

5. 其他方法

  • join(): 等待此 CompletableFuture 完成,然后返回其结果(或抛出异常),是get方法的升级版。
  • get(): 等待此 CompletableFuture 完成,然后返回其结果(或抛出异常)。与 join() 类似,但可能抛出 InterruptedException 和 ExecutionException

join()和get()的区别

  1. 抛出异常的方式不同
    • get 方法会抛出 ExecutionException 异常(如果异步任务执行过程中出现异常),这个异常是具体的,需要显式捕获。此外,如果线程在等待过程中被中断,它还会抛出 InterruptedException
    • join 方法则会抛出 CompletionException 异常(如果异步任务执行过程中出现异常),这个异常是 unchecked 的,因此不需要显式捕获。如果线程在等待过程中被中断,它不会抛出 InterruptedException,因为它本身是不可中断的。

  1. 方法调用限制不同
    • get 方法可以在调用时设置等待的超时时间,如果超时还没有获取到结果,就会抛出 TimeoutException 异常。这使得 get 方法在使用时具有更大的灵活性。
    • join 方法则没有这样的超时机制,一旦调用就必须等待任务执行完成才能返回结果。它不能被中断,除非异步任务本身完成。

  1. 返回结果类型不同
    • get 方法返回的是异步任务的执行结果,该结果是泛型类型 T 的,需要强制转换才能获取真正的结果。
    • join 方法同样返回的是异步任务的执行结果,但不需要强制类型转换,因为其结果就是泛型类型 T
  2. 推荐使用方式不同
    • join 方法通常被推荐用于 CompletableFuture,因为它没有受到 interrupt 的干扰,不需要捕获异常,也不需要强制类型转换。这使得代码更加简洁和易于阅读。
    • get 方法则提供了更多的控制选项,如设置超时时间,这在某些需要更细粒度控制的场景下可能是有用的。但需要注意的是,它可能会抛出 InterruptedException,这需要在代码中显式处理。
  3. 阻塞行为
    • 两者都是阻塞方法,都会阻塞当前线程直到异步任务完成。但如上所述,join 方法是不可中断的,而 get 方法可以被中断。

总结来说,join 和 get 方法在 CompletableFuture 中都用于获取异步任务的执行结果,但在抛出异常的方式、方法调用限制、返回结果类型以及推荐使用方式等方面存在显著的区别。根据具体的需求和场景,可以选择使用 join 或 get 方法。

  • complete(T value): 如果尚未完成,则尝试以给定值完成此 CompletableFuture
  • completeExceptionally(Throwable ex): 如果尚未完成,则尝试以给定异常完成此 CompletableFuture

这些只是 CompletableFuture 提供的一部分方法,但它已经足够强大,可以处理大多数异步编程和并发处理的场景。


相关文章:

CompletableFuture 基本用法

一、 CompletableFuture简介 CompletableFuture 是 Java 8 引入的一个功能强大的类&#xff0c;用于异步编程和并发处理。它提供了丰富的 API 来处理异步任务的结果&#xff0c;支持函数式编程风格&#xff0c;并允许通过链式调用组合多个异步操作。 二、CompletableFuture中…...

网页如何发布到服务器上

将网页发布到服务器上的过程涉及多个步骤&#xff0c;包括准备阶段、选择托管提供商、发布网站等。12 准备阶段&#xff1a; 确保在本地开发环境中对网站进行了充分的测试&#xff0c;包括功能测试、性能测试和安全测试。 检查Web.config文件&#xff0c;确保所有的配置设置…...

Jenkins简要说明

Jenkins 是一个开源的持续集成和持续部署&#xff08;CI/CD&#xff09;工具&#xff0c;广泛用于自动化软件开发过程中的构建、测试和部署等任务。它是基于Java开发的&#xff0c;因此可以在任何支持Java的平台上运行&#xff0c;并且能够与各种操作系统、开发工具和插件无缝集…...

C# 比较基础知识:最佳实践和技巧

以下是一些在 C# 中进行比较的技巧和窍门的概述。 1. 比较原始类型 对于原始类型&#xff08;int、double、char 等&#xff09;&#xff0c;可以使用标准比较运算符。 int a 5; int b 10; bool isEqual (a b); // false bool isGreater (a > b); // false bool is…...

Ansible 自动化运维实践

随着 IT 基础设施的复杂性不断增加&#xff0c;手动运维已无法满足现代企业对高效、可靠的 IT 运维需求。Ansible 作为一款开源的自动化运维工具&#xff0c;通过简洁易用的 YAML 语法和无代理&#xff08;agentless&#xff09;架构&#xff0c;极大简化了系统配置管理、应用部…...

红队攻防渗透技术实战流程:中间件安全:IISNGINXAPACHETOMCAT

红队攻防渗透实战 1. 中间件安全1.1 中间件-IIS-短文件&解析&蓝屏等1.2 中间件-Nginx-文件解析&命令执行等1.2.1 后缀解析 文件名解析1.2.2 cve_2021_23017 无EXP有POC1.2.3 cve_2017_7529 意义不大1.3 中间件-Apache-RCE&目录遍历&文件解析等1.3.1 cve_20…...

如何卸载宝塔面板?

宝塔官方有提供宝塔面板的卸载命令&#xff0c;使用这个卸载命令&#xff0c;我们就能将宝塔面板卸载掉。 这里有一点需要注意的&#xff0c;如果卸载宝塔面板的同时&#xff0c;也希望将 Nginx、MySQL、PHP 等组件卸载掉&#xff0c;那么我们应该先在宝塔面板里面卸载掉以上软…...

python入门基础知识(错误和异常)

本文部分内容来自菜鸟教程Python 基础教程 | 菜鸟教程 (runoob.com) 本人负责概括总结代码实现。 以此达到快速复习目的 目录 语法错误 异常 异常处理 try/except try/except...else try-finally 语句 抛出异常 用户自定义异常 内置异常类型 常见的标准异常类型 语法…...

迈巴赫S480升级增强现实AR抬头显示hud比普通抬头显示HUD更好用吗

增强AR实景抬头显示HUD&#xff08;Augmented Reality Head-Up Display&#xff09;是一种更高级的驾驶辅助技术&#xff0c;相比于普通抬头显示HUD&#xff0c;它提供了更丰富、更具沉浸感的驾驶体验。以下是它比普通抬头显示HUD多的一些功能&#xff1a; • 信息呈现方式&am…...

vivado、vitis2022安装及其注意事项(省时、省空间)

1、下载 AMD官网-资源与支持-vivado ML开发者工具&#xff0c;或者vitis平台&#xff0c; 下载的时候有个官网推荐web安装&#xff0c;亲测这个耗时非常久&#xff0c;不建议使用&#xff0c;还是直接下载89G的安装包快。 注意&#xff1a;安装vitis平台会默认安装vivado&…...

【自动驾驶】ROS小车系统

文章目录 小车组成轮式运动底盘的组成轮式运动底盘的分类轮式机器人的控制方式感知传感器ROS决策主控ROS介绍ROS的坐标系ROS的单位机器人电气连接变压模块运动底盘的电气连接ROS主控与传感器的电气连接ROS主控和STM32控制器两种控制器的功能运动底盘基本组成电池电机控制器与驱…...

mysql学习——多表查询

多表查询 内连接外连接自连接自连接查询联合查询 子查询 学习黑马MySQL课程&#xff0c;记录笔记&#xff0c;用于复习。 添加外键 alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);多表查询 select * from emp , dept where emp…...

【Gradio】如何设置 Gradio 数据框的样式

简介 数据可视化是数据分析和机器学习的关键方面。Gradio DataFrame 组件是一种流行的方式&#xff0c;在网络应用程序中显示表格数据&#xff08;特别是以 pandas DataFrame 对象的形式&#xff09;。 本文将探讨 Gradio 的最新增强功能&#xff0c;这些功能允许用户整合 pand…...

【ThreeJS】Threejs +Vue3 开发基础

目前流行的前端3D框架以以Three.js、Babylon.js、A-Frame和ThingJS为例&#xff1a; 1.Three.js 功能&#xff1a; 提供了大量的3D功能&#xff0c;包括基本几何形状、材质、灯光、动画、特效等。 易用性&#xff1a; 功能强大且易于使用&#xff0c;抽象了复杂的底层细节&…...

cocos 如何使用九宫格图片,以及在微信小程序上失效。

1.在图片下方&#xff0c;点击edit。 2.拖动线条&#xff0c;使四角不被拉伸。 3.使用。 其他 在微信小程序上失效&#xff0c;需要将packable合图功能取消掉。...

Spring企业开发核心框架

一、框架前言 1、总体技术体系 单一架构 一个项目&#xff0c;一个工程&#xff0c;导出为一个war包&#xff0c;在一个Tomcat上运行。也叫all in one. 单一架构&#xff0c;项目主要应用技术框架为&#xff1a;Spring&#xff0c;SpringMVC&#xff0c;Mybatis等 分布式架构…...

Scrum团队在迭代中如何处理计划外的工作

认为 Scrum 团队不做计划其实是一个误区&#xff0c;实际上很多 Scrum 团队在冲刺计划会议以及在细化工作项时均会进行详细规划。此外&#xff0c;他们还会创建一个路线图&#xff0c;以便显示他们在多个冲刺中的计划。 Scrum 团队需要经常进行计划&#xff0c;以便在不断变化…...

桌面识别技术革新交互,展厅互动体验步入新时代!

在这股科技浪潮中&#xff0c;物体识别桌作为一种前沿的人机交互设备&#xff0c;其影响力尤为显著。它不仅颠覆了传统展厅内容的交互模式&#xff0c;更以科技之力为观众呈现了一场前所未有的视觉盛宴。那么&#xff0c;接下来&#xff0c;就让我们一起深入探索&#xff0c;物…...

书生·浦语大模型LagentAgentLego智能体应用搭建 第二期

文章目录 智能体概述智能体的定义智能体组成智能体范式 环境配置Lagent&#xff1a;轻量级智能体框架实战Lagent Web Demo用 Lagent 自定义工具 AgentLego&#xff1a;组装智能体“乐高”直接使用AgentLego作为智能体工具使用 用 AgentLego 自定义工具 智能体概述 智能体的定义…...

具有 Hudi、MinIO 和 HMS 的现代数据湖

Apache Hudi 已成为管理现代数据湖的领先开放表格式之一&#xff0c;直接在现代数据湖中提供核心仓库和数据库功能。这在很大程度上要归功于 Hudi 提供了表、事务、更新/删除、高级索引、流式摄取服务、数据聚类/压缩优化和并发控制等高级功能。 我们已经探讨了 MinIO 和 Hudi…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积

1.题目介绍 给定一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O…...

通过MicroSip配置自己的freeswitch服务器进行调试记录

之前用docker安装的freeswitch的&#xff0c;启动是正常的&#xff0c; 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...