认识Java异步编程

一 、认识异步编程

通常Java开发人员喜欢使用同步代码编写程序,因为这种请求(request)/响应(response)的方式比较简单,并且比较符合编程人员的思维习惯;这种做法很好,直到系统出现性能瓶颈;在同步编程方式时由于每个线程同时只能发起一个请求并同步等待返回,所以为了提高系统性能,此时我们就需要引入更多的线程来实现并行化处理;但是多线程下对共享资源进行访问时,不可避免会引入资源争用和并发问题;另外操作系统层面对线程的个数是有限制的,不可能通过无限的增加线程数来提供系统性能;最后使用同步阻塞的编程方式还会导致浪费资源,比如发起网络IO请求时候,调用线程就会处于同步阻塞等待响应结果的状态,而这时候调用线程明明可以去做其他事情,等网络IO响应结果返回后在对结果进行处理。

可见通过增加单机系统线程个数的并行编程方式并不是灵丹妙药;通过编写异步、非阻塞的代码,则可以使用相同的底层资源将执行切换到另一个活动任务,然后在异步处理完成后在返回到当前线程进行继续处理,从而提高系统性能。

异步编程是可以让程序并行运行的一种手段,其可以让程序中的一个工作单元与主应用程序线程分开独立运行,并且等工作单元运行结束后通知主应用程序线程它的运行结果或者失败原因。使用它有许多好处,例如可以提高应用程序的性能和响应能力。

比如当调用线程使用异步方式发起网络IO请求后,调用线程就不会同步阻塞等待响应结果,而是在内存保存请求上下文后,会马上返回后做其他事情,等网络IO响应结果返回后在使用IO线程通知业务线程响应结果已经返回,然后业务线程在对结果进行处理。可知异步调用方式提高了线程的利用率,让系统有更多的线程资源来处理更多的请求。

比如在移动应用程序中,在用户操作移动设备屏幕发起请求后,如果是同步等待后台服务器返回结果,则当后台服务操作非常耗时时,就会造成用户看到移动设备屏幕冻结(一直处理请求处理中),在结果返回前,用户不能操作移动设备的其他功能,这对用户体验非常不好。而使用异步编程则当发起请求后,调用线程会马上返回,具体返回结果则会通过UI线程异步进行渲染,而在这期间用户可以使用移动设备的其他功能。

二、 异步编程场景概述

在日常开发中我们经常会遇到这样的情况,就是需要异步的处理一些事情,而不需要知道异步任务的结果;比如在调用线程里面异步打日志,为了不让日志打印阻塞调用线程,会把日志设置为异步方式。如下图1-2-1日志异步化打印,使用一个内存队列把日志打印异步化,使用单一线程来消费队列里面日志事件执行具体的日志落盘操作(本质是一个多生产单消费模型),这种情况下调用线程把日志任务放入队列后就继续去干自己的事情了,而不再关心日志任务具体是什么时候入盘的;
image.png
图 1-2-1 日志异步打印

在Java中每当我们需要执行异步任务的时候我们可以直接开启一个线程来实现,也可以把异步任务封装为任务对象投递到线程池里面来执行,在Spring框架中则提供了@Async注解把一个任务异步化来进行处理,这些内容会在后面章节具体讲解。

另外有时候我们还需要在主线程等待异步任务的执行结果,这时候Future就排上用场了;比如调用线程要等执行任务A执行完毕后在顺序执行任务B,并且把两者结果拼接起来作为前端展示使用,如果调用线程是同步调用两次查询(如下图1-2-2同步调用),则整个过程耗时时间为执行任务A的耗时加上执行任务B的耗时。
image.png
图1-2-2 同步调用

如果使用异步编程(如下图1-2-3)则可以在调用线程内开启一个异步运行单元来执行任务A,开启异步运行单元后调用线程会马上返回一个Future对象(futureB),然后调用线程本身来执行任务B,等任务B执行完毕后,调用线程可以调用futureB的get()方法获取任务A的执行结果,最后在拼接两者结果。这时由于任务A和任务B是并行运行的,所以整个过程耗时为max(调用线程执行任务B耗时,异步运行单元执行任务A耗时)。

image.png
图1-2-3 异步调用

可见整个过程耗时有显著缩短,对于用户来说页面响应时间会更短,对用户体验会更好,其中异步单元的执行一般是线程池中的线程。

使用Future确实可以获取异步任务的执行结果,但是获取其结果还是会阻塞调用线程的,并没有实现完全异步化处理,在JDK8中提供了CompletableFuture来弥补了其缺点。CompletableFuture类允许以非阻塞方式和基于通知的方式处理结果,其通过设置回调函数方式,让主线程彻底解放出来,做自己的事情,实现了实际意义上的异步处理;

如下图1-2-4使用CompletableFuture时候当异步单元返回futureB后,调用线程可以在其上调用whenComplete方法设置一个回调函数action,然后调用线程就会马上返回了,等异步任务执行完毕后会使用异步线程来执行回调函数action,而无需调用线程干预,如果你对CompletableFuture不了解,没关系,后面章节我们会详细讲解,这里你只需要知道其解决了传统Future的缺陷就可以了。

image.png
图1-2-4 CompletableFuture异步执行

JDK8还引入了Stream,它旨在有效地处理数据流(包括原始类型),其使用声明式编程让我们可以写出可读性、可维护性很强的代码,并且结合CompletableFuture可以完美的实现异步编程。但是它产生的流只能使用一次,并且缺少与时间相关的操作(例如RxJava中的基于时间窗口的缓存元素),虽然可以执行并行计算,但无法指定要使用的线程池。并且它还没有设计用于处理延迟的操作(例如RxJava中的defer操作);而Reactor或RxJava等Reactive API就是为了解决这些问题而生的。

Reactor或RxJava等反应式API也提供Java 8 Stream的运算符,但它们更适用于任何流序列(不仅仅是集合),并允许定义一个转换操作的管道,该管道将应用于通过它的数据,这要归功于方便的流畅API和Lambda表达式的使用。Reactive旨在处理同步或异步操作,并允许您缓冲(buffer)、合并(merge)、连接(join) 元素等对元素做各种转换。

上面我们讲解了单JVM内的异步编程,那么对于跨网络的交互是否也存在异步编程范畴那?对于网络请求来说,同步调用时比较直截了当的,比如我们在一个线程A中通过RPC请求获取服务B和服务C的数据,然后基于两者结果做一些事情。在同步调用情况下,线程A需要调用服务B,然后需要同步等待服务B结果返回后,才可以对服务C发起调用,然后等服务C结果返回后才可以结合服务B和C的结果做一件事,如下图1-2-5:

image.png

图1-2-5 同步RPC调用

如上图1-2-5线程A同步获取服务B结果后,在同步调用服务C获取结果,可见在同步调用情况下业务执行语义比较清晰,线程A顺序的对多个服务请求进行调用;但是同步调用意味着当前发起请求的调用线程在远端机器返回结果前必须阻塞等待,这明显很浪费资源。好的做法应该是发起请求的调用线程发起请求后,注册一个回调函数,然后马上返回去做其他事情,当远端把结果返回后在使用IO线程执行回调函数。

那么如何实现异步调用?在Java中NIO的出现让实现上面的功能变得简单,而高性能异步、基于事件驱动的网络编程框架Netty的出现让我们从编写繁杂的Java NIO程序出解放出来了,现在的RPC框架比如Dubbo底层网络通信就是基于Netty实现的;Netty框架将网络编程逻辑与业务逻辑处理分离开来,其内部帮我们自动处理好网络与异步处理逻辑,让我们专心写自己的业务处理逻辑,Netty的异步非阻塞能力与CompletableFuture结合就可以轻松的实现网络请求的异步调用。

在执行RPC(远程过程调用)调用时候,使用异步编程可以提高系统的性能;如下图1-2-6,在异步调用情况下,当线程A调用服务B后,马上会返回一个异步的futureB对象,然后线程A可以在futureB上设置一个回调函数;然后线程A可以继续访问服务C,也会马上返回一个futureC对象,然后线程A可以在futureC上设置一个回调函数:
image.png
图1-2-6 RPC异步调用

如上图1-2-6可知异步调用情况下线程A可以并发的调用服务B和服务C,而不再是顺序的,由于服务B和服务C是并发运行,所以相比线程A同步调用,线程A获取到服务B和服务C结果的时间会缩短很多(同步调用情况下耗时时间为服务B和服务C返回结果耗时的和,异步调用时候耗时为max(服务B耗时,服务C耗时));另外这里可以借助CompletableFuture的能力等两次RPC调用都异步返回结果后做一件事情,这时候调用流程如下图图1-2-7:
image.png
图1-2-7 合并Rpc调用结果

如上图图1-2-7调用线程A首先发起服务B的远程调用,然后马上返回一个futureB对象,然后发起服务C的远程调用,然后马上返回一个futureC对象,最后调用线程A使用代码futureB.thenCombine(futureC,action)等futureB和futureC结果可用时候执行回调函数action;这里我们只是简单的概述下基于Netty的异步非阻塞能力以及CompletableFuture的可编排能力,我们可以实现功能很强大的异步编程能力,后面章节我们会以Dubbo框架为例讲解其借助Netty的非阻塞异步API实现了服务消费端的异步调用。

其实有了CompletableFuture实现异步编程,我们可以很自然的使用适配器来实现Reactive风格的编程,当我们使用RxJava API时候我们只需要使用Flowable的一些函数转换CompletableFuture为Flowable对象即可,这个我们在后面章节也会讲述。

上节讲解了网络请求中的RPC框架的异步请求,其实还有一类,也就是Web请求,在Web应用中Servlet占有一席之地。在Servlet3.0规范前,Servlet容器对Servlet的处理都是每个请求对应一个线程这种1:1的模式进行处理的(如下图1-2-8),每当来一个请求时候都会开启一个Servlet容器内的线程来进行处理,如果Servlet内处理比较耗时,则会把Servlet容器内线程使用耗尽,然后容器就不能再处理新的请求。
image.png
图1-2-8 Servlet的阻塞处理模型

Servlet3.0规范中则提供了异步处理的能力,让Servlet容器中的线程可以及时释放,具体Servlet业务处理逻辑是在业务自己线程池内来处理;虽然Servlet3.0规范让Servlet的执行变为了异步,但是其IO还是阻塞式的,IO阻塞是说在Servlet处理请求时候从ServletInputStream中读取请求体时候是阻塞的,而我们想要的是当数据已经就绪时候通知我们去读取就可以了,因为这可以避免占用我们自己的线程来进行阻塞读取,Servlet3.1规范则提供了非阻塞IO来解决这个问题。

虽然Servlet技术栈的不断发展实现了异步处理与非阻塞IO,但是其异步是不彻底的,因为受制于Servlet规范本身,比如其规范是同步的(Filter,Servlet)或阻塞(getParameter,getPart)。所以新的使用少量线程和较少的硬件资源来处理并发的非阻塞Web技术栈应运而生-WebFlux,其是与Servlet技术栈并行存在的一种新的技术,其基于JDK8函数式编程与Netty实现天然的异步、非阻塞处理,这些我们在后面章节会具体介绍。

另外为了更好的处理异步编程,降低我们异步编程的成本,一些框架也应运而生,比如高性能线程间消息传递库Disruptor,其通过为事件(events)预先分配内存、无锁CAS算法、缓冲行填充、两阶段协议提交来实现多线程并发的处理不同的元素,从而实现高性能的异步处理;比如Akka其基于Actor模式实现了天然支持分布式的使用消息进行异步处理的服务;比如高性能分布式消息中间件Apache RocketMetaQ用来实现应用间的异步解耦、流量削峰。

一些新兴的语言对异步处理的支持能力让我们忍不住称赞,GoLang就是其中之一,其通过语言层面内置的goroutine与channel可以轻松的实现复杂的异步处理能力。

《Java异步编程实战》),一书则是根据上述介绍的次序,把内容划分了若干章节,每章则具体展开讨论相应的异步编程技术。

三、 为何写作本书

异步编程是可以让程序并行运行的一种手段,其可以让程序中的一个工作单元与主应用程序线程分开独立运行,使用它有许多好处,例如可以提高应用程序的性能和响应能力。

虽然Java中不同技术域提供了相应的异步编程技术,但是对异步编程技术的描述散落到了不同技术域的技术文档中,并没有一个统一的地方对这些技术进行梳理归纳。另外这些技术之间是什么关系,各自的出现都是为了解决什么问题,我们也很难找到资料来解释。

本书的出现则是为了打破这种局面,本书旨在把Java中相关的异步编程技术进行归纳分类总结,然后呈现给大家,让大家可以有一个统一的地方来查看与探究。

四、本书特色

本书涵盖了Java中常见的异步编程场景,这包含单JVM内的异步编程、以及跨主机通过网络通讯的远程过程调用的异步调用与异步处理、以及Web请求的异步处理等等。

本书在讲解Java中每种异步编程技术时都附有案例,以便理论与实践进行结合。

本书在讲解每种异步编程技术时大多都会对其实现原理进行讲解,以便让读者知其然也知其所以然。

本书对最近比较火的反应式编程以及WebFlux的使用与原理解析有一定深入的探索。

五、 业界推荐


原文链接
本文为阿里云原创内容,未经允许不得转载。

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

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

相关文章

云原生应用万节点分钟级分发协同实践

导读:2019 年天猫 双11,阿里巴巴核心系统首次实现 100% 上云。面对全球最大的交易洪峰,阿里云扛住了每秒 54.4 万笔的交易峰值,这是“云原生”与“天猫全球狂欢节”的一次完美联名。 (图为 2019 年天猫 双11 成交额&am…

为啥Underlay才是容器网络的最佳落地选择

导语: 几年前,当博云启动自研容器网络研发的时候,除了技术选型的考虑,我们对于先做 Underlay 还是 Overlay 网络也有过深度的讨论。当时的开源社区以及主流容器厂商,多数还是以 Overlay 网络方案为主,但在我…

oracle用户编辑文件中文乱码

文章目录一、分析定位1. 现象2. 查看当前编码3. 配置文件现状二、解决方案2.1. 编辑配置文件2.2. 调整2.3. 刷新环境变量2.4. 效果图2.5. 配置文件源码一、分析定位 1. 现象 2. 查看当前编码 登录oracle用户,查看当前oracle用户下的编码 [oraclelocalhost ~]$ ec…

今年圣诞,麋鹿第一次请假

年末的圣诞节来啦~~! 但我们的圣诞老人为什么有点低落?? 是在愁信太多?没地儿放? 不会是看不懂我信上的“鬼画符”吧.. 难道是在忌惮空中的雾霾?哈哈~ 圣诞老人:o(╥﹏╥)o 不不不&#xf…

高德网络定位算法的演进

1.导读 GPS定位精度高,且早已成为移动设备标配,但GPS也具有一些难以克服的缺陷,包括: 冷启动时间长。GPS启动时,需要进行搜星,锁定卫星信号,然后再进行位置技术,这个过程可能会达到…

疫情之下网络安全如何保障?Akamai防护方案前来“保驾护航”

2020年春天,以5G、人工智能、云计算为代表的“新基建”蔚然成风,着眼国家数字经济体系建设,打造数字经济体系底座的“新基建”,无疑成为中国经济整体应对未来发展的核心方案。然而,面临“新基建”与企业数字化进程的突…

2020将至,谈谈中小企业创业的那些事儿

2019正迎来尾声,在创业这场轰轰烈烈的旅途中,有的人名利双收,有的人饱受非议。一入创业深似海,在创业赛道上,很多公司完成了从0到1,却死在从1到100的路上。而小程序的出现,让移动互联网创业走向…

axios从安装到使用的教程

安装axios: npm install --save axios 目录结构: 红框部分是接口文件: appApi.js是存放接口的文件 import Vue from vue import axios from axiosexport default {// 获取分类show_category: function () {return axios.post(/point-api-show_category);},// 获…

嘘,这是手淘双11 GMV 暴涨的秘密

阿里妹导读:信息流作为手淘的一大流量入口,对手淘的浏览效率转化和流量分发起到至关重要的作用。在探索如何给用户推荐其喜欢的商品这条路上,我们首次将端计算大规模应用在手淘客户端,通过端侧丰富的用户特征数据和触发点&#xf…

vuex从安装到使用的教程

vuex的安装 npm install vuex --save或cnpm install vuex --save main.js引入vuex import Vue from vue import Vuex from vuex import store from ./vuex/storeVue.use(Vuex) vuex的目录结构和store.js的代码 store.js的代码(异步和同步的代码均留下两个方法&am…

未来,仅凭几个前端工程师,就能 hold 住一家企业吗?

阿里妹导读:微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增加,从一个普通应用演变成一个巨石应用(Frontend Monolith),随之而来的应用不可维护问题。这类问题在企业级 Web 应用中尤为常见。今天&a…

yarn : 无法加载文件 C:\Users\Administrator\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本

文章目录1. 现象2. 授权安全策略3. 重新执行命令1. 现象 2. 授权安全策略 若要在本地计算机上运行您编写的未签名脚本和来自其他用户的签名脚本,请使用以下命令将计算机上的 执行策略更改为 RemoteSigned 执行: set-ExecutionPolicy RemoteSigned查看执…

央视315曝光SDK事件,应用开发者如何避坑?

7月16日晚,央视315晚会拉开大幕,再次敲响了消费领域的警钟。据央视报道,上海市消费者权益保护委员会委托第三方对市场上的App进行检测,发现某些第三方开发的SDK包存在违规收集用户个人信息的情况。日前,工信部已要求严…

VS Code 切换大小写

默认是空的,我设置的快捷键是 shift altd 转换为大写 shift altx 转换为小写

携程实时智能检测平台建设实践

本次演讲将为大家介绍携程实时智能异常检测平台——Prophet。到目前为止,Prophet基本覆盖了携程所有业务线,监控指标的数量达到10K,覆盖了携程所有订单、支付等重要的业务指标。Prophet将时间序列的数据作为数据输入,以监控平台作…

VS Code 设置好看的字体:Operator Mono

文章目录一、字体资源地址1. 链接2. 资源下载二、效果图2.1. JS 代码效果2.2. CSS 文件效果2.3. HTML 文件效果三、安装字体3.1. 字体列表3.3. 安装方式3.3. VSCode 配置一、字体资源地址 1. 链接 FiraCode 和 Operator Mono 字体下载地址 2. 资源下载 Git下载 git clone …

钟南山团队携手腾讯研发新冠重症AI预测 成果登上Nature子刊

钟南山院士团队与腾讯AI Lab日前披露了利用AI预测COVID-19患者病情发展至危重概率的研究成果,可分别预测5天、10天和30天内病情危重的概率,有助合理地为病人进行早期分诊。这项研究已在2020年7月15日发布于国际顶级期刊《Nature》子刊《Nature Communica…

CentOs搭建svn

安装SVN yum install -y subversion检查是否安装成功 svnserve --version创建版本库 我们先创建/var/svn这么目录 mkdir /var/svn cd /var/svn创建版本库 svnadmin create /var/svn/project后边的project就是我们项目的版本库 cd project ls 会看到自动生成的版本库文件…

互联网全域降维攻击战略概述

前言: 在互联网维度体系概念被提出之后,社会上对于降维的理解向不同的方向发展,如很多人认为降维呈现的是有高难度业务实施能力的企业向低难度领域业务进行渗透和发展的方式,还有的认为降维就是互联网行业对于传统行业的业务冲击…

当达摩院大牛学会抠图,这一切都不受控制了……

在外界人眼中,达摩院人才济济,大多是奇人异士,做着神秘且高端的研究,有如扫地僧一般的存在,但是如果有一天,当神秘专家不再神秘,你发现他们也开始玩抠图,且这一切都朝着不受控制的方…