JUC下的CompletableFuture详解

详细介绍

CompletableFuture是Java 8引入的一个实现Future接口的类,它代表一个异步计算的结果。与传统的Future相比,CompletableFuture提供了更丰富的功能,比如链式调用、组合异步操作、转换结果、异常处理等,极大地增强了Java在处理异步编程的能力。

核心API与方法
  1. 创建实例

    • CompletableFuture<Void/Type> supplyAsync(Supplier<U> supplier):在ForkJoinPool.commonPool()或自定义Executor中异步执行supplier,并返回一个新的CompletableFuture,其结果类型由Supplier决定。
    • CompletableFuture<Void> runAsync(Runnable runnable):同上,但Runnable没有返回值,因此CompletableFuture的类型为Void。
    • 还有非Async版本,如supplyAsync(Supplier<U> supplier, Executor executor),允许指定执行器。
  2. 链式调用与转换结果

    • .thenApply(Function<? super T,? extends U> fn):当前阶段正常完成时,使用给定的函数fn转换结果,并返回一个新的CompletableFuture。
    • .thenAccept(Consumer<? super T> action):当前阶段正常完成时,执行给定的action消费结果,无返回值。
    • .thenCompose(Function<? super T,? extends CompletionStage<U>> fn):当前阶段完成时,使用给定的函数fn将结果转换为另一个CompletionStage,并将其结果作为新的CompletableFuture。
  3. 组合异步操作

    • .thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn):当两个CompletableFuture都完成时,使用BiFunction组合它们的结果。
    • .thenComposeBoth(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends CompletionStage<V>> fn):类似于.thenCombine(),但fn返回的是另一个CompletionStage,而非直接结果。
  4. 异常处理

    • .exceptionally(Function<Throwable,? extends T> fn):如果出现异常,使用提供的函数fn处理异常,并返回一个包含处理结果的新CompletableFuture。
    • .handle(BiFunction<? super T, Throwable, ? extends U> fn):无论正常完成还是异常结束,都会调用fn处理结果或异常,返回一个新的CompletableFuture。
  5. 完成与等待

    • .join():等待计算完成,如有异常直接抛出,适用于不需要区分成功失败的场景。
    • .get():同join,但此方法会抛出具体的ExecutionException或InterruptedException,便于区分错误类型。
  6. 触发动作

    • .whenComplete(BiConsumer<? super T,? super Throwable> action):无论CompletableFuture成功完成还是异常结束,都会执行action,但不改变结果或处理异常。
    • .whenCompleteAsync(BiConsumer<? super T,? super Throwable> action):异步版本的whenComplete。
实现原理浅析

   CompletableFuture基于Java的Fork/Join框架实现,利用工作窃取算法提高线程利用率。每个CompletableFuture对象内部维护了一个状态机,用于跟踪任务的完成状态(初始化、完成、取消、异常)。当一个阶段的任务完成时,它会触发后续阶段的任务开始执行,这一过程通过内部的等待队列和信号机制高效完成,实现了非阻塞的链式调用。

性能与最佳实践
  • 线程池选择:合理选择线程池,避免过度使用ForkJoinPool.commonPool(),尤其是在IO密集型任务中,应考虑使用自定义的固定大小线程池。
  • 避免过度链式:虽然链式调用使代码简洁,但过度使用可能导致逻辑难以理解和维护,适当使用中间变量命名可以帮助理解。
  • 异常传播与日志:确保异常处理逻辑完整,利用日志记录异常信息,便于调试。
  • 资源释放:在涉及资源操作的异步任务中,确保资源能够正确关闭或释放,避免资源泄露。

核心特性

1. 非阻塞式执行

CompletableFuture 允许任务异步执行,而不需要调用线程等待其完成。这意味着主线程可以继续执行其他操作,而不是被阻塞等待结果,提高了程序的响应性和并发性能。

2. 异步回调

它支持链式调用回调函数(如 thenApply, thenAccept, thenCompose 等),允许在异步操作完成时自动执行后续操作,无论是成功还是失败。这种机制使得复杂的异步流程控制变得简单直观。

3. 组合异步任务

CompletableFuture 提供了多种方法来组合多个异步操作,比如 thenCombine, thenCompose,可以将多个独立的异步任务按照依赖关系组织起来,实现任务间的流水线处理,提高程序的并发执行效率。

4. 异步转换结果

通过 thenApply 方法,可以在异步操作完成后,对结果进行转换或进一步处理,而无需等待整个过程完成,这对于构建复杂的异步工作流特别有用。

5. 错误处理

提供了错误处理机制,如 exceptionallyhandle 方法,可以优雅地捕获并处理异步执行过程中发生的异常,增强了程序的健壮性。

6. 自动线程管理

默认情况下,CompletableFuture 会使用 ForkJoinPool.commonPool() 来执行异步任务,但也可以通过 supplyAsync(Supplier<U> supplier, Executor executor) 方法指定自定义的 Executor,从而实现对线程资源的精细控制。

7. 可取消性

如同 FutureCompletableFuture 也支持任务的取消操作,通过 cancel(boolean mayInterruptIfRunning) 方法可以尝试取消一个正在执行或尚未开始的任务。

8. 延迟计算与懒初始化

通过 CompletableFuture.supplyAsync(Supplier<U> supplier) 方法,可以在需要结果时才真正触发计算,这对于资源敏感或计算密集型操作特别有用。

9. 高度可组合性

由于其丰富的API,CompletableFuture 可以轻松地与其他 CompletableFuture 实例组合,形成复杂的异步逻辑,而代码依然保持清晰和可维护。

10. 函数式编程风格

它鼓励使用函数式编程的思维,通过传递函数(Lambda表达式)来定义任务的执行逻辑和结果处理逻辑,使得代码更加简洁、易读且易于推理。

这些特性使 CompletableFuture 成为了Java中处理异步编程的强大工具,尤其是在构建高性能、响应式系统时,能够有效地提升代码的效率和可维护性。

使用场景

微服务架构中的异步通信

在微服务架构中,服务间通常通过HTTP、gRPC或其他协议进行通信。当一个服务需要调用多个下游服务获取数据时,如果采用同步方式,每个调用都会阻塞当前线程直到响应,这会大大增加请求的总耗时。使用CompletableFuture,可以并行发起这些调用,然后通过组合操作处理所有响应,显著减少整体响应时间,提升用户体验。

示例场景:电商平台的订单服务需要在创建订单时,异步调用库存服务检查商品库存、用户服务验证用户信息、支付服务预扣款等多个下游服务,使用CompletableFuture可以高效地并行处理这些任务。

数据库批量操作

对于大量数据的读写操作,如批量插入、更新或查询,直接在主线程中执行可能会导致长时间阻塞。通过CompletableFuture,可以将这些操作分批异步执行,每批操作完成后合并结果,既充分利用了系统资源,又保证了操作的高效性。

示例场景:系统日志收集模块,需要定时从多个来源拉取日志并存储到数据库中。利用CompletableFuture异步处理每个数据源的日志收集和存储任务,最后合并所有操作结果。

文件处理与上传下载

处理大文件的上传、下载或转换操作时,这些操作往往比较耗时,直接在主线程中执行会影响用户体验。使用CompletableFuture可以将这些操作放在后台执行,同时可以提供进度监控、结果通知等功能。

示例场景:云存储服务中,用户上传大文件后,服务端需要异步进行病毒扫描、格式转换等操作,完成后通知用户或进行下一步处理。

定时任务与周期性任务

在实现定时任务或周期性任务时,如定时数据分析、报表生成等,可以利用CompletableFuture配合ScheduledExecutorService进行异步调度,避免阻塞主线程,并且可以方便地控制任务的执行策略(如首次执行延迟、执行间隔等)。

示例场景:每天凌晨自动统计前一天的销售数据并生成报表,通过CompletableFuture安排在低峰时段异步执行,不影响白天的业务操作。

并发限制与资源池管理

在处理大量并发请求时,直接为每个请求分配线程可能会迅速耗尽资源。通过自定义线程池与CompletableFuture结合,可以有效控制并发数量,例如使用Semaphore限制并发数,确保系统稳定运行。

示例场景:在爬虫系统中,需要并发抓取大量网页,但不能无限制地创建线程。通过CompletableFuture结合Semaphore限制并发抓取的数量,既保证了抓取效率,又防止了资源耗尽。

使用示例:

通过一个具体的场景来展示其核心特性的应用:模拟在线购物平台中,用户下单后,系统需要异步执行三个任务——检查库存、验证用户支付能力和发送订单确认邮件,并在所有任务完成后,更新订单状态。

示例场景
  1. 检查库存:确认所购商品是否有足够的库存。
  2. 验证用户支付能力:检查用户的账户余额是否足够支付订单金额。
  3. 发送订单确认邮件:向用户邮箱发送订单确认信息。
代码示例:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class CompletableFutureDemo {public static void main(String[] args) throws Exception {// 创建一个线程池,用于执行异步任务ExecutorService executor = Executors.newFixedThreadPool(3);// 模拟用户下单Order order = new Order(1, "Product A", 100);// 异步检查库存CompletableFuture<Boolean> stockCheckFuture = CompletableFuture.supplyAsync(() -> checkStock(order), executor);// 异步验证用户支付能力CompletableFuture<Boolean> paymentValidationFuture = CompletableFuture.supplyAsync(() -> validatePayment(order), executor);// 异步发送订单确认邮件CompletableFuture<Void> emailSendingFuture = CompletableFuture.runAsync(() -> sendConfirmationEmail(order), executor);// 当所有任务完成时,更新订单状态CompletableFuture<Void> allTasks = CompletableFuture.allOf(stockCheckFuture, paymentValidationFuture, emailSendingFuture);// 等待所有任务完成allTasks.get(); // 注意:在实际生产中应避免使用get()阻塞主线程,这里仅作示例// 假设所有任务成功完成,更新订单状态updateOrderStatus(order, true);executor.shutdown();}// 检查库存的模拟方法private static boolean checkStock(Order order) {// ... 实际库存检查逻辑System.out.println("Checking stock for order: " + order);return true; // 假设库存充足}// 验证用户支付能力的模拟方法private static boolean validatePayment(Order order) {// ... 实际支付能力验证逻辑System.out.println("Validating payment for order: " + order);return true; // 假设支付能力充足}// 发送订单确认邮件的模拟方法private static void sendConfirmationEmail(Order order) {// ... 实际邮件发送逻辑System.out.println("Sending confirmation email for order: " + order);}// 更新订单状态的模拟方法private static void updateOrderStatus(Order order, boolean isSuccess) {if (isSuccess) {System.out.println("Order " + order + " processed successfully. Status updated.");} else {System.out.println("Order processing failed for " + order);}}// 订单类static class Order {int orderId;String productName;int amount;public Order(int orderId, String productName, int amount) {this.orderId = orderId;this.productName = productName;this.amount = amount;}@Overridepublic String toString() {return "Order{" +"orderId=" + orderId +", productName='" + productName + '\'' +", amount=" + amount +'}';}}
}
解释说明
  • 使用CompletableFuture.supplyAsync来启动异步任务,对于有返回值的任务(如库存检查和支付能力验证)。
  • 使用CompletableFuture.runAsync来启动无返回值的任务(如发送邮件)。
  • 利用CompletableFuture.allOf来组合多个CompletableFuture实例,等待所有任务完成。
  • 通过get()方法阻塞等待所有任务完成(实际应用中,应避免在主线程中阻塞等待,可以考虑使用.thenAccept().thenRun()等方法继续异步处理)。
  • 在所有异步任务完成后,调用updateOrderStatus方法更新订单状态。

注意事项

1. 线程池的选择与管理
  • 选择合适的线程池:默认情况下,如果没有显式提供ExecutorCompletableFuture会使用ForkJoinPool.commonPool(),这适合CPU密集型任务。对于IO密集型任务或大量短期任务,考虑自定义单线程或固定大小的线程池。
  • 避免资源耗尽:合理设置线程池大小,避免因创建过多线程而导致资源耗尽或系统崩溃。
  • 及时关闭线程池:使用完毕后,通过shutdownshutdownNow方法关闭线程池,尤其是对于短生命周期的线程池。
2. 异常处理
  • 全面的异常捕捉:确保每个可能抛出异常的异步操作都有相应的异常处理逻辑,如使用exceptionallyhandle方法。
  • 避免异常丢失:如果不恰当处理,异常可能在异步链中被忽略,导致问题难以追踪。
3. 避免过度链式调用
  • 代码可读性:过度的链式调用会使代码变得难以阅读和维护。适时使用局部变量存储中间结果,或拆分为多个方法。
  • 性能考量:链式调用过长可能导致不必要的复杂性,影响性能,尤其是在异常处理和资源管理方面。
4. 合理使用组合方法
  • 并行与串行:理解.thenCompose.thenCombine.thenApply等方法的区别,合理安排任务的执行顺序和依赖关系。
  • allOf与anyOf:使用.allOf等待所有任务完成,或.anyOf等待任一任务完成,根据需求选择合适的方法。
5. 避免阻塞操作
  • 非阻塞性质:尽量避免在使用CompletableFuture时调用阻塞方法,如.get(),除非确实需要阻塞等待结果。考虑使用.join()或异步处理结果。
  • 超时处理:如果必须使用.get(),考虑设置超时参数,防止无限等待。
6. 资源泄漏预防
  • 外部资源管理:在异步任务中使用外部资源(如数据库连接、文件句柄)时,确保资源被正确关闭,即使任务异常终止也要清理资源。
7. 测试与调试
  • 测试难度:异步代码的测试通常比同步代码更复杂。确保为异步流程编写充分的单元测试和集成测试。
  • 日志记录:适当添加日志记录,特别是关键路径和异常处理部分,有助于调试和问题定位。
8. 并发控制与限流
  • 并发控制:对于需要限制并发度的操作,如数据库写入,可以使用Semaphore等工具控制并发访问。
  • 限流保护:对于高负载场景,考虑在入口处实施限流策略,避免因并发度过高导致的服务不稳定。

优缺点

优点
  1. 提高并发性与响应性:通过非阻塞的异步执行,CompletableFuture允许程序在等待某个操作完成的同时执行其他任务,显著提高了系统的并发处理能力和响应速度。

  2. 链式编程与易于组合:丰富的API支持链式调用,使得异步任务的组织和逻辑流转清晰明了,易于构建复杂的异步流程。同时,提供了多种组合操作,如.thenCompose().thenCombine(),使得任务间的协作和依赖管理变得简单。

  3. 强大的异常处理机制:通过.exceptionally().handle()等方法,CompletableFuture允许开发者优雅地处理异步执行过程中的异常,确保程序的健壮性。

  4. 灵活性与可定制性:开发者可以根据需要选择不同的线程池执行异步任务,或者利用.supplyAsync().runAsync()的重载方法指定特定的Executor,提供了高度的灵活性和对资源的控制能力。

  5. 易于测试与调试:虽然异步编程通常比同步编程更难测试,但通过明确的链式调用和异常处理逻辑,CompletableFuture的代码结构相对清晰,有助于单元测试和调试。

缺点
  1. 代码可读性与维护性:虽然链式调用提高了编码效率,但在处理复杂逻辑时,过度的链式可能会导致代码变得冗长且难以阅读,特别是当涉及到多个条件分支和异常处理时。

  2. 潜在的资源管理问题:如果不小心管理,尤其是在大量使用CompletableFuture时,可能会导致线程池资源耗尽,或因忘记关闭资源(如数据库连接)而引发资源泄漏。

  3. 调试难度:异步编程的调试相较于同步编程更为复杂。异常的传播和处理路径可能跨越多个异步阶段,使得定位问题变得更加困难。

  4. 潜在的性能开销:虽然异步执行可以提升整体性能,但如果任务本身非常轻量级,创建和管理CompletableFuture实例以及线程上下文切换的开销可能会抵消异步带来的好处。

  5. 阻塞风险:尽管鼓励非阻塞使用,但在某些场景下,如直接使用.get()方法等待结果,可能会导致线程阻塞,影响程序的响应性。

可能遇到的问题及解决方案

1. 死锁问题

问题描述:在使用CompletableFuture时,如果存在相互等待的情况,可能导致死锁。例如,一个任务的完成依赖于另一个任务的结果,而后者又反过来等待前者的完成。

解决方案:仔细设计任务之间的依赖关系,避免循环等待。使用正确的组合方法,如.thenCompose()而不是.thenApply()来避免不必要的阻塞等待。

2. 资源泄漏

问题描述:未正确管理外部资源(如数据库连接、文件句柄)可能导致资源泄漏,尤其是在异步任务异常终止时。

解决方案:确保使用try-with-resources语句或finally块来确保资源被正确关闭。对于异步操作中的资源,考虑使用自定义的CompletableFuture,以便在任务完成或异常时清理资源。

3. 异常处理不当

问题描述:异常信息可能在异步链中丢失,尤其是当使用.thenApply().thenCompose()等方法时,异常不会自动传递到链的下一个阶段。

解决方案:积极使用.exceptionally().handle()方法来捕获和处理异常,确保异常信息被妥善处理并记录。

4. 性能瓶颈

问题描述:不合理的线程池配置或过度使用CompletableFuture可能导致线程创建过多,消耗过多系统资源,甚至引起OutOfMemoryError。

解决方案:根据任务特性合理配置线程池,例如,对于IO密集型任务,使用较大的线程池;对于CPU密集型任务,线程池大小应接近可用处理器的数量。避免过度创建CompletableFuture实例,必要时复用已完成的实例。

5. 代码可读性差

问题描述:过度的链式调用和复杂的异步逻辑可能会降低代码的可读性和可维护性。

解决方案:适度分解复杂逻辑,将长链式调用分解为多个小方法或使用中间变量存储结果。对于复杂的流程控制,考虑使用设计模式(如状态模式、责任链模式)来简化逻辑。

6. 调试困难

问题描述:异步执行的非线性特性使得通过日志或调试器跟踪程序流程变得困难。

解决方案:利用日志记录关键步骤和异常信息,使用条件断点和线程Dump分析工具来辅助调试。在设计阶段,尽量使异步逻辑清晰有序,便于追踪问题。

7. 过度阻塞

问题描述:虽然CompletableFuture鼓励非阻塞使用,但不当使用.get().join()方法可能导致主线程或工作线程阻塞。

解决方案:尽量使用.thenApply().thenAccept()等非阻塞方法处理结果。若需等待多个任务完成,优先使用.allOf().anyOf()结合.thenRun(),而非直接阻塞等待。

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

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

相关文章

C语言:__attribute__((packed))

一、简介 在使用结构体的时候&#xff0c;经常要根据结构体的长度来进行相关判断。但是按照C语言的规则&#xff0c;会对不同类型的数据类型进行自动对齐。有时候就会造成一些问题&#xff0c;如果不需要使用自动对齐的功能&#xff0c;就需要使用到本章的关键字。 二、自动对…

ICode国际青少年编程竞赛- Python-4级训练场-while语句入门

ICode国际青少年编程竞赛- Python-4级训练场-while语句入门 1、 while Flyer.disappear():wait() Dev.step(2)2、 Dev.step(1) while Flyer.disappear():wait() Dev.step(5)3、 while Flyer[0].disappear():wait() Dev.step(3) Dev.step(-1) while Flyer[0].disappear():…

爬虫-无限debug场景 解决方式

解决无限debug 场景1 1. 鼠标右键 选择 continue to here&#xff08;此处不停留&#xff09;2. 鼠标右键 选择 edite breakpoint 设置 10 保证条件不成立 这行永远不执行3.方法置空 1. 方法调用加断点2. 控制台 setInterval function name() {}4. 替换文件 5. hoo…

【CSDN搜材料的小技巧】怎么快速查到高质量最新的内容

问题描述: 我最近搜CSDN已经搜累了&#xff0c;好多东西明显是有问题的&#xff0c;还有一堆人复制粘贴&#xff0c;从海量文章中提取出最新且高质量文章成了当务之急&#xff01; 解决方案: 我本来想写个爬虫按照文章的收藏或者点赞排序的&#xff0c;无意中看到了这篇文章…

msix packaging tool打包问题

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

FreeRTOS学习 -- 任务相关API函数

一、任务创建和删除API函数 FreeRTOS 最基本的功能就是任务管理&#xff0c;而任务管理最基本的操作就是创建和删除任务。 FreeRTOS的任务创建和删除API函数如下&#xff1a; 1、函数 xTaskCreate() 此函数用来创建一个任务&#xff0c;任务需要 RAM 来保存于任务有关的状…

子查询之二(不相关子查询与相关子查询)

1. 相关子查询 如果子查询的执行依赖于外部查询&#xff0c;通常情况下都是因为子查询中的表用到了外部的表&#xff0c;并进行的条件关联&#xff0c;因此每一次执行一次外部查询&#xff0c;子查询都会重新计算一次&#xff0c;这样的子查询称为关联子查询. 相关子查询按照…

VS配置三方依赖

1.配置include 1.1.打开属性 1.2.打开“配置属性”-"C/C"-"常规" 2.配置lib 2.1.配置lib目录 打开"配置属性"-“链接器”-“常规”。 2.2.配置具体的lib 打开"配置属性"-"链接器"-“输入”。 也可以通过代码方式加入&…

【挑战30天首通《谷粒商城》】-【第一天】【10 番外篇】 解决docker 仓库无法访问 + MobaXterm连接VirtualBox虚拟机

文章目录 课程介绍 1、解决docker 仓库无法访问 2、 MobaXterm连接VirtualBox虚拟机 Stage 1&#xff1a;下载MobaXterm选择适合你的版本 Stage 2&#xff1a;vagrant ssh 连接&#xff0c;开启ssh访问 Stage 2-1&#xff1a;su获取root账号权限,输入密码&#xff08;默认vagra…

Visual Studio生成C++的DLL文件(最简单版)

前言 当你在使用C编写一些可重用的代码时&#xff0c;将其打包成一个动态链接库&#xff08;DLL&#xff09;可以使其更容易地被其他项目或者程序调用和使用。Visual Studio提供了一种简单的方式来生成C的DLL文件。下面是一个关于如何在Visual Studio中生成C的DLL文件的简单教…

力扣HOT100 - 215. 数组中第K个最大元素

解题思路&#xff1a; 快速选择&#xff0c;目标是找出数组中第 k 小&#xff08;或第 k 大&#xff09;的元素&#xff0c;而不是对整个数组进行排序。 &#xff08;需要和快排进行区分&#xff0c;快排的目的是排序&#xff09; 注意&#xff1a; i l - 1, j r 1; 为什…

羊大师:羊奶助力宝宝成长无忧

羊大师&#xff1a;羊奶助力宝宝成长无忧 在宝宝的成长过程中&#xff0c;营养是至关重要的。随着人们对健康和营养的日益关注&#xff0c;越来越多的家长开始寻找更优质的食品来喂养宝宝。羊奶作为一种营养丰富、易于消化的天然食品&#xff0c;逐渐成为了家长们的首选。 羊奶…

现场工程师出手--虚拟化软件预留内存过大导致其他程序崩溃问题

项目场景&#xff1a; 一位学生有一台笔记本电脑&#xff0c;安装了Android&#xff0c;Kafka虚拟机很多软件。笔记本配置了20GB内存&#xff0c;固态硬盘&#xff0c;但最近很卡&#xff0c;Android Stuido经常闪退&#xff0c;一些游戏也无法运行。 问题描述 由于Android S…

2024最新洗地机选购攻略!分享四款热门洗地机推荐

洗地机可以说是现代家庭生活中一大利器&#xff0c;它能帮我们快速搞定家里的地板清洁工作&#xff0c;省去了自己清洗滚刷的麻烦。不过&#xff0c;当下市面上洗地机品牌种类繁多&#xff0c;价格区间也相差悬殊&#xff0c;要选择一款性价比较高、使用体验较好的洗地机产品&a…

Vision Mamba:高效视觉表示学习双向状态空间模型,超越Vision Transformer!

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享&#xff0c;与你一起了解前沿深度学习信息&#xff01; Vision Mamba: Efficient Visual Representation Learning with Bidirectional State Space Model 引言&#xff1a;探索视觉领域的新方向 在计算机视觉领域&…

beyondCompare工具

目录 一 资源地址 二 过期处理 一 资源地址 链接&#xff1a;https://pan.baidu.com/s/10TxNj0ZvLh2qusYZCPaGRA?pwduq26 提取码&#xff1a;uq26 二 过期处理 过期后删除对应路径下所有文件&#xff0c;重启软件即可

教你解决PUBG绝地求生游戏中闪退掉线无法重连回去的问题

《绝地求生》&#xff08;PUBG&#xff09;&#xff0c;作为一款在全球范围内掀起热潮的战术竞技游戏&#xff0c;以其栩栩如生的战场环境和令人心跳加速的生存冒险博得了广大玩家的青睐。然而&#xff0c;一些玩家在经历了一场惊心动魄的对局后&#xff0c;却面临了一个不大不…

error C2039: “NotifySeverity“: 不是 “osg“ 的成员 问题分析

程序从osg3.6.5Qt5.9osgearth2.10环境中移植到osg3.7.0Qt5.15.2osgearth3.3环境中&#xff0c;出现了无尽的错误。 有些错误很莫名奇妙&#xff0c;比如下述错误&#xff1a; D:\OsgEarth3.3\include\osgEarth\Notify(34,53): error C2039: "NotifySeverity": 不是 &…

耳朵嗡嗡响,睡不好?张朝晖主任:近几年越来越多年轻人出现耳鸣

王先生&#xff0c;由于工作压力大、应酬多&#xff0c;经常熬夜&#xff0c;心情郁闷情绪差&#xff0c;前不久他发觉自己左耳朵嗡嗡响&#xff0c;还有真正耳鸣&#xff0c;他以为是没休息好&#xff0c;睡两天就好了。 在休假期间好好休息后&#xff0c;耳鸣依然没有改变。…