Java异步非阻塞编程的几种方式

简介: Java异步非阻塞编程的几种方式

 

一、 从一个同步的Http调用说起

一个很简单的业务逻辑,其他后端服务提供了一个接口,我们需要通过接口调用,获取到响应的数据。

逆地理接口:通过经纬度获取这个经纬度所在的省市区县以及响应的code:

curl-i"http://xxx?latitude=31.08966221524924&channel=amap7a&near=false&longitude=105.13990312814713"
{"adcode":"510722"}

服务端执行,最简单的同步调用方式:

 

服务端响应之前,IO会阻塞在:
java.net.SocketInputStream#socketRead0 的native方法上:

 

通过jstack日志,可以发现,此时这个Thread会一直在runable的状态:

"main"#1 prio=5 os_prio=31 tid=0x00007fed0c810000 nid=0x1003 runnable [0x000070000ce14000]   java.lang.Thread.State: RUNNABLEat java.net.SocketInputStream.socketRead0(Native Method)at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)at java.net.SocketInputStream.read(SocketInputStream.java:171)at java.net.SocketInputStream.read(SocketInputStream.java:141)at org.apache.http.impl.conn.LoggingInputStream.read(LoggingInputStream.java:84)at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:137)at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:153)at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:282)at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:138)at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163)at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:165)at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:273)at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272)at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)at com.amap.aos.async.AsyncIO.blockingIO(AsyncIO.java:207).......

线程模型示例:

 

同步最大的问题是在IO等待的过程中,线程资源没有得到充分的利用,对于大量IO场景的业务吞吐量会有一定限制。

二 、JDK NIO & Future

在JDK 1.5 中,JUC提供了Future抽象:

 

 

当然并不是所有的Future都是这样实现的,如
io.netty.util.concurrent.AbstractFuture 就是通过线程轮询去。

这样做的好处是,主线程可以不用等待IO响应,可以去做点其他的,比如说再发送一个IO请求,可以等到一起返回:

"main"#1 prio=5 os_prio=31 tid=0x00007fd7a500b000 nid=0xe03 waiting on condition [0x000070000a95d000]   java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)
- parking to wait for  <0x000000076ee2d768> (a java.util.concurrent.CountDownLatch$Sync)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:997)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:231)at org.asynchttpclient.netty.NettyResponseFuture.get(NettyResponseFuture.java:162)at com.amap.aos.async.AsyncIO.futureBlockingGet(AsyncIO.java:201).....
"AsyncHttpClient-2-1"#11 prio=5 os_prio=31 tid=0x00007fd7a7247800 nid=0x340b runnable [0x000070000ba94000]   java.lang.Thread.State: RUNNABLEat sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)at sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:198)at sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:117)at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
- locked <0x000000076eb00ef0> (a io.netty.channel.nio.SelectedSelectionKeySet)
- locked <0x000000076eb00f10> (a java.util.Collections$UnmodifiableSet)
- locked <0x000000076eb00ea0> (a sun.nio.ch.KQueueSelectorImpl)at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:693)at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:353)at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:140)at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)at java.lang.Thread.run(Thread.java:748)

 

主线程在等待结果返回过程中依然需要等待,没有根本解决此问题。

三 、使用Callback回调方式

第二节中,依然需要主线程等待,获取结果,那么可不可以在主线程完成发送请求后,再也不用关心这个逻辑,去执行其他的逻辑?那就可以使用Callback机制。

 

如此一来,主线程再也不需要关心发起IO后的业务逻辑,发送完请求后,就可以彻底去干其他事情,或者回到线程池中再供调度。如果是HttpServer,那么需要结合Servlet 3.1的异步Servlet。

 

 

使用Callback方式,从线程模型中看,发现线程资源已经得到了比较充分的利用,整个过程中已经没有线程阻塞。

四、 Callback hell

回调地狱,当Callback的线程还需要执行下一个IO调用的时候,这个时候进入回调地狱模式。

典型的应用场景如,通过经纬度获取行政区域adcode(逆地理接口),然后再根据获得的adcode,获取当地的天气信息(天气接口)。

在同步的编程模型中,几乎不会涉及到此类问题。

 

Callback方式的核心缺陷

五、 JDK 1.8 CompletableFuture

那么有没有办法解决Callback Hell的问题?当然有,JDK 1.8中提供了CompletableFuture,先看看它是怎么解决这个问题的。

将逆地理的Callback逻辑,封装成一个独立的CompletableFuture,当异步线程回调时,调用 future.complete(T) ,将结果封装。

 

将天气执行的Call逻辑,也封装成为一个独立的CompletableFuture ,完成之后,逻辑同上。

 

compose衔接,whenComplete输出:

 

每一个IO操作,均可以封装为独立的CompletableFuture,从而避免回调地狱。

CompletableFuture,只有两个属性:

  • result:Future的执行结果 (Either the result or boxed AltResult)。
  • stack:操作栈,用于定义这个Future接下来操作的行为 (Top of Treiber stack of dependent actions)。

weatherFuture这个方法是如何被调用的呢?

通过堆栈可以发现,是在
reverseCodeFuture.complete(result) 的时候,并且也将获得的adcode作为参数执行接下来的逻辑。

 

这样一来,就完美解决回调地狱问题,在主的逻辑中,看起来像是在同步的进行编码。

六、 Vert.x Future

Info-Service中,大量使用的 Vert.x Future 也是类似的解决的方案,不过设计上使用Handler的概念。

 

核心执行的逻辑差不多:

 

这当然不是Vertx的全部,当然这是题外话了。

七 、Reactive Streams

异步编程对吞吐量以及资源有好处,但是有没有统一的抽象去解决此类问题内,答案是 Reactive Streams。

核心抽象:Publisher Subscriber Processor Subscription ,整个包里面,只有这四个接口,没有实现类。

 


在JDK 9里面,已经被作为一种规范封装到 java.util.concurrent.Flow :

 

 

一个简单的例子:

 

八、 Reactor & Spring 5 & Spring WebFlux

Flux & Mono

 

 

作者:开发者小助手_LS

原文链接

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

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

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

相关文章

张一鸣 90 亿购得元宇宙入场券,谁将是头号玩家?

整理 | 禾木木 出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09; VR 技术不断发展&#xff0c;虚拟网络世界“元宇宙”的概念也愈加火热&#xff0c;VR 已经不再局限于游戏与影视&#xff0c;它将作为降本增效的工具服务于更多行业领域。 8月29日&#xf…

前端开发:如何正确地跨端?

简介&#xff1a; 面对多种多样的跨端诉求&#xff0c;有哪些跨端方案&#xff1f;跨端的本质是什么&#xff1f;作为业务技术开发者&#xff0c;应该怎么做&#xff1f;本文分享阿里巴巴ICBU技术部在跨端开发上的一些思考&#xff0c;介绍了当前主流的跨端方案&#xff0c;以及…

mysql 创建表check如何使用_MySQL怎么使用check约束

在数据库中&#xff0c;CHECK 约束是指约束表中某一个或者某些列中可接受的数据值或者数据格式(用于限制列中的值的范围)。在一些情况下&#xff0c;我们需要字段在指定范围的输入&#xff0c;例如&#xff1a;性别只能输入 男或者女&#xff0c;余额只能大于0等条件&#xff0…

2020年,这个算法团队都干了啥?

简介&#xff1a; 什么是算法&#xff1f;什么是广告算法工程师&#xff1f;算法工程师又是如何定义的&#xff1f;今天作者将就算法、电商算法为主题和我们分享他的理解&#xff0c;同时还将和我们分享ICBU算法团队的整体工作和2020年的一些重要技术突破。 写在最前 我个人有…

为了让你在“口袋奇兵”聊遍全球,Serverless 做了什么?

简介&#xff1a; 江娱互动是一家新兴的游戏企业&#xff0c;自 2018 年成立伊始&#xff0c;江娱互动就面向广阔的全球游戏市场&#xff0c;通过创造有趣的游戏体验&#xff0c;在竞争激烈的游戏市场占得一席之地。仅仅 2 年的时间&#xff0c;江娱互动就凭借 Topwar&#xff…

mysql批量插入 增加参数_MySql 的批量操作,要加rewriteBatchedStatements参数

MySql 的批量操作&#xff0c;要加rewriteBatchedStatements参数作者&#xff1a;赵磊博客&#xff1a;http://elf8848.iteye.com--------------------------------结论 ---------------------------------MySql 非批量 10万条记录&#xff0c; 5700条/秒MyS…

为开发者而生 | 2021 SuperMap开发者大会议程全公布

如果地理信息产业是一片江湖 SuperMap开发者大会 则是一场卧虎藏龙的群英会 技术卓越的大侠们在此一展风采 精通各路应用的绝世门派在此切磋技艺 一起修炼顶级武功秘籍 致敬技术极客精神 为开发者而生&#xff08;D4D&#xff09; 9月15日-18日 2021 SuperMap开发者大会…

Flink 如何实时分析 Iceberg 数据湖的 CDC 数据

简介&#xff1a; 数据湖的架构中&#xff0c;CDC 数据实时读写的方案和原理 本文由李劲松、胡争分享&#xff0c;社区志愿者杨伟海、李培殿整理。主要介绍在数据湖的架构中&#xff0c;CDC 数据实时读写的方案和原理。文章主要分为 4 个部分内容&#xff1a; 常见的 CDC 分析…

mysql 加载数据校验_mysql 导入数据后的校验程序

参考mysql导入样本数据库employees之后的数据校验&#xff0c;可以使用md5或者sha&#xff0c;原理与思路&#xff1a;首先在将要备份的数据库中生成每个表里的每行每列数据的累加计算md5值&#xff0c;接着hardcode在测试单元文件中&#xff0c;作为期望值。以下是md5的校验方…

如何通过事务消息保障抢购业务的分布式一致性?

简介&#xff1a; 在柔性事务的多种实现中&#xff0c;事务消息是最为优雅易用的一种。基于阿里云RocketMQ高性能、高可用的特点&#xff0c;完全可以胜任抢购业务这类高并发大流量的场景。但引入事务消息机制在实现高性能的同时&#xff0c;也增加了整体的业务复杂度。我们需要…

海量秋招面试资料等你来拿!你离大厂也许并不远

秋招在即&#xff0c;你还在为秋招如何准备而发愁吗&#xff1f;你还在为拿不到大厂offer而苦恼吗&#xff1f;工欲善其事&#xff0c;必先利其器。金秋开学季&#xff0c;CSDN助力你的技术学习与成长&#xff0c;为你免费提供海量大厂面试资料&#xff0c;让你的秋招不再慌乱&…

基于Ganos百行代码实现亿级矢量空间数据在线可视化

简介&#xff1a; 本文介绍如何使用RDS PG或PolarDB&#xff08;兼容PG版或Oracle版&#xff09;的Ganos时空引擎提供的数据库快显技术&#xff0c;仅用百行代码实现亿级海量几何空间数据的在线快速显示和流畅地图交互&#xff0c;且无需关注切片存储和效率问题。 01 引言 如何…

流批一体生产应用!Bigo 实时计算平台建设实践

简介&#xff1a; 本文由 Bigo 计算平台负责人徐帅分享&#xff0c;主要介绍 Bigo 实时计算平台建设实践的介绍 本文由 Bigo 计算平台负责人徐帅分享&#xff0c;主要介绍 Bigo 实时计算平台建设实践的介绍。内容包括&#xff1a; Bigo 实时计算平台的发展历程特色与改进业务场…

一部手机是否能用 7 年?苹果、三星、Google:三年差不多!

整理 | 苏宓出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;一部手机如果可以流畅地使用 7 年&#xff0c;是种什么样的感觉&#xff1a;有人说&#xff0c;这对于 iPhone 而言&#xff0c;或许会很轻松做到&#xff0c;但也会给一些平价的 Android 手机制造商带来…

五福背后的 Web 3D 引擎 Oasis Engine 正式开源

简介&#xff1a; Oasis 从开源走向新的起点&#xff0c;用 3D 化的交互和表达让世界变得更美好。 相信大家已经体验了今年支付宝五福的活动&#xff0c;无论是今年的五福首页还是打年兽游戏都是由蚂蚁互动图形引擎&#xff08;代号&#xff1a;Oasis Engine&#xff09;驱动的…

我用 Python 自制成语接龙小游戏,刺激!

作者&#xff1a;小小明原文链接&#xff1a;https://blog.csdn.net/as604049322/article/details/118154687本文为读者投稿在 https://github.com/pwxcoo/chinese-xinhua 项目中可以下载到中华成语的语料库&#xff0c;该项目收录包括 14032 条歇后语&#xff0c;16142 个汉字…

基于SLS构建RDS审计合规监控

简介&#xff1a; 数据库是企业业务的数据核心&#xff0c;其安全方面的问题在传统环境中已经成为泄漏和被篡改的重要根源。因此&#xff0c;对数据库的操作行为尤其是全量 SQL 执行记录的审计日志&#xff0c;就显得尤为重要。 背景 数据库是企业业务的数据核心&#xff0c;其…

云效DevOps实践-如何基于云效实现测试自动化集成和分析

简介&#xff1a; 对于现代软件研发来说&#xff0c;持续、快速、高质量、低风险地交付需求特性&#xff0c;是业务对研发的主要诉求。而要做到这一点&#xff0c;除了要有良好的架构设计、卓越的工程能力&#xff0c;快速可靠的测试反馈也是其非常重要的一环&#xff0c;达到这…

spring 使用其他类protected方法_Java操作bean、属性、方法的使用工具类

在实际的项目开发中&#xff0c;反射操作类的实例、属性赋值、执行方法是常规的操作&#xff0c;虽然spring提供了比较完整的API来执行上述操作&#xff0c;不过在实际的应用中&#xff0c;spring的函数隐藏比较深&#xff0c;比较分散&#xff0c;小伙伴们可能懒得花时间去寻找…

2021年阿里云采购季大促主会场全攻略

在疫情的影响下&#xff0c;企业都在谋求各种转机&#xff0c;探寻各种转型之路&#xff0c;为助力企业复工复产低成本上云&#xff0c;日前阿里云开年采购季优惠活动于3月1日正式开启。 从主会场页面来看&#xff0c;活动分为三个阶段&#xff1a; 3月1日-3月16日&#xff1a…