前言

在现代软件开发中,异步编程(Asynchronous Programming) 已经成为构建高性能、高并发应用程序的关键技术之一。Java 作为一门广泛应用于后端服务开发的语言,在其发展过程中不断引入和优化异步编程的支持。从最初的 ThreadRunnable,到后来的 FutureCompletableFuture,再到如今基于反应式流(Reactive Streams)的框架如 Project ReactorRxJava,Java 的异步编程生态日趋成熟。

本文将从基础概念讲起,逐步深入讲解 Java 中的各种异步编程方式,并辅以代码示例,帮助你全面掌握这一核心技术。


一、什么是异步编程?

1. 同步 vs 异步

  • 同步编程:任务按顺序执行,每个任务必须等待前一个任务完成后才能开始。
  • 异步编程:任务可以并行或并发执行,调用者无需等待任务完成即可继续执行其他操作。

例如:

// 同步
int result = calculate(); // 等待结果返回
System.out.println(result);// 异步
Future<Integer> future = executor.submit(() -> calculate());
System.out.println("继续做其他事情");
Integer result = future.get(); // 可选地等待结果

2. 为什么需要异步编程?

  • 提高程序响应速度,避免阻塞主线程;
  • 更好地利用多核 CPU 资源;
  • 支持高并发场景下的性能优化;
  • 构建非阻塞 I/O 操作(如网络请求、数据库访问等)。

二、Java 原生异步编程方式

1. Thread + Runnable / Callable

这是 Java 最原始的并发模型,通过创建线程来实现异步执行。

new Thread(() -> {System.out.println("异步任务执行");
}).start();

优点:

  • 简单易懂。

缺点:

  • 难以管理大量线程;
  • 缺乏对任务依赖关系的控制;
  • 容易造成资源浪费。

2. ExecutorService + Future

为了更好地管理线程资源,Java 提供了线程池 ExecutorService,配合 Future 来获取异步结果。

ExecutorService executor = Executors.newFixedThreadPool(4);
Future<Integer> future = executor.submit(() -> {return calculate();
});try {Integer result = future.get(); // 阻塞直到结果可用
} catch (InterruptedException | ExecutionException e) {e.printStackTrace();
}

优点:

  • 更好的资源管理;
  • 可获取异步结果。

缺点:

  • Future.get() 是阻塞的;
  • 不支持链式调用或组合多个异步任务。

3. CompletableFuture(Java 8+)

这是 Java 对异步编程的一次重大升级,提供了丰富的 API 来处理异步任务的组合、异常处理、回调等。

示例:基本使用
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {// 模拟耗时任务try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}return "Hello";
});future.thenApply(s -> s + " World").thenAccept(System.out::println); // 输出 Hello World
示例:组合多个异步任务
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 20);future1.thenCombine(future2, (a, b) -> a + b).thenAccept(sum -> System.out.println("Sum: " + sum));
示例:异常处理
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {if (true) throw new RuntimeException("出错啦!");return 100;
});future.exceptionally(ex -> {System.err.println("发生异常:" + ex.getMessage());return -1;
});

优点:

  • 支持链式调用;
  • 支持任务组合(allOf、anyOf、thenApply、thenCompose 等);
  • 支持异常处理;
  • 支持非阻塞回调(thenAccept、thenRun);

缺点:

  • 代码可读性稍差(尤其嵌套较多时);
  • 不支持背压(backpressure)机制。

三、反应式编程(Reactive Programming)

随着异步需求的增长,传统的 CompletableFuture 在处理复杂数据流时显得力不从心。于是,出现了基于 反应式流规范(Reactive Streams) 的编程范式。

Java 中主流的反应式库包括:

  • Project Reactor(Spring WebFlux 使用)
  • RxJava

它们都实现了 Publisher/Subscriber 模型,并支持背压、异步调度、错误传播等高级特性。

1. Project Reactor

Mono & Flux
  • Mono<T>:表示 0 或 1 个元素的异步序列(适合单一结果);
  • Flux<T>:表示 0 到 N 个元素的异步序列(适合流式数据)。
示例:Mono 基本使用
Mono<String> mono = Mono.just("Hello").map(s -> s + " World").doOnNext(System.out::println);mono.subscribe(); // 触发执行
示例:Flux 多元素处理
Flux.range(1, 5).map(i -> i * 2).subscribe(System.out::println);
示例:异步处理与调度器
Scheduler scheduler = Schedulers.boundedElastic();Mono<String> asyncMono = Mono.fromCallable(() -> {Thread.sleep(1000);return "Async Result";
})
.scheduleOn(scheduler);asyncMono.subscribe(System.out::println);
示例:错误处理
Mono.error(new RuntimeException("出错了!")).onErrorResume(e -> Mono.just("默认值")).subscribe(System.out::println);

优点:

  • 支持背压;
  • 支持非阻塞流式处理;
  • 与 Spring WebFlux、Spring Boot 等现代框架集成良好;
  • 强大的操作符链式调用能力。

缺点:

  • 学习曲线较陡;
  • 调试相对困难(尤其涉及异步调度时);
  • 不适合简单任务。

四、异步编程中的常见问题及解决方案

1. 死锁

当多个异步任务互相等待彼此的结果时,可能造成死锁。解决办法:

  • 使用合适的线程池;
  • 避免循环依赖;
  • 使用 CompletableFuturecomplete() 方法手动设置结果。

2. 线程切换开销大

频繁切换线程会带来上下文切换的成本。建议:

  • 使用 Schedulers.single()boundedElastic() 控制线程数量;
  • 尽量使用非阻塞操作;
  • 避免在异步流中频繁进行线程切换。

3. 异常处理不透明

异步任务中的异常可能被吞掉或难以捕获。建议:

  • 使用 .onErrorReturn().onErrorResume().doOnError() 显式处理;
  • 使用日志记录所有异常信息;
  • 使用 .block() 测试时强制抛出异常。

五、实际应用场景举例

场景 1:并发下载多个文件

List<CompletableFuture<String>> futures = Arrays.asList("url1", "url2", "url3").stream().map(url -> CompletableFuture.supplyAsync(() -> downloadFile(url))).collect(Collectors.toList());CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])
);allFutures.thenRun(() -> {futures.forEach(f -> {try {System.out.println(f.get());} catch (Exception e) {e.printStackTrace();}});
});

场景 2:WebFlux 异步接口

@RestController
public class AsyncController {@GetMapping("/data")public Mono<String> getData() {return Mono.fromCallable(this::fetchDataFromDB).subscribeOn(Schedulers.boundedElastic());}private String fetchDataFromDB() {// 模拟数据库查询Thread.sleep(1000);return "Some Data";}
}

六、总结

技术是否推荐特点
Thread + Runnable一般原始,适合简单场景
ExecutorService + Future一般适合少量并发任务
CompletableFuture推荐功能强大,适合大多数异步逻辑
Reactor (Mono/Flux)推荐支持背压、流式处理,适合高并发、响应式系统

七、参考资料

  • Oracle 官方文档:CompletableFuture
  • Project Reactor 官方文档
  • Java Concurrency in Practice

如果你正在构建一个高性能、高并发的 Java 应用,掌握异步编程是必不可少的技能。希望这篇博客能帮助你更好地理解 Java 中的异步编程机制,并在项目中灵活运用!

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

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

相关文章

MySQL逻辑删除与唯一索引冲突解决

问题背景 在MySQL数据库设计中&#xff0c;逻辑删除&#xff08;软删除&#xff09;是一种常见的实践&#xff0c;它通过设置标志位&#xff08;如is_delete&#xff09;来标记记录被"删除"&#xff0c;而不是实际删除数据。然而&#xff0c;当表中存在唯一约束时&am…

php命名空间用正斜杠还是反斜杠?

在PHP中&#xff0c;命名空间使用反斜杠&#xff08;\&#xff09;作为分隔符&#xff0c;这是PHP语言规范明确规定的。反斜杠在命名空间中扮演路径分隔的角色&#xff0c;用于区分不同层级的命名空间。 具体说明&#xff1a;语法规则 PHP命名空间使用反斜杠&#xff08;\&…

《从依赖纠缠到接口协作:ASP.NET Core注入式开发指南》

在C#的ASP.NET Core开发中&#xff0c;依赖注入绝非简单的技术技巧&#xff0c;而是重构代码关系的底层逻辑。它像一套隐形的神经网络&#xff0c;让程序模块摆脱硬编码的束缚&#xff0c;在运行时实现动态连接&#xff0c;从而为系统注入可测试、可进化的核心生命力。理解其深…

星云ERP本地环境搭建笔记

看到星云ERP两个比较实用的功能&#xff0c;编号规则和打印模板&#xff0c;如下图所示&#xff0c;于是本地跑起来学习学习。开发环境必备&#xff1a;1. JDK 1.82. MySQL 5.73. Redis 44. RabbitMQ 3.12.45. nodejs 206. pnpm 9.7.1 (npm install -g pnpm9.7.1)其他开发工具&…

RedisJSON 的 `JSON.ARRAPPEND`一行命令让数组动态生长

1 、 为什么选择 JSON.ARRAPPEND 在传统的键值模型里&#xff0c;若要往数组尾部追加元素&#xff0c;通常需要 取→改→写 三步&#xff1a; GET 整个 JSON&#xff1b;在应用层把元素 push 进数组&#xff1b;SET 回 Redis。 一条 JSON.ARRAPPEND 则可一次完成&#xff0c;具…

14:00开始面试,14:08就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到4月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

Unity物理系统由浅入深第四节:物理约束求解与稳定性

Unity物理系统由浅入深第一节&#xff1a;Unity 物理系统基础与应用 Unity物理系统由浅入深第二节&#xff1a;物理系统高级特性与优化 Unity物理系统由浅入深第三节&#xff1a;物理引擎底层原理剖析 Unity物理系统由浅入深第四节&#xff1a;物理约束求解与稳定性 物理引擎的…

深入浅出Kafka Consumer源码解析:设计哲学与实现艺术

一、Kafka Consumer全景架构 1.1 核心组件交互图 #mermaid-svg-JDEEOd2M5PzLkYa6 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-JDEEOd2M5PzLkYa6 .error-icon{fill:#552222;}#mermaid-svg-JDEEOd2M5PzLkYa6 .erro…

Matplotlib(一)- 数据可视化与Matplotlib

文章目录一、数据可视化1. 数据可视化的概念2. 数据可视化流程3. 数据可视化目的4. 常见的可视化图表4.1 折线图4.2 柱形图4.3 条形图4.4 堆积图4.4.1 堆积面积图4.4.2 堆积柱形图和堆积条形图4.5 直方图4.6 箱形图4.7 饼图4.8 散点图4.9 气泡图4.10 误差棒图4.11 雷达图二、Py…

传输层协议UDP原理

端口号回顾端口号的作用类似pid&#xff0c;用来标识进程的唯一性。只是为了与系统解耦&#xff0c;所以有了端口号。通过ip来确定唯一主机&#xff0c;再通过端口号找到指定的进程。就可以让全网内唯一的两个进程通信了。所以一个完整的报文至少要携带ip和端口号&#xff0c;i…

【牛客刷题】小红的数字删除

文章目录 一、题目介绍1.1 题目描述1.2 输入描述:1.3 输出描述:1.4 示例11.5 示例2二、解题思路2.1 核心观察2.2 关键问题处理三、算法实现四、算法分析4.1 算法流程图4.2 为什么这么设计算法?4.3 算法复杂度五、模拟演练数据示例1: "103252"示例2: "333&quo…

《大数据技术原理与应用》实验报告三 熟悉HBase常用操作

目 录 一、实验目的 二、实验环境 三、实验内容与完成情况 3.1 用Hadoop提供的HBase Shell命令完成以下任务 3.2 现有以下关系型数据库中的表和数据&#xff0c;要求将其转换为适合于HBase存储的表并插入数据&#xff1a; 四、问题和解决方法 五、心得体会 一、实验目的…

微服务初步入门

服务拆分原则 单一职责原则 单一职责原则原本是面向对象设计的一个基本原则&#xff0c;是指一个类应该专注于单一的功能&#xff0c;不要存在多于一个导致类变更的原因 在微服务架构中&#xff0c;是指一个微服务只负责一个功能或者业务领域&#xff0c;每个服务应该由清晰的定…

Liunx操作系统笔记5

用户管理命令&#xff1a; useradd命令&#xff1a; useradd命令的功能是创建并设置用户信息。使用useradd命令可以自动完成用户信息、基本组、家目录等的创建工作&#xff0c;并在创建过程中对用户初始信息进行定制。语法格式:useradd 参数 用户名常用参数: -M 不建立用…

spring-ai-alibaba 接入Tushare查询股票行情

最近spring-ai-alibaba主干分支新增了对Tushare的支持&#xff0c;一起来看看如何使用简单样例老样子&#xff0c;分三步进行&#xff1a;第一步&#xff1a;添加依赖<dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-aliba…

Java使用Langchai4j接入AI大模型的简单使用(一)

一、LangChain4j 简介 LangChain4j 是 Java 生态中的 LangChain 实现&#xff0c;是一个用于构建大语言模型(LLM)应用程序的框架。它提供了与各种LLM服务集成的能力&#xff0c;并简化了构建复杂AI应用的过程。 LangChain4j官方文档&#xff1a;Integrations | LangChain4j …

Linux —— A / 基础指令

建议学习路径&#xff1a;Linux系统与系统编程 ⇒ Linux网络和网络编程 ⇒ MySQL一、初识shell命令 1.1、关于 Linux 桌面很多同学的 Linux 启动进⼊图形化的桌⾯. 这个东西⼤家以后就可以忘记了。以后的工作中没有机会使用图形界面。思考: 为什么不使用图形界面? 1.2、下…

[论文阅读] 人工智能 + 软件工程 | 用大语言模型+排名机制,让代码评论自动更新更靠谱

LLMCup&#xff1a;用大语言模型排名机制&#xff0c;让代码评论自动更新更靠谱 LLMCup: Ranking-Enhanced Comment Updating with LLMsarXiv:2507.08671 LLMCup: Ranking-Enhanced Comment Updating with LLMs Hua Ge, Juan Zhai, Minxue Pan, Fusen He, Ziyue Tan Comments: …

悲观锁 乐观锁

悲观锁 乐观锁 在没有加锁的秒杀场景下 每秒打进来的请求是巨大的 高并发场景下 我们发现不仅异常率高的可怕 库存竟然还变成了负数 这产生的结果肯定是很大损失的 那为什么会出现超卖问题呢 我们假设有下面两个线程线程1查询库存&#xff0c;发现库存充足&#xff0c;创建订单…

如何使用Cisco DevNet提供的免费ACI学习实验室(Learning Labs)?(Grok3 回答)

Cisco DevNet 提供的免费 ACI&#xff08;Application Centric Infrastructure&#xff09;学习实验室&#xff08;Learning Labs&#xff09;是帮助用户学习和实践 Cisco ACI 技术&#xff08;包括 APIC 控制器&#xff09;的优秀资源&#xff0c;适合网络工程师、开发者和准备…