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

《异步编程之美》— 全栈修仙《Java 8 CompletableFuture 对比 ES6 Promise 以及Spring @Async》

   哈喽,大家好!在平常开发过程中会遇到许多意想不到的坑,本篇文章就记录在开发过程中遇到一些常见的问题,看了许多博主的异步编程,我只能说一言难尽。本文详细的讲解了异步编程之美,是不可多得的好文,建议细细品尝。文章末尾附有思维导图以及参考文档。

        首先,我们得弄明白什么异步?JavaScript 语言的执行环境是单线程的,异步编程对于 JavaScript 来说必不可少。JavaScript 传统异步解决方案主要是通过回调函数,而回调函数最大的问题就是 Callback Hell。所以 ES6 标准提供的 Promise 对象,专门用于解决异步编程的问题。而 Java 语言是一个支持多线程的语言,语法以同步为主,在实际开发中很少需要用到大量的异步编程。但是要想追求更高的性能,异步通常是更好的选择。例如 Servlet 3 的异步支持、Spring 5 提供的 Spring WebFlux 等,都是为了追求更高的性能。和 JavaScript 一样,传统的 Callback 方式处理 Java 异步也会有 Callback Hell 问题,所以在 Java 8 中新增了和 ES6 的 Promise 类似的对象: java.util.concurrent.CompletableFuture 。

        其次,类似与前端 Promise 代表 异步对象,类似Java中的 CompletableFuture。Promise 是现代 JavaScript 中异步编程的基础,是一个由异步函数返回的可以向我们指示当前操作所处的状态的对象。在 Promise 返回给调用者的时候,操作往往还没有完成,但 Promise 对象可以让我们操作最终完成时对其进行处理(无论成功还是失败)。

        最后,@Async是Spring提供的一个异步注解,默认采用SimpleAsyncTaskExecutor线程池,该线程池不是真正意义上的线程池。使用此线程池无法实现线程重用,每次调用都会新建一条线程。若系统中不断的创建线程,最终会导致系统占用内存过高,引发OutOfMemoryError错误。所以我们得自定义线程池

public void execute(Runnable task, long startTimeout) {Assert.notNull(task, "Runnable must not be null");Runnable taskToUse = this.taskDecorator != null ? this.taskDecorator.decorate(task) : task;//判断是否开启限流,默认为否if (this.isThrottleActive() && startTimeout > 0L) {//执行前置操作,进行限流this.concurrencyThrottle.beforeAccess();this.doExecute(new SimpleAsyncTaskExecutor.ConcurrencyThrottlingRunnable(taskToUse));} else {//未限流的情况,执行线程任务this.doExecute(taskToUse);}}protected void doExecute(Runnable task) {//不断创建线程Thread thread = this.threadFactory != null ? this.threadFactory.newThread(task) : this.createThread(task);thread.start();
}//创建线程
public Thread createThread(Runnable runnable) {//指定线程名,task-1,task-2...Thread thread = new Thread(this.getThreadGroup(), runnable, this.nextThreadName());thread.setPriority(this.getThreadPriority());thread.setDaemon(this.isDaemon());return thread;
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.ThreadPoolExecutor;/*** 描述:异步配置2* */
@Configuration
@EnableAsync    // 可放在启动类上或单独的配置类
public class AsyncConfiguration2 {@Bean(name = "asyncPoolTaskExecutor")public ThreadPoolTaskExecutor executor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();//核心线程数taskExecutor.setCorePoolSize(10);//线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程taskExecutor.setMaxPoolSize(100);//缓存队列taskExecutor.setQueueCapacity(50);//许的空闲时间,当超过了核心线程出之外的线程在空闲时间到达之后会被销毁taskExecutor.setKeepAliveSeconds(200);//异步方法内部线程名称taskExecutor.setThreadNamePrefix("async-");/*** 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略* 通常有以下四种策略:* ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。* ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。* ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)* ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功*/taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());taskExecutor.initialize();return taskExecutor;}
}
//使用异步编程操作大量数据 List<Long> collect = examSysQuestionReals.stream().map(i -> CompletableFuture.supplyAsync(i::getId)).map(CompletableFuture::join).collect(Collectors.toList());/** 与普通stream.map()对比优化后的代码中,stream.map() 分为两步:
第一次 map():将每个 examSysQuestionReal 对象转换为一个 CompletableFuture,该异步任务在后台线程(通常是线程池中的线程)中执行 i.getId() 方法。这意味着多个 getId() 操作可以同时进行,实现了并行处理。
第二次 map():对上一步产生的 CompletableFuture 列表调用 join() 方法,等待所有异步任务完成并获取它们的结果。这些结果随后被收集到一个新的 List<Long> 中。
区别总结:
执行模式:直接使用 stream.map() 时,映射操作是同步且顺序执行的;优化后的代码则利用 CompletableFuture 实现了异步并行执行。
性能:对于耗时的 getId() 操作(例如涉及数据库查询或其他远程服务调用),优化后的代码能利用多核处理器的能力,通过并行处理显著减少整体处理时间。而直接使用 stream.map() 只能在单一线程中顺序执行,无法利用多核优势,处理大量数据或耗时操作时可能效率较低。
资源消耗:优化后的代码引入了异步任务,可能增加线程池的使用和上下文切换的开销。然而,只要 getId() 操作的并行效益超过这些开销,总体上仍能提高性能。直接使用 stream.map() 无需额外的线程池资源,但在处理大规模或耗时任务时可能会因无法并行而显得低效。
综上所述,直接使用 stream.map() 适用于同步、轻量级且无需并行处理的场景。而优化后的代码结合 CompletableFuture 更适合处理可能耗时、受益于并行计算的任务,以提高程序的整体执行效率。在实际应用中,应根据具体需求和性能指标选择合适的方法///进阶,@Async与CompletableFuture
/**
在这个示例中,performComplexAsyncTask() 方法被标记为 @Async,由 Spring 异步执行。方法内部使用 CompletableFuture 实现了多个步骤的异步任务创建、组合和结果处理。这样既利用了 Spring 的异步方法抽象,又充分利用了 CompletableFuture 的灵活性和控制力。
总结来说,CompletableFuture 和 @Async 可以分别进行练习,然后在实践中结合使用,以适应不同复杂度和需求的异步编程场景。
/
@Service
public class AsyncService {@Asyncpublic CompletableFuture<String> performComplexAsyncTask(String input) {// Step 1: 异步获取数据CompletableFuture<String> dataFuture = CompletableFuture.supplyAsync(() -> {// 这里可能是耗时的数据库查询或网络请求return fetchData(input);});// Step 2: 异步处理数据,依赖于第一步的结果CompletableFuture<String> processedDataFuture = dataFuture.thenApply(this::processData);// Step 3: 异步发送通知,不依赖于前两步的结果,可以并发执行CompletableFuture<Void> notificationFuture = CompletableFuture.runAsync(() -> {sendNotification();});// Step 4: 等待所有异步操作完成return CompletableFuture.allOf(processedDataFuture, notificationFuture).thenApply(unused -> {// 返回最终处理结果或相关信息return processedDataFuture.join();});}// 其他方法:fetchData(), processData(), sendNotification()
}/**在实际应用中,CompletableFuture 和 @Async 可以结合使用,发挥各自的优势。
使用 @Async 注解标记那些业务逻辑相对独立、适合作为单独任务执行的方法。这有助于简化代码结构,将异步处理逻辑从业务逻辑中分离出来。
在 @Async 方法内部,可以使用 CompletableFuture 构建更复杂的异步流程,如组合多个异步操作、处理中间结果等。这种方法结合了 Spring 的高级抽象与 CompletableFuture 的强大功能。
/  

现在我们从线程池配置类实战一次

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;/*** 配置线程池* */
@Configuration
@EnableAsync
public class ExecutorConfig {/* 获取快递单号线程池,量大 配置 start */// 维持最小的线程数private int corePoolSizeAutoTrace = 10;// 最大的活动线程数private int maxPoolSizeAutoTrace = 50;// 排队的线程数private int queueCapacityAutoTrace = maxPoolSizeAutoTrace * 2;// 线程长时间闲置关闭的时间,单位秒private int keepAliveSecondsAutoTrace = 1 * 60 * 60;private String asyncTask = "asyncTask-";private String eventExecute = "eventExecute-";private String pushStatus = "pushStatus-";// 异常记录private int recordCoreSize = 5;// 最大的活动线程数private int recordMaxPoolSize = 10;// 异常记录private int orderCoreSize = 10;// 最大的活动线程数private int orderMaxPoolSize = 100;/*** 异步任务线程池* @return 执行器*/@Beanpublic Executor asyncTask() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(corePoolSizeAutoTrace);executor.setMaxPoolSize(maxPoolSizeAutoTrace);executor.setQueueCapacity(queueCapacityAutoTrace);executor.setThreadNamePrefix(asyncTask);executor.setKeepAliveSeconds(keepAliveSecondsAutoTrace);executor.setThreadGroupName(asyncTask);executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}@Beanpublic Executor eventExecute() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(corePoolSizeAutoTrace);executor.setMaxPoolSize(maxPoolSizeAutoTrace);executor.setQueueCapacity(queueCapacityAutoTrace);executor.setThreadNamePrefix(eventExecute);executor.setKeepAliveSeconds(keepAliveSecondsAutoTrace);executor.setThreadGroupName(eventExecute);executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}@Beanpublic Executor pushStatus() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(corePoolSizeAutoTrace);executor.setMaxPoolSize(maxPoolSizeAutoTrace);executor.setQueueCapacity(queueCapacityAutoTrace);executor.setThreadNamePrefix(pushStatus);executor.setKeepAliveSeconds(keepAliveSecondsAutoTrace);executor.setThreadGroupName(pushStatus);executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}@Beanpublic Executor addOperationLogList() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(corePoolSizeAutoTrace);executor.setMaxPoolSize(maxPoolSizeAutoTrace);executor.setQueueCapacity(queueCapacityAutoTrace);executor.setThreadNamePrefix("addOperationLog-");executor.setKeepAliveSeconds(keepAliveSecondsAutoTrace);executor.setThreadGroupName("addOperationLog-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}/*** 新增托运单时,添加单据收发方信息* @return*/@Beanpublic Executor addBillLogisticsInfo() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(corePoolSizeAutoTrace);executor.setMaxPoolSize(maxPoolSizeAutoTrace);executor.setQueueCapacity(queueCapacityAutoTrace);executor.setThreadNamePrefix("addBillLogisticsInfo-");executor.setKeepAliveSeconds(keepAliveSecondsAutoTrace);executor.setThreadGroupName("addBillLogisticsInfo-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}/*** 派车单异常记录*/@Bean(name = "recordBoxExecutor")public Executor recordBoxExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(recordCoreSize);executor.setMaxPoolSize(recordMaxPoolSize);executor.setQueueCapacity(queueCapacityAutoTrace);executor.setThreadNamePrefix("addTmsBillRecordBoxDtl-");executor.setKeepAliveSeconds(keepAliveSecondsAutoTrace);executor.setThreadGroupName("addTmsBillRecordBoxDtl-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}/*** 运单回写揽收线程池*/@Bean(name = "orderHandExecutor")public Executor orderHandExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(corePoolSizeAutoTrace);executor.setMaxPoolSize(maxPoolSizeAutoTrace);executor.setQueueCapacity(queueCapacityAutoTrace);executor.setThreadNamePrefix("orderHandExecutor-");executor.setKeepAliveSeconds(keepAliveSecondsAutoTrace);executor.setThreadGroupName("orderHandExecutor-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}/*** 运单回写签收线程池*/@Bean(name = "orderSignExecutor")public Executor orderSignExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(corePoolSizeAutoTrace);executor.setMaxPoolSize(maxPoolSizeAutoTrace);executor.setQueueCapacity(queueCapacityAutoTrace);executor.setThreadNamePrefix("orderSignExecutor-");executor.setKeepAliveSeconds(keepAliveSecondsAutoTrace);executor.setThreadGroupName("orderSignExecutor-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}
}

在代码中使用

PS:线程安全问题及解决方法

参考文档:

网址:CompletableFuture 指南 |贝尔东 (baeldung.com)

思维图解:CompletableFuture的使用| ProcessOn免费在线作图,在线流程图,在线思维导图

优雅处理并发:Java CompletableFuture最佳实践 - 个人文章 - SegmentFault 思否

Java 8 CompletableFuture 对比 ES6 Promise | 叉叉哥的BLOG (xxgblog.com)

SpringBoot 实现异步调用@Async | 以及使用@Async注解可能会导致的问题_springboot @async异步类被aop拦截会报什么错误-CSDN博客

 线上调优:接口响应慢?那是你没用 CompletableFuture 来优化!

一次真实生产事故,让我总结了线程池的正确使用方式 (qq.com)

相关文章:

《异步编程之美》— 全栈修仙《Java 8 CompletableFuture 对比 ES6 Promise 以及Spring @Async》

哈喽&#xff0c;大家好&#xff01;在平常开发过程中会遇到许多意想不到的坑&#xff0c;本篇文章就记录在开发过程中遇到一些常见的问题&#xff0c;看了许多博主的异步编程&#xff0c;我只能说一言难尽。本文详细的讲解了异步编程之美&#xff0c;是不可多得的好文&#xf…...

新模型设计:Hybrid Quantum-Classical Neural Network (HQCNN) for Image Classification

新模型设计:Hybrid Quantum-Classical Neural Network (HQCNN) for Image Classification 目录 新模型设计:Hybrid Quantum-Classical Neural Network (HQCNN) for Image Classification引言1. Hybrid Quantum-Classical Neural Network 简介2. Hybrid Quantum-Classical Neu…...

iOS 中spring动画的使用

我们先来看以下两个动画的效果 上面的位移动画&#xff0c;一个是普通的动画&#xff0c;一个是spring动画&#xff0c;可以明显的看出来&#xff0c;spring动画在动画的前期更快一些&#xff0c;给人的感觉干脆&#xff0c;利落 以下是代码 - (void)normalAnimation {[UIV…...

初学stm32 --- DMA直接存储器

目录 DMA介绍 STM32F1 DMA框图 DMA处理过程 DMA通道 DMA优先级 DMA相关寄存器介绍 F1 DMA通道x配置寄存器&#xff08;DMA_CCRx&#xff09; DMA中断状态寄存器&#xff08;DMA_ISR&#xff09; DMA中断标志清除寄存器&#xff08;DMA_IFCR&#xff09; DMA通道x传输…...

校医院挂号及预约 APP 的设计与实现

标题:校医院挂号及预约 APP 的设计与实现 内容:1.摘要 随着移动互联网的发展&#xff0c;越来越多的人开始使用手机应用程序来解决生活中的各种问题。本项目旨在设计和实现一款校医院挂号及预约 APP&#xff0c;以提高校医院的服务效率和质量&#xff0c;方便师生就医。本文介…...

代理模式详解与应用

代理模式&#xff08;Proxy Pattern&#xff09;&#xff0c;也称为委托模式或 surrogate 模式&#xff0c;是一种结构型设计模式。它为其他对象提供一个代理以控制对这个对象的访问。通过引入代理对象&#xff0c;可以在不改变原始对象接口的前提下&#xff0c;添加额外的功能…...

Model-based RL自动出价算法的演进之路

▐ 导读 近年来&#xff0c;强化学习自动出价算法已成为智能投放领域的标志性技术&#xff0c;然而其所存在的在离线不一致、线上数据覆盖空间受限等关键问题尚未被完全解决。在本文中&#xff0c;我们提出一种Model-based RL&#xff08;MBRL&#xff09;自动出价算法训练新范…...

.NET AI 开发人员库 --AI Dev Gallery简单示例--问答机器人

资源及介绍接上篇 nuget引用以下组件 效果展示&#xff1a; 内存和cpu占有&#xff1a; 代码如下&#xff1a;路径换成自己的模型路径 模型请从上篇文尾下载 internal class Program{private static CancellationTokenSource? cts;private static IChatClient? model;privat…...

框架部分面试题学习

IOC容器&#xff0c;AOP IOC &#xff1a;依赖反转&#xff0c;将对象的创建&#xff0c;组装&#xff0c;管理的控制权限从应用程序反转到IOC容器中。由springboot的来实现对象的自动装配和注入。 当某个类使用了Componnet 注解后&#xff0c;标记为一个组件。那么这个类在项…...

tdengine数据库使用java连接

1 首先给你的项目添加依赖 <dependency> <groupId>com.taosdata.jdbc</groupId> <artifactId>taos-jdbcdriver</artifactId> <version>3.4.0</version> <!-- 表示依赖不会传递 --> </dependency> 注意&am…...

Java 模板变量替换——字符串替换器(思路Mybatis的GenericTokenParser)

Java 模板变量替换——字符串替换器&#xff08;思路Mybatis的GenericTokenParser&#xff09; 思路字符串替换器 思路 模板变量替换无非是寻找出字符串&#xff08;模板&#xff09;中的特殊标记&#xff0c;用对应的变量进行字符串替换。 提到变量替换&#xff0c;大家第一能…...

跨界融合:人工智能与区块链如何重新定义数据安全?

引言&#xff1a;数据安全的挑战与现状 在信息化驱动的数字化时代&#xff0c;数据已成为企业和个人最重要的资产之一。然而&#xff0c;随着网络技术的逐步优化和数据量的爆发式增长&#xff0c;数据安全问题也愈变突出。 数据安全现状&#xff1a;– 数据泄露驱动相关事件驱…...

android 自定义SwitchCompat,Radiobutton,SeekBar样式

纯代码的笔记记录。 自定义SwitchCompat按钮的样式 先自定义中间的圆球switch_thumb_bg.xml <?xml version"1.0" encoding"utf-8"?> <shape xmlns:android"http://schemas.android.com/apk/res/android"android:shape"oval&q…...

计算机网络的定义与发展历程

计算机网络的定义 计算机网络是指通过通信设备和传输介质将分布在不同地点的计算机及其相关设备&#xff08;如打印机、服务器等&#xff09;连接起来&#xff0c;按照一定的通信协议进行数据交换与资源共享的系统。计算机网络的基本功能包括&#xff1a;信息的传输、资源共享…...

对比学习 (Contrastive Learning) 算法详解与PyTorch实现

对比学习 (Contrastive Learning) 算法详解与PyTorch实现 目录 对比学习 (Contrastive Learning) 算法详解与PyTorch实现1. 对比学习 (Contrastive Learning) 算法概述1.1 自监督学习1.2 对比学习的优势2. 对比学习的核心技术2.1 正样本对与负样本对2.2 对比损失函数2.3 数据增…...

DBeaver执行本地的sql语句文件避免直接在客户端运行卡顿

直接在客户端运行 SQL 语句和通过加载本地文件执行 SQL 语句可能会出现不同的性能表现&#xff0c;原因可能包括以下几点&#xff1a; 客户端资源使用&#xff1a; 当你在客户端界面直接输入和执行 SQL 语句时&#xff0c;客户端可能会消耗资源来维护用户界面、语法高亮、自动完…...

C++ 的 pair 和 tuple

1 std::pair 1.1 C 98 的 std::pair 1.1.1 std::pair 的构造 ​ C 的二元组 std::pair<> 在 C 98 标准中就存在了&#xff0c;其定义如下&#xff1a; template<class T1, class T2> struct pair;std::pair<> 是个类模板&#xff0c;它有两个成员&#x…...

Zookeeper 集群安装

Zookeeper 集群 主机 IP SoftWare Port OS Myidnode1 192.168.230.128 apache-zookeeper-3.7.1 2181 Centos 7 1 node2 192.168.230.129 apache-zookeeper-3.7.1...

git merge与rebase区别以及实际应用

在 Git 中&#xff0c;merge 和 rebase 是两种将分支的更改合并到一起的常用方法。虽然它们都可以实现类似的目标&#xff0c;但它们的工作方式和效果有所不同。 1. Git Merge 定义&#xff1a;git merge 是将两个分支的历史合并在一起的一种操作。当你执行 git merge 时&…...

kvm虚拟机出现应用程序无法正常启动报0xc0000142错误

场景&#xff1a;我的是window10虚拟机&#xff0c;在运行我的软件的时候&#xff0c;出现0xc0000142错误&#xff0c;原因可能是cpu型号问题&#xff0c;某些虚拟cpu可能没有特定的指令&#xff0c;只需要修改虚拟机配置文件以下参数即可...

Redis 安装与 Spring Boot 集成指南

安装 Redis 和将其与 Spring Boot 应用集成是构建高效缓存解决方案的常见步骤。以下是详细的指南&#xff0c;帮助你在本地环境中安装 Redis&#xff0c;并在 Spring Boot 项目中配置和使用它。 1. 安装 Redis Windows 环境 Redis 官方并不直接支持 Windows&#xff0c;但你…...

Flink集成TDEngine来批处理或流式读取数据进行流批一体化计算(Flink SQL)拿来即用的案例

Flink 以其流批一体化的编程模型而备受青睐。它支持高吞吐、低延迟的实时流计算,同时在批处理方面也表现出色。Flink 提供了丰富的 API,如 DataStream API 和 DataSet API,方便开发者进行数据处理操作,包括转换、聚合、连接等,使得开发者能够轻松构建复杂的数据处理逻辑。…...

【STM32】利用SysTick定时器定时1s

1.SysTick简单介绍 SysTick定时器是一个24位的倒计数定时器&#xff0c;当计数到0时&#xff0c;将从RELOAD寄存器中自动重装载定时初值&#xff0c;开始新一轮计数。 SysTick定时器用于在每隔一定的时间产生一个中断&#xff0c;即使在系统睡眠模式下也能工作。 关于SysTic…...

Python中的format格式化、填充与对齐、数字格式化方式

文章目录 一、format语法二、format格式化的用法2.1、按照先后顺序替换{}2.2、按照索引进行匹配替换{0}2.3、按关键字索引进行匹配替换2.4、通过列表索引格式化字符串2.5、使用元组2.6、通过字典设置格式化字符串2.7、混合使用 三、字符串填充与对齐3.1、左对齐及填充3.2、右对…...

winform第三方界面开源库AntdUI的使用教程保姆级环境设置篇

1. AntdUI 1.1. 导入项目 1.1.1. 首先新建一个空白的基于.net的Winfrom项目1.1.2. 复制AntdUI中src目录到我们的解决方案下面1.1.3. 解决方案下添加现有项目1.1.4. 添加项目引用 1.2. 编写代码 1.2.1. 改写Form1类&#xff0c;让其继承自public partial class Form1 : AntdUI.W…...

如何使用Yarn Workspaces实现Monorepo模式在一个仓库中管理多个项目

Yarn Workspaces是Yarn提供的一种依赖管理机制&#xff0c;它支持在单个代码仓库中管理多个包的依赖。这种机制非常适合需要多个相互依赖的包的项目&#xff0c;能够减少重复依赖&#xff0c;加快依赖安装速度&#xff0c;并简化依赖管理。下面将详细介绍如何使用Yarn Workspac…...

SpringCloud系列教程:微服务的未来(十一)服务注册、服务发现、OpenFeign快速入门

本篇博客将通过实例演示如何在 Spring Cloud 中使用 Nacos 实现服务注册与发现&#xff0c;并使用 OpenFeign 进行服务间调用。你将学到如何搭建一个完整的微服务通信框架&#xff0c;帮助你快速开发可扩展、高效的分布式系统。 目录 前言 服务注册和发现 服务注册 ​编辑 …...

物联网:七天构建一个闭环的物联网DEMO

我计划用七天的时间&#xff0c; 基于开源物联网平台&#xff0c; 打造一款物联网案例的闭环。 为了增加感观体验&#xff0c;欢迎大家与我保持亲密的沟通。 我们来看一段代码&#xff1a; Slf4j Component public class MqttSendManager {Resourceprivate MqttSendHandler m…...

景联文科技提供高质量多模态数据处理服务,驱动AI新时代

在当今快速发展的AI时代&#xff0c;多模态数据标注成为推动人工智能技术进步的关键环节。景联文科技作为行业领先的AI数据服务提供商&#xff0c;专注于为客户提供高质量、高精度的多模态数据标注服务&#xff0c;涵盖图像、语音、文本、视频及3D点云等多种类型的数据。通过专…...

c#13新特性

C# 13 即 .NET 9 按照计划会在2024年11月发布&#xff0c;目前一些新特性已经定型&#xff0c;让我们来预览一个比较大型比较重要的新特性。 正文 扩展类型 Extension types 在5月份的微软 Build 大会中的 What’s new in C# 13 会议上&#xff0c;两位大佬花了很长的篇幅来…...