Java实现异步的原因、场景、实现异步的方式


异步,作为性能调优核心方式之一,经常被用于各种高并发场景。

实现异步的原因:

Java 中实现异步的主要原因是为了提高程序的性能和响应速度。如果程序中存在大量的IO操作或者其他需要长时间等待的操作(如网络请求、文件读写等),使用同步方式会导致线程长时间阻塞,降低系统的并发能力和响应速度。而使用异步方式可以将等待时间利用起来,让 CPU 在等待 IO 操作完成时去执行其他任务,从而提高系统的并发性能和响应速度。

Java实现异步的原因主要有以下几点:

  1. 提高性能:异步编程可以显著提高程序的性能。在等待某些耗时操作(如I/O操作)完成时,异步编程允许其他代码继续执行,而不是等待这些操作完成。这样可以使程序更高效地利用系统资源。
  2. 改善用户体验:异步编程可以用于实现非阻塞操作,例如在用户等待数据下载或页面加载时,异步操作可以继续执行其他任务,从而减少用户等待时间,改善用户体验。
  3. 更好的响应性能:由于异步操作不会阻塞线程,因此可以处理更多的用户请求,提高系统的响应性能。

异步和同步的区别

同步:
调用方在调用过程中,持续阻塞,一直到返回结果。
同步获取结果的方式是: 主动等待。
异步:
调用方在调用过程中,不会阻塞, 不直接等待返回结果, 而是执行其他任务。
异步获取结果的方式是 : 被动通知或者 被动回调。

异步使用的场景

场景1: 超高并发 批量 写 mysql 、批量写 elasticSearch
场景2: 超高并发 批量 IO
场景3: 超高并发 发送短信、发邮件
场景4: 超高并发 发送消息
场景5: 超高吞吐 生产者、 消费者 场景
场景6: 超高吞吐 发布、 订阅 场景
场景7: 分布式的 通知场景
场景8:异步回调场景
场景9:其他的 异步场景, 不知道能列举出多少,总之非常多

在 Java 中,异步编程通常用于以下场景:

  1. 网络编程:对于涉及网络通信的应用,如 HTTP 请求、TCP 连接等,由于网络延迟和带宽限制,常常需要异步处理以避免阻塞主线程,提高系统的并发能力和响应速度。
  2. IO 操作:包括文件读写、数据库访问等操作,这些操作通常是耗时的,并且会阻塞当前线程。通过异步方式执行 IO 操作,可以充分利用等待时间,提高程序的效率和性能。
  3. 并发处理:在需要同时处理多个任务或事件的情况下,使用异步可以更好地利用系统资源,提高处理效率,如并发下载、批量处理等。
  4. 定时任务:对于需要定时执行的任务,如定时发送邮件、定时备份数据等,可以使用异步方式执行定时任务,避免阻塞主线程。
  5. 事件驱动编程:在事件驱动的架构中,如 GUI 应用、服务器端的事件处理等,异步编程可以很好地处理事件触发和事件响应。
  6. 大数据处理:在处理大规模数据时,如数据清洗、分析、计算等,使用异步方式可以提高处理效率和系统的并发能力。
  7. 消息队列处理:在使用消息队列进行异步消息处理时,可以实现生产者和消费者的异步通信,提高系统的可伸缩性和吞吐量。
    以上是在 Java 中常见的使用异步编程的场景,通过合理地使用异步编程,可以提高系统的性能、响应速度和并发能力。

实现异步的方式:

在 Java 中实现异步的方式有以下几种:

  1. 多线程:使用多线程可以让程序同时执行多个任务,从而提高程序的并发性能。Java 提供了多种方式来实现多线程,如继承 Thread 类或者实现 Runnable 接口,并使用 Thread 类的 start() 方法启动线程。
  2. 线程池:使用线程池可以避免频繁创建和销毁线程的开销,从而提高程序的并发能力和性能。Java 提供了 Executor 框架来管理线程池,通过预先创建一定数量的线程,可以让这些线程轮流执行多个任务。
  3. Future 和 CompletableFuture:使用 Future 和 CompletableFuture 可以在异步任务执行完成后获取返回结果。Future 代表一个异步计算的结果,可以使用 get() 方法阻塞当前线程等待计算完成,并获取计算结果。CompletableFuture 除了支持 Future 的所有功能之外,还提供了一些额外的方法,如 thenApply()、thenAccept()、thenRun() 等,可以在任务完成后执行一些操作。
    Java 5引入了Future和CompletableFuture类,它们可以用于表示异步操作的结果。Future表示一个异步计算的结果,可以用来查询计算是否完成,获取计算结果。CompletableFuture是Future的扩展,增加了更丰富的功能,例如链式调用、异常处理等。
  4. 回调函数:通过定义回调函数,可以在异步任务完成后执行一些特定的操作。在 Java 中,可以使用函数式接口来定义回调函数,然后传递给异步任务,当任务完成时,会自动调用回调函数。
    回调函数是一种实现异步编程的常见方式。在异步操作完成时,调用预先定义的回调函数。这种方式的缺点是可能导致“回调地狱”(回调函数嵌套过深导致的代码难以理解和维护)
  5. 事件监听器:使用事件监听器可以在事件发生时触发相应的操作,从而实现异步处理。Java 中提供了事件监听器接口和事件源接口,可以通过注册事件监听器来监听事件,并在事件发生时执行相应的操作。
    以上是 Java 中实现异步的常见方式,根据实际需求和系统架构选择合适的方式可以提高系统的性能和响应速度。
  6. 使用Reactor或RxJava:这些是第三方库,可以帮助你更方便地实现异步编程。它们采用了响应式编程模型,可以方便地处理异步操作和事件。
  7. 使用Spring的@Async注解:如果你使用的是Spring框架,可以使用@Async注解来声明一个方法是异步的。Spring会在合适的时候执行这些方法,无需你手动创建和管理线程池。

以上就是Java实现异步编程的一些常见原因和实现方式。具体选择哪种方式取决于你的应用场景和需求。

实现异步的20种实现方式

新建线程Thread 实现异步
线程池化 实现异步
Future 阻塞式异步
guava 回调式异步
Netty 回调式异步
Servlet 3.0 异步
CompletableFuture 回调式异步
JDK 9 Flow 响应式编程
RxJava 响应式 异步
Reactor 响应式 异步
Spring注解@Async 异步
EventBus 框架 发布订阅模式异步
Spring ApplicationEvent 事件 发布订阅模式
RocketMq 消息队列 分布式 发布订阅模式(Pub/Sub) 异步
Redis 消息队列 分布式 发布订阅模式(Pub/Sub) 异步
Distruptor 框架异步
ForkJoin 框架异步
RocketMQ源码中ServiceThread 能急能缓的高性能异步

方式3:Future 阻塞式异步
为了获取异步线程的返回结果,以及更好的对异步线程的干预,Java在1.5版本之后提供了一种新的多线程的创建方式—— FutureTask方式。
FutureTask方式包含了一系列的Java相关的类,处于java.util.concurrent包中。
使用FutureTask方式进行异步调用时,所涉及的重要组件为FutureTask类和Callable接口。
Future 的调用方式,属于阻塞式异步
主要原因在于,在获取异步线程处理结果时,需要主线程主动通过Future.get() 去获取,
如果异步线程没有执行完,那么Future.get() 会阻塞 调用线程,一直到超时。

阻塞式异步Future的不足之处
Future的不足之处的包括以下几点:
无法被动接收异步任务的计算结果:
虽然我们可以主动将异步任务提交给线程池中的线程来执行,但是待异步任务执行结束之后,主线程无法得到任务完成与否的通知,它需要通过get方法主动获取任务执行的结果。
Future件彼此孤立:
有时某一个耗时很长的异步任务执行结束之后,你想利用它返回的结果再做进一步的运算,该运算也会是一个异步任务,两者之间的关系需要程序开发人员手动进行绑定赋予,Future并不能将其形成一个任务流(pipeline),每一个Future都是彼此之间都是孤立的,所以才有了后面的CompletableFuture,CompletableFuture就可以将多个Future串联起来形成任务流。
Futrue没有很好的错误处理机制:
截止目前,如果某个异步任务在执行发的过程中发生了异常,调用者无法被动感知,必须通过捕获get方法的异常才知晓异步任务执行是否出现了错误,从而在做进一步的判断处理。

方式4:guava 回调式异步
由于JDK在1.8之前没有 回调式异步组件,于是出现了很多 开源的 回调式异步组件。
比较常用的是 guava 的回调式异步。
Guava是Google提供的Java扩展包,它提供了一种异步回调的解决方案。
Guava中与异步回调相关的源码处于com.google.common.util.concurrent包中。
包中的很多类都用于对java.util.concurrent的能力扩展和能力增强。
比如,Guava的异步任务接口ListenableFuture扩展了Java的Future接口,实现了异步回调的的能力。

方式5:Netty 回调式异步
由于JDK在1.8之前没有 回调式异步组件,于是出现了很多 开源的 回调式异步组件。
Netty 也算其中之一。
Netty 是 一个 著名的高性能NIO王者框架, 是 IO 的王者组件。 具体,请参见尼恩梳理的四大王者组件。
Netty 除了作为NIO框架之王,其子模也是可以单独使用的,比如说异步回调模块。
Netty 的 回调式异步组件 更加牛掰,为啥呢?
通过Netty源码可以知道: Netty 的 回调式异步组件不光提供了外部的回调监听设置,而且可以在异步代码中, 通过Promise接口,可以对回调结果进行干预,比如说在进行回调之前,执行一些其他的操作。

方式6:Servlet 3.0 异步
梳理一下: Callback 异步回调的使用场景。
Callback 真正体现价值,是它与 NIO 技术结合之后。
CPU 密集型场景,采用 Callback 回调没有太多意义;
IO 密集型场景,如果是使用 BIO模式,Callback 同样没有意义,因为一个连接一个线程,IO线程是因为 IO 而阻塞。
IO 密集型场景,如果是使用 NIO 模式,使用Callback 才有意义。 NIO是少量IO线程负责大量IO通道,IO线程需要避免线程阻塞,所以,也必须使用 Callback ,才能使应用得以被开发出来。
所以,高性能的 NIO 框架如 Netty ,都是基于 Callback 异步回调的。
但是,在微服务流行的今天,Netty 却没有在WEB服务器中占据统治地位。
微服务系统中,多级服务调用很常见,一个服务先调 A,再用结果 A 调 B,然后用结果 B 调用 C,等等。
如果使用Netty 作为底层服务器,IO 线程能大大降低,能处理的连接数(/请求数)也能大大增加,那么,为啥Netty 却没有在WEB服务器中占据统治地位呢?
—这其中的难度来自两方面:
一是 NIO 和 Netty 本身的技术难度,
二是 Callback hell:Callback 风格所导致的代码理解和维护的困难。
因此,Netty 通常用于在基础架构层面,在业务系统中应用较少。
这也是大厂小伙人人要求 精通Netty,而中小厂小伙伴,不怎么认识Netty的原因。
当然,作为IO之王,学习Netty对应提升大家的内功,是至关重要的。
Netty的知识,具体请参见《Java高并发核心编程 卷1 加强版》,很多小伙伴,靠此书入的门。
直接使用 Netty 开发WEB应用会遇到技术难度挑战、以及 Callback Hell 问题。
所以,Servlet 3.0 提供了一个异步解决方案。

方式7:回调式 异步CompletableFuture
JDK 1.8之前并没有实现回调式的异步,CompletableFuture是JDK 1.8引入的实现类,实现了JDK内置的异步回调模式异步。
CompletableFuture的创新是:通过 链式调用,解决 Callback Hell(回调地狱)问题, 让代码变得的可理解行更强,可读性 更强。
CompletableFuture 该类实现了Future和CompletionStage两个接口。
该类的实例作为一个异步任务,可以在自己异步执行完成之后触发一些其他的异步任务,从而达到异步回调的效果。

方式8:JDK 9 Flow 响应式编程
但是 JDK 8 的 CompletableFuture 属于链式调用,它在形式上带有一些响应式编程的函数式代码风格。
因为 Callback Hell 对代码可读性有很大杀伤力,从开发人员的角度来讲,反应式编程技术和链式调用一样,使得代码可读性要比 Callback 提升了许多。
响应式流从2013年开始,作为提供非阻塞背压的异步流处理标准的倡议。
Reactive Stream是一套基于发布/订阅模式的数据处理规范。
更确切地说,Reactive流目的是“找到最小的一组接口,方法和协议,用来描述必要的操作和实体以实现这样的目标:以非阻塞背压方式实现数据的异步流”。
**响应式流(Reactive Streams)**是一个响应式编程的规范,用来为具有非阻塞背压(Back pressure)的异步流处理提供标准,用最小的一组接口、方法和协议,用来描述必要的操作和实体。这里涉及到一个关键概念叫 Backpressure,国内大部分翻译为背压,我们先来了解这是什么。
响应式编程,其实就是对数据流的编程,而对流的处理对数据流的变化进行响应,是通过异步监听的方式来处理的。既然是异步监听,就涉及到监听事件的发布者和订阅者,数据流其实就是由发布者生产,再由一个或多个订阅者进行消费的元素(item)序列。

方式9:RxJava 响应式 异步
在JDK 9 Flow 之前,响应式编程 的框架,早就存在。
比如说, 席卷了android 端编程的 RxJava 框架。RxJava 是一种响应式编程,来创建基于事件的异步操作库。
这个组件,是 Netflix的杰作,也叫作Netflix RxJava。
这个框架,在Java 后端的中间件中,也有广泛使用,比如在Hystrix 源码中,就用大量用到。

方式10:Reactor 响应式 异步

目前,在 Java 领域实现了反应式编程的技术除了 Netflix RxJava ,还有 Spring 的 Project Reactor。
Netflix 网飞 的RxJava 出现时间更早,在前端开发领域应用的比后端更要广泛一些。
Spring 的 Project Reactor的 3.0 版本作为 Spring 5 的基础,在17年底发布,推动了后端领域反应式编程的发展。

方式11:Spring的@Async异步
在Spring中,使用@Async标注某方法,可以使该方法变成异步方法,这些方法在被调用的时候,将会在独立的线程中进行执行,调用者不需等待该方法执行完成。
但在Spring中使用@Async注解,需要使用@EnableAsync来开启异步调用。
@Async注解,默认使用系统自定义线程池。
在实际项目中,推荐等方式是是使用自定义线程池的模式。
可在项目中设置多个线程池,在异步调用的时候,指明需要调用的线程池名称,比如:@Async(“taskName”)

方式12:EventBus 发布订阅模式异步

实际开发中,常常 通过事件总线EventBus/AsyncEventBus进行JAVA模块解耦 ,
比如,在顶级开源组件 JD hotkey的源码中, 就多次用到 EventBus/AsyncEventBus进行JAVA模块解耦
掌握了 EventBus ,在平时的开发中,多了一个神器

方法13:Spring ApplicationEvent事件实现异步
Spring内置了简便的事件机制,原理和EventBus 差不多
通过Spring ApplicationEvent事件, 可以非常方便的实现事件驱动,核心类包括
ApplicationEvent,具体事件内容,事件抽象基类,可继承该类自定义具体事件
ApplicationEventPublisher,事件发布器,可以发布ApplicationEvent,也可以发布普通的Object对象
ApplicationListener,事件监听器,可以使用注解@EventListener
TransactionalEventListener,事务事件监听,可监听事务提交前、提交后、事务回滚、事务完成(成功或失败)

方法14: RocketMq 消息队列 分布式 发布订阅模式 异步
上游系统对下游系统的调用若为同步调用,则会大大降低系统的吞吐量与并发度,且系统耦合度太高。
而异步调用则会解决这些问题。
所以两层之间若要实现由同步到异步的转化,一般性做法就是,在这两层间添加一个MQ层。

方法15:Redis 消息队列 分布式 发布订阅模式 异步
在springboot项目中,一般分布式 发布订阅模式 异步,都是用RocketMQ的方式,
如果集成太麻烦了,而一般系统里面已经有了redis,就用了redis做异步的功能
Redis发布订阅(pub/sub)是一种消息通信模式:
发送者(pub)发送消息,
订阅者(sub)接收消息。
Redis 发布订阅(pub/sub)实现了消息系统,发送者(在redis术语中称为发布者)在接收者(订阅者)接收消息时发送消息。传送消息的链路称为信道。

方法16: Distruptor 框架异步

Disruptor是一个优秀的并发框架,使用无锁编程+环形队列架构, 是高性能异步队列的王者组件
Disruptor 可以使用在 非常多 的生产者 消费者 异步场景

方法17:ForkJoin 框架异步

Fork/Join框架是JDK1.7提供的一个并行任务执行框架,它可以把一个大任务分成多个可并行执行的子任务,然后合并每个子任务的结果,得到的大任务的结果。
有点类似Hadoop的MapReduce,Fork/Join框架也可以分成两个核心操作:
Fork操作:将大任务分割成若干个可以并行执行的子任务
Join操作:合并子任务的执行结果

方法18:RocketMQ源码中ServiceThread 能急能缓的高性能异步

RocketMQ源码中, 实现了一种特殊的,高性能异步: 能急能缓 ServiceThread 异步。
能急能缓 ServiceThread 异步 有两个特点:
既能周期性的执行异步任务
还能紧急的时候,执行应急性的任务
RocketMQ的吞吐量达到 70Wqps,ServiceThread 的异步框架,发挥了重要的价值。
尼恩 在 实现《10WQps推送中台实操》使用了 ServiceThread 异步框架,在 实现《100WQps三级缓存组件实操》,实操时,也使用了 ServiceThread 异步框架。
总之, ServiceThread 异步框架是RocketMq的精华之一,性能非常高,也 非常好用。

方式19:Kotlin 协程 异步
2018 年,一种新的 JVM 编程语言开始流行:Kotlin。
Kotlin 首先流行在 Android 开发领域,因为它得到了 Google 的首肯和支持。
但对于后端开发领域,因为一项特性,使得 Kotlin 也非常值得注意。那就是 Kotlin Coroutine(后文称 Kotlin 协程)。
协程技术不是什么新技术,它在很多语言中都有实现,比如大家所熟悉的 Python、Lua、Go 都是支持协程的。
在不同语言中,协程的实现方法各有不同。因为 Kotlin 的运行依赖于 JVM,不能对 JVM 进行修改,因此,Kotlin 不能在底层支持协程。
同时,Kotlin 是一门编程语言,需要在语言层面支持协程,而不是像框架那样在语言层面之上支持。
因此,Kotlin 对协程支持最核心的部分是在编译器中。因为对这部分原理的解释在之前文章中都有涉及,因此不在这里重复。
使用 Kotlin 协程之后最大的好处是异步代码的可读性大大提高。

  • 22
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

思静语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值