从 Future 到 CompletableFuture:简化 Java 中的异步编程
引言
在并发编程中,我们经常需要处理多线程的任务,这些任务往往具有依赖性,异步性,且需要在所有任务完成后获取结果。Java 8 引入了 CompletableFuture 类,它带来了一种新的编程模式,让我们能够以函数式编程的方式处理并发任务,显著提升了代码的可读性和简洁性。
在这篇博客中,我们将深入探讨 CompletableFuture 的设计原理,详细介绍其 API 的使用方式,并通过具体的示例来展示其在并发任务处理中的应用。我们也将探讨其与 Future,CompletableFuture 以及 Java 并发包中其他工具的对比,理解何时以及为什么需要使用 CompletableFuture。让我们一起踏上这个富有挑战性的学习之旅吧!
❝
在开始之前,我们先来回顾一下Java语言发展历史
❞
Java 并发编程的演进
自从诞生以来,Java 就一直致力于提供强大的并发和异步编程工具。在最初的 JDK 1.4 时期,Java 开发者需要使用低级的并发控制工具,如 synchronized 和 wait/notify,这些工具虽然功能强大,但使用起来非常复杂。
为了简化并发编程,Java 在 JDK 1.5 中引入了JUC包,提供了一系列高级的并发控制工具,如 ExecutorService、Semaphore 和 Future。
❝
我们先来看下,Future到底是怎么进行异步编程的
❞
Future的异步编程之旅
在开始我们的旅程之前,我们先看看一下这个需求。
一个复杂的需求
假设你正在为一家在线旅行社工作,用户可以在网站上搜索并预订飞机票和酒店。以下是你需要处理的一系列操作:
- 根据用户的搜索条件,查询所有可用的飞机票
- 对每一个飞机票,查询与之匹配的可用酒店
- 对每一个飞机票和酒店的组合,计算总价格
- 将所有的飞机票和酒店的组合按照价格排序
- 将结果返回给用户
实现
为了实现这个需求,首先,我们需要创建一个 ExecutorService,:
ExecutorService executor = Executors.newFixedThreadPool(10);
// 1. 查询飞机票
Future<List<Flight>> futureFlights = executor.submit(() -> searchFlights(searchCondition));List<Flight> flights;
try {flights = futureFlights.get();
} catch (InterruptedException | ExecutionException e) {// 处理异常
}// 2. 对每个飞机票查询酒店
List<Future<List<Hotel>>> futureHotelsList = new ArrayList<>();
for (Flight flight : flights) {Future<List<Hotel>> futureHotels = executor.submit(() -> searchHotels(flight));futureHotelsList.add(futureHotels);
}List<Future<List<TravelPackage>>> futureTravelPackagesList = new ArrayList<>();
for (Future<List<Hotel>> futureHotels : futureHotelsList) {List<Hotel> hotels;try {hotels = futureHotels.get();} catch (InterruptedException | ExecutionException e) {// 处理异常}// 3. 对每个飞机票和酒店的组合计算总价格for (Hotel hotel : hotels) {Future<List<TravelPackage>> futureTravelPackages = executor.submit(() -> calculatePrices(flight, hotel));futureTravelPackagesList.add(futureTravelPackages);}
}List<TravelPackage> travelPackages = new ArrayList<>();
for (Future<List<TravelPackage>> futureTravelPackages : futureTravelPackagesList) {try {travelPackages.addAll(futureTravelPackages.get());} catch (InterruptedException | ExecutionException e) {// 处理异常}
}// 4. 将所有的旅行套餐按照价格排序
travelPackages.sort(Comparator.comparing(TravelPackage::getPrice));// 5. 返回结果
return travelPackages;
需求终于做完了(叹气声)。此时此刻,生在JDK8+的你,会不会感同身受呢。这还是在没有处理异常,没有很多业务代码的前提下。好,现在缓一下我们继续。我们可以从上面代码最直观的看到什么?
❝
再完美的表达,也敌不过一个让你直观感受的例子。接下来,我们来分析一下Future的缺点。
❞
分析这趟Future异步编程之旅
从上面的 Future 的例子中,我们可以明显看到以下几点缺点:
回调地狱
Future 的实现使得我们必须在每一个 Future 完成后启动另一个 Future,这使得代码看起来像是在不断嵌套回调。这种方式会使得代码难以阅读和理解,特别是在涉及复杂的异步任务链时。
阻塞操作
虽然 Future.get() 可以得到任务的结果,但这是一个阻塞操作,它会阻止当前线程的执行,直到异步操作完成。这种设计对于要实现非阻塞的异步编程来说,是非常不理想的。
复杂的错误处理
在使用 Future 链式处理异步任务时,如果中间某个环节出现错误,错误处理的复杂性就会大大增加。你需要在每个 Future 的处理过程中都增加异常处理代码,这使得代码变得更加复杂和难以维护。
无法表示任务间复杂关系
使用 Future 很难直观地表示出任务之间的依赖关系。例如,你无法使用 Future 来表示某个任务需要在另外两个任务都完成后才能开始,或者表示多个任务可以并行执行但是必须在一个共同的任务之前完成。这种限制使得 Future 在处理复杂的异步任务链时变得非常困难。
因此,为了解决这些问题,CompletableFuture 被引入了 Java 8,提供了更强大和灵活的异步编程工具。
CompletableFuture的异步编程之旅
同样还是上面的例子,我们来看下它的实现代码:
CompletableFuture.supplyAsync(() -> searchFlights()) // 1. 查询飞机票.thenCompose(flights -> { // 2. 对每个飞机票查询酒店List<CompletableFuture<List<TravelPackage>>> travelPackageFutures = flights.stream().map(flight -> CompletableFuture.supplyAsync(() -> searchHotels(flight)) // 查询酒店.thenCompose(hotels -> { // 3. 对每个飞机票和酒店的组合计算总价格List<CompletableFuture<TravelPackage>> packageFutures = hotels.stream().map(hotel -> CompletableFuture.supplyAsync(() -> new TravelPackage(flight, hotel))).collect(Collectors.toList());return CompletableFuture.allOf(packageFutures.toArray(new CompletableFuture[0])).thenApply(v -> packageFutures.stream().map(CompletableFuture::join).collect(Collectors.toList()));})).collect(Collectors.toList());return CompletableFuture.allOf(travelPackageFutures.toArray(new CompletableFuture[0])).thenApply(v -> travelPackageFutures.stream().flatMap(future -> future.join().stream()).collect(Collectors.toList()));}).thenApply(travelPackages -> { // 4. 将所有的旅行套餐按照价格排序return travelPackages.stream().sorted(Comparator.comparing(TravelPackage::getPrice)).collect(Collectors.toList());}).exceptionally(e -> { // 处理所有的异常// 处理异常return null;});
你可能乍一看,感觉怎么比Future还要复杂。但是实际在业务中,它反而更加容易读懂。每一步,每一个操作都可以顺着thenCompose下去。
分析这趟CompletableFuture异步编程之旅
CompletableFuture 是 Java 8 中引入的,用于解决在使用 Future 时遇到的一些问题。它实现了 Future 和 CompletionStage 接口,并且提供了大量的方法来帮助你更好地控制和管理异步操作。我们来结合上面的例子来分析它的优点:
链式编程
我们使用 CompletableFuture 中的 supplyAsync 方法来异步地开始查询航班的操作:
CompletableFuture<List<Flight>> flightsFuture = CompletableFuture.supplyAsync(() -> searchFlights(source, destination));
然后,我们使用 thenCompose 方法将查询航班和查询酒店的操作连在一起:
CompletableFuture<List<TravelPackage>> travelPackagesFuture = flightsFuture.thenCompose(flights ->CompletableFuture.supplyAsync(() -> flights.stream().map(flight -> searchHotels(flight)).collect(Collectors.toList())));
非阻塞操作
上述的 thenCompose 方法是非阻塞的,即查询酒店的操作会立即开始,而不需要等待查询航班的操作完成。
异常处理
我们使用 exceptionally 方法处理查询航班和查询酒店过程中可能出现的异常:
CompletableFuture<List<TravelPackage>> travelPackagesFuture = flightsFuture.thenCompose(flights ->CompletableFuture.supplyAsync(() -> flights.stream().map(flight -> searchHotels(flight)).collect(Collectors.toList()))).exceptionally(ex -> {System.out.println("失败了: " + ex);return new ArrayList<>();});
表示任务间复杂关系
我们使用 CompletableFuture.allOf 方法来表示所有的旅行套餐计算任务都必须在开始排序之前完成:
CompletableFuture<List<TravelPackage>> sortedTravelPackagesFuture = travelPackagesFuture.thenApply(travelPackages ->travelPackages.stream().flatMap(List::stream).sorted(Comparator.comparing(TravelPackage::getPrice)).collect(Collectors.toList()));
❝
暂停一分钟,再细细体会上面的例子。我们接着来集中比较这两者
❞
CompletableFuture与Future的比较
异步执行与结果获取
- Future 提供了一种在未来某个时间点获取结果的方式,但它的主要问题是在获取结果时,如果结果尚未准备好,会导致阻塞。另外,使用 isDone() 方法进行轮询也不是一个好的选择,因为它将消耗CPU资源。
- CompletableFuture 提供了非阻塞的结果获取方法,thenApply, thenAccept, thenRun 等方法可以在结果准备好后被自动执行,这样我们不需要手动检查和等待结果。
链式操作
- Future 不支持链式操作,我们无法在 Future 完成后自动触发另一个任务。
- CompletableFuture 提供了 thenApply, thenAccept, thenRun, thenCompose, thenCombine 等一系列方法,用于在当前任务完成后自动执行另一个任务,形成任务链。
异常处理
- 在 Future 中,只能通过 get() 方法获取异常,但是这种方式会阻塞线程,直到任务执行完毕。
- CompletableFuture 提供了 exceptionally, handle 等方法,我们可以用这些方法在发生异常时提供备用的结果,或者对异常进行处理。
任务组合
- Future 并未提供任何任务组合的方式。
- CompletableFuture 提供了 allOf, anyOf, thenCombine 等方法,我们可以通过这些方法来表示任务间的并行关系,或者汇聚关系。
灵活的任务执行控制
- Future 在任务执行上相对较为死板,我们无法中途取消任务,也无法在任务结束后执行特定操作。
- CompletableFuture 提供了 cancel, complete 等方法,用于中途取消任务,或者提前完成任务。此外,whenComplete 和 whenCompleteAsync 方法允许我们在任务结束时,无论成功或失败,都可以执行特定的操作。
❝
假如有一个面试官现在问题它们两者的区别,你会回答了吗?接下来,我们来解析一下
❞
进阶 | 理解CompletableFuture原理
为了让你理解的不那么晦涩,我为你讲生活中的例子:
我们可以把 CompletableFuture 想象成一家装配线生产车间。每一件零件(任务)的加工完成(Future 完成)都可能会触发下一步工作(下一步的操作),而每一步工作的完成都会通知车间(Future),以便开始下一个阶段的生产。这个过程就像一条流水线,每完成一个步骤就自动进行下一个。
带着这个场景,我们接着往下看。
任务链
CompletableFuture 的源码中,有一个内部类 Completion,代表了任务链中的一项任务。每当一个任务完成时,它都会尝试去完成依赖于它的任务,就像流水线上的工人完成了一部分工作后,就会把半成品传递给下一个工人。
abstract static class Completion extends ForkJoinTask<Void> implements Runnable, AsynchronousCompletionTask {// ...
}
结果容器
CompletableFuture 本身就是一个结果容器,它持有了执行的结果,包括正常的计算结果或者执行过程中出现的异常。
volatile Object result; // The outcome of the computation
工作线程
所有的异步任务都会提交到 ForkJoinPool.commonPool() 中进行执行,当然也可以指定自定义的 Executor 来执行任务。
static final ForkJoinPool ASYNC_POOL = ForkJoinPool.commonPool();
任务触发
当一个任务完成后,CompletableFuture 会通过 tryFire 方法触发与之关联的下一个任务。这就好比工人完成了一部分工作后,通知流水线的下一位工人继续完成接下来的工作。
final CompletableFuture<T> postFire(CompletableFuture<?> a, int mode) {// ...if (a != null && a.stack != null) {if (mode < 0)a.cleanStack();elsea.postComplete();}if (b != null && b.stack != null) {if (mode < 0)b.cleanStack();elseb.postComplete();}return null;}
是不是有点理解了呢?我可以肯定的说,你已经超过80%的人了!
CompletableFuture的主要方法
细心的你肯定发现了,CompletableFuture大多数方法都实现于一个CompletionStage接口。当然,我在这里可以为你把所有方法都试过一遍,但是你肯定会看的特别累。这样!我把上面需求中所用到的方法都为你讲解,剩下的请你结合网上的案例学习。
supplyAsync()方法
这个方法用于异步执行一个供应函数,并返回一个CompletableFuture对象。在我们的示例中,这个方法用于启动一个异步任务来查找航班。
CompletableFuture<List<Flight>> flightsFuture = CompletableFuture.supplyAsync(() -> searchFlights(destination));
thenCompose()方法
这个方法用于链接多个CompletableFuture对象,形成一个操作链。当一个操作完成后,thenCompose()方法会将操作的结果传递给下一个操作。在我们的示例中,这个方法用于在找到航班之后查找酒店。
CompletableFuture<List<Hotel>> hotelsFuture = flightsFuture.thenCompose(flights -> CompletableFuture.supplyAsync(() -> searchHotels(destination)));
thenCombine()方法
这个方法用于将两个独立的CompletableFuture对象的结果合并为一个结果。在我们的示例中,这个方法用于将查找航班和酒店的结果合并为一个旅行套餐。
CompletableFuture<List<TravelPackage>> travelPackagesFuture = flightsFuture.thenCombine(hotelsFuture, (flights, hotels) -> createTravelPackages(flights, hotels));
thenAccept()方法
这个方法在CompletableFuture对象完成计算后执行一个消费函数,接收计算结果作为参数,不返回新的计算值。在我们的示例中,这个方法用于打印出所有的旅行套餐。
travelPackagesFuture.thenAccept(travelPackages -> printTravelPackages(travelPackages));
allOf()方法
这个方法用于将一个CompletableFuture对象的数组组合成一个新的CompletableFuture对象,这个新的CompletableFuture对象在数组中所有的CompletableFuture对象都完成时完成。在我们的示例中,这个方法用于将每个航班与每个酒店的组合结果(也就是旅行套餐)组合在一起。
CompletableFuture.allOf(packageFutures.toArray(new CompletableFuture[0])).thenApply(v -> packageFutures.stream().map(CompletableFuture::join).collect(Collectors.toList()));
thenApply()方法
这个方法用于对CompletableFuture的结果进行变换,并返回一个新的CompletableFuture对象。在我们的示例中,这个方法用于将查询到的旅行套餐按照价格进行排序。
.thenApply(travelPackages -> { // 4. 将所有的旅行套餐按照价格排序return travelPackages.stream().sorted(Comparator.comparing(TravelPackage::getPrice)).collect(Collectors.toList());})
exceptionally()方法
这个方法用于处理CompletableFuture的异常情况。如果CompletableFuture的计算过程中抛出异常,那么这个方法会被调用。在我们的示例中,这个方法用于处理查询旅行套餐过程中可能出现的任何异常。
.exceptionally(e -> { // 处理所有的异常// 处理异常return null;});
❝
当然,这些方法已经够你用了。除非这个需求比我想得还复杂,那算你厉害。哦,不对,算需求变态。现在,你可以挥起历史的毛笔续写了吗?
❞
Java 并发编程的续章
JDK 1.5 的 Future 解决了许多并发编程的复杂性,但是它仍有一些局限性。Future 只能描述一个异步操作,并不能描述一个由多个步骤组成的异步操作。例如,当需要处理一个由多个异步操作序列组成的业务流程时,你可能会发现你的代码被复杂的回调逻辑淹没,这就是人们常说的回调地狱。此外,Future 没有提供一种有效的方式来处理异步操作的结果,你只能通过阻塞调用 get() 方法来获取结果。
为了解决这些问题,Java 在 JDK 1.8 中引入了 CompletableFuture。CompletableFuture 是 Future 的增强版,它不仅能表示一个异步操作,还可以通过 thenCompose(), thenCombine(), allOf() 等方法来描述一个由多个步骤组成的异步操作。通过这些方法,CompletableFuture 能以流畅的链式调用的方式来描述复杂的异步业务流程,这大大简化了异步编程的复杂性。
常见面试题
- 请解释一下 Future 接口在 Java 中的用途?
- 解释一下 Future 的局限性是什么?
- 请解释一下 CompletableFuture 的用途以及它如何克服 Future 的局限性?
- 如何用 CompletableFuture 来表示一组并行的异步操作?
- 请解释一下 CompletableFuture 的 thenApply(),thenCompose(),和 thenCombine() 方法的作用及区别?
- 如果你有一个耗时的异步操作需要执行,但是你又不希望调用 get() 方法时阻塞,你可以使用 CompletableFuture 的哪个方法来达到这个目的?
- 如何处理 CompletableFuture 的异常?
- 请解释一下 CompletableFuture 的工作原理?
阅读完文章的你,是否可以回答这些问题呢?我在留言等你。
总结
好了,到这里就结束了,我们来回顾一下。首先,我带你回顾了一下Java并发世界的编年史。紧接着,我带你体验了一下古人经常使用的Future。感到它的不妙之后,我带你回到CompletableFuture 。紧接着有深入了解了它的全貌以及使用方法。
相关文章:

从 Future 到 CompletableFuture:简化 Java 中的异步编程
引言 在并发编程中,我们经常需要处理多线程的任务,这些任务往往具有依赖性,异步性,且需要在所有任务完成后获取结果。Java 8 引入了 CompletableFuture 类,它带来了一种新的编程模式,让我们能够以函数式编…...

【ARMv8 SIMD和浮点指令编程】NEON 乘法指令——乘法知多少?
NEON 乘法指令包括向量乘法、向量乘加和向量乘减,还有和饱和相关的指令。总之,乘法指令是必修课,在我们的实际开发中会经常遇到。 1 MUL (by element) 乘(向量,按元素)。该指令将第一个源 SIMD&FP 寄存器中的向量元素乘以第二个源 SIMD&FP 寄存器中的指定值,将…...

Nginx详解 第三部分:Nginx高级配置(附配置实例)
Part 3 一、网页的状态页二、Nginx第三方模块2.1 echo 模块 三、变量3.1 内置变量3.1.1 常用内置变量3.1.2 举个例子 3.2 自定义变量 四、自定义访问日志 (优化)4.1 自定义访问日志的格式4.2 自定义json 格式日志 五、Nginx压缩功能(重要)六、HTTPS 功能…...

postman访问ruoyi后台接口
打开若依页面,登录进去,F12打开控制台,选一个后台服务,把下图两个节点,补到postman请求header里面即可...

大数据时代的软件开发实践:利用云计算和AI赋能创新
文章目录 云计算的赋能弹性资源管理远程协作与分布式开发持续集成和持续交付成本效益 人工智能的赋能数据驱动的决策自动化智能预测和优化自适应系统 创新的实践方法数据驱动的创新智能化产品开放式创新迭代和反馈 🎈个人主页:程序员 小侯 🎐…...

32、启用 HTTP 响应压缩和编程式配置Web应用
★ 启用HTTP压缩 就是前端页面如果改动的比较多,那么响应就会比较慢,可以通过设置HTTP响应压缩来提高响应,如果前端改动少,那么就不需要启动这个响应压缩。 目的:为了提高HTTP响应数据在网络上的传输效率。▲ 设置如…...

DiskCatalogMaker for Mac简单智能快速的磁盘管理工具
DiskCatalogMaker是一款Mac上的磁盘目录管理工具。它可以帮助用户快速创建和管理磁盘目录,方便查找和访问存储在磁盘上的文件和文件夹。它具有快速扫描和索引功能,生成详细的目录列表,支持关键字搜索和自定义标签。 此外,DiskCat…...

C语言练习5(巩固提升)
C语言练习5 选择题 选择题 1,下面代码的结果是:( ) #include <stdio.h> #include <string.h> int main() {char arr[] { b, i, t };printf("%d\n", strlen(arr));return 0; }A.3 B.4 C.随机值 D.5 💯答案解析&#…...

SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录(第三天)动态SQL
动态SQL—SSM框架的学习与应用(Spring Spring MVC MyBatis)-Java EE企业级应用开发学习记录(第三天)Mybatis的动态SQL操作 昨天我们深入学习了Mybatis的核心对象SqlSessionFactoryBuilder,掌握MyBatis核心配置文件以及元素的使用,也掌握My…...

Kaggle(3):Predict CO2 Emissions in Rwanda
Kaggle(3):Predict CO2 Emissions in Rwanda 1. Introduction 在本次竞赛中,我们的任务是预测非洲 497 个不同地点 2022 年的二氧化碳排放量。 在训练数据中,我们有 2019-2021 年的二氧化碳排放量 本笔记本的内容&am…...

【技巧分享】如何获取子窗体选择了多少记录数?一招搞定!
Hi,大家好久不见。 我这个更新速度是不是太慢了呀,因为,最近又又又在忙,请大家谅解啦。 现在更新文章、视频都要花好久去考虑,好不容易有个灵感了,一搜索,结果发现之前都已经分享过了(委屈脸&…...

Kotlin AQ
如何学习kotlin? 学习Kotlin的步骤如下: 1. 理解Kotlin的基础:首先,你需要理解Kotlin的基础知识,包括变量、数据类型、运算符、控制流等。你可以通过阅读Kotlin的官方文档或者其他在线教程来学习。 2. 实践编程:理论…...

SpringBoot入门篇2 - 配置文件格式、多环境开发、配置文件分类
目录 1.配置文件格式(3种) 例:修改服务器端口。(3种) src/main/resources/application.properties server.port80 src/main/resources/application.yml(主要用这种) server:port: 80 src/m…...

UOS安装6.1.11内核或4.19内核
6.1.11内核 sudo sh -c echo "deb https://proposed-packages.deepin.com/beige-testing unstable main dde community commercial " > /etc/apt/sources.list.d/deepin-testing.list sudo apt update && sudo apt install linux-image-6.1.11-amd64-de…...

HiveSQL刷题
41、同时在线人数问题 现有各直播间的用户访问记录表(live_events)如下,表中每行数据表达的信息为,一个用户何时进入了一个直播间,又在何时离开了该直播间。 user_id (用户id)live_id (直播间id)in_datetime (进入直…...

path路径模块
path模块是Node.js官方提供的、用来处理路径的模块。它提供了一系列的方法和属性,用来满足用户对路径的处理需求。 path.join( )用来将多个路径片段拼接成一个完整的路径字符串 ../会抵消前面的路径 const path require(path) const pathStr path.join(/a,/b,../,/d) conso…...

1.文章复现《热电联产系统在区域综合能源系统中的定容选址研究》(附matlab程序)
0.代码链接 文章复现《热电联产系统在区域综合能源系统中的定容选址研究》(matlab程序)-Matlab文档类资源-CSDN文库 1.简述 本文采用遗传算法的方式进行了下述文章的复现并采用电-热节点的方式进行了潮流计算以降低电网的网络损耗 分析了电网的基本数…...

【Terraform学习】使用 Terraform 托管 S3 静态网站(Terraform-AWS最佳实战学习)
使用 Terraform 托管 S3 静态网站 实验步骤 前提条件 安装 Terraform: 地址 下载仓库代码模版 本实验代码位于 task_s3 文件夹中。 变量文件 variables.tf 在上面的代码中,您将声明,aws_access_key,aws_secret_key和区域变量…...

触发JVM fatal error并配置相关JVM参数
1. 絮絮叨叨 工作中,Java服务因为fatal error(致命错误,笔者称其为jvm crash),在服务运行日志中出现了致命错误的概要信息: # # A fatal error has been detected by the Java Runtime Environment: # # S…...

爬虫(bilibili热门课程记录)
什么是爬虫?程序蜘蛛,沿着互联网获取相关信息,收集目标信息。 一、python环境安装 1、先从Download Python | Python.org中下载最新版本的python解释器 2、再从Download PyCharm: Python IDE for Professional Developers by JetBrains中下…...

14-模型 - 增删改查
增: # 1. 找到模型类并创建对象 user User() # 2. 给对象的属性赋值 user.username username user.password password user.phone phone # 3. 将user对象添加到session中 (类似缓存) db.session.add(user) # 4. 提交数据 db.session.commit() 删: # 两种删除:# 1. 逻辑删…...

C#与西门子PLC1500的ModbusTcp服务器通信3--搭建ModbusTcp服务器
1、打开仿真工具,创建PLC,注意创建完成后不要关闭 注意,这个IP地址必须与西门子虚拟网卡的IP地址及虚拟机的网卡IP地址同一网段 2、打开博途V15,创建项目,命名为Lan项目 3、添加1500系列CPU1513 4、设置设置IP地址及属…...

Linux系统编程:线程控制
目录 一. 线程的创建 1.1 pthread_create函数 1.2 线程id的本质 二. 多线程中的异常和程序替换 2.1 多线程程序异常 2.2 多线程中的程序替换 三. 线程等待 四. 线程的终止和分离 4.1 线程函数return 4.2 线程取消 pthread_cancel 4.3 线程退出 pthread_exit 4.4 线程…...

基于Java+SpringBoot+Vue前后端分离纺织品企业财务管理系统设计和实现
博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专…...

搭建开发环境-Windows
写C# 的请出去。 然后,Windows 是最好的Linux发行版。搭建开发环境-WSLUbuntu...

【 Python 全栈开发 - 人工智能篇 - 45 】集成算法与聚类算法
文章目录 一、集成算法1.1 概念1.2 常用集成算法1.2.1 Bagging1.2.2 Boosting1.2.2.1 AdaBoost1.2.2.2 GBDT1.2.2.3 XgBoost 1.2.3 Stacking 二、聚类算法2.1 概念2.2 常用聚类算法2.2.1 K-means2.2.2 层次聚类2.2.3 DBSCAN算法2.2.4 AP聚类算法2.2.5 高斯混合模型聚类算法 一、…...

SSM商城项目实战:账户充值功能实现
SSM商城项目实战:账户充值功能实现 在一个电商平台中,用户账户充值是一个非常重要的功能。本文将介绍如何在SSM(SpringSpringMVCMyBatis)商城项目中实现账户充值功能。通过本文的指导,你将学会如何在项目中添加账户充…...

wireshark工具pcap文件转换
pcap详解_pcap_loop_小虎随笔的博客-CSDN博客 分析802.11无线报文hexdump内容:利用wireshark自带二进制工具text2pcap将hexdump内容转换为pcap文件..._weixin_30835933的博客-CSDN博客 text2pcap: 将hex转储文本转换为Wireshark可打开的pcap文件(wireshark,数据) …...

Python+TinyPNG熊猫网站自动化的压缩图片
前言 本篇在讲什么 PythonTinyPNG自动化处理图片 本篇需要什么 对Python语法有简单认知 依赖Python2.7环境 依赖TinyPNG工具 本篇的特色 具有全流程的图文教学 重实践,轻理论,快速上手 提供全流程的源码内容 ★提高阅读体验★ 👉…...

【Linux】socket 编程基础
文章目录 📕 网络间的通信📕 socket 是什么1. socket 套接字2. 套接字描述符3. 基本的 socket 接口函数3.1 头文件3.2 socket() 函数3.3 bind() 函数struct sockaddr主机序列与网络序列 3.4 listen() 函数3.5 connect() 函数3.6 accept() 函数IP 地址风格…...