当前位置: 首页 > Web前端 > HTML

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

时间:2023-04-02 18:32:01 HTML

介绍:Java异步非阻塞编程的几种方式1.从一个同步的Http调用到一个很简单的业务逻辑,其他后端服务提供接口,我们需要调用接口获取响应数据。逆向地理接口:获取经纬度所在省、市、市、县及对应编码:curl-i"http://xxx?latitude=31.08966221524924&channel=amap7a&near=false&longitude=105.13990312814713"{"adcode":"510722"}服务器执行,最简单的同步调用方式:在服务器响应之前,IO会阻塞在:java.net.SocketInputStream#socketRead0的native方法上:通过jstack日志可以发现,此时线程会一直处于可运行状态:"main"#1prio=5os_prio=31tid=0x00007fed0c810000nid=0x1003runnable[0x000070000ce14000]java.lang.Thread.State:RUNNABLEatjava.net.SocketInputStream.socketRead0(本机方法)在java.net.SocketsocketRead(SocketInputStream.java:116)在java.net.SocketInputStream.read(SocketInputStream.java:171)在java.net.SocketInputStream.read(SocketInputStream.java:141)在org.apache.http.impl.conn.LoggingInputStream.read(LoggingInputStream.java:84)在org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:137)在org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionImplnputBufferImpl.java:153)在org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:282)在org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:138)在org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)在org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)在org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163)在org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:165)在org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java)在:27org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)在org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272)在org.apache.http.impl。execchain.ProtocolExec.execute(ProtocolExec.java:185)在org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)在org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)在org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)在org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)在org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)atcom.amap.aos.async.AsyncIO.blockingIO(AsyncIO.java:207)......线程模型示例:synchronousmax问题就是在IO等待的过程中,线程资源没有得到充分利用,对大量IO场景的业务吞吐量会有一定的限制。2.JDKNIO&Future在JDK1.5中,JUC提供了Future抽象:当然不是所有的Future都是这样实现的,比如io.netty.util.concurrent.AbstractFuture就是线程轮询的。这样做的好处是主线程不用等待IO响应,而是可以做别的事情,比如再发送一个IO请求,可以一直等到它一起返回:"main"#1prio=5os_prio=31tid=0x00007fd7a500b000nid=0xe03等待条件[0x000070000a95d000]java.lang.Thread.State:等待(停放)在sun.misc.Unsafe.park(本机方法)-停放等待<0x000000076ee2d768>(java.util.concurrent.CountDownLatch$Syn)在java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)在java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)在java.util。concurrent.locks.AbstractQueuedSynchronizer。java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)在java.util.concurrent.CountDownLatch.await(CountDownLatch.net.javaat:2.NettyResponseFuture.get(NettyResponseFuture.java:162)在com.amap.aos.async.AsyncIO.futureBlockingGet(AsyncIO.java:201)....."AsyncHttpClient-2-1"#11prio=5os_prio=31tid=0x00007fd7a7247800nid=0x340b可运行[0x000070000ba94000]java.lang.Thread.State:在sun.nio.ch.KQueueArrayWrapper.kevent0(本机方法)在sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:198)在sun.nio.ch.KQueueSelectorImpl。doSelect(KQueueSelectorImpl.java:117)atsun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)-锁定<0x000000076eb00ef0>(一个io.netty.channel.nio.SelectedSelectionKeySet)-锁定<0x000000076eb00f10>(一个java.util.Collections$UnmodifiableSet)-在io.netty.channel.nio.NioEventLoop的sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)处锁定<0x000000076eb00ea0>(一个sun.nio.ch.KQueueSelectorImpl)。选择(NioEventLoop.java:693)在io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:353)在io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:140)在io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)在java.lang.Thread.run(Thread.java:748)主线程在等待结果返回的时候还需要等待,这个问题并没有从根本上解决。发送请求后,就不用再关心这个逻辑去执行其他逻辑了?然后就可以使用回调机制了。这样主线程发起IO后就不再需要关心业务逻辑了。发送请求后,完全可以做其他事情,或者返回线程池进行调度。如果是HttpServer,需要结合Servlet3.1的异步Servlet。使用Callback方法,从线程模型上发现线程资源已经被充分利用,整个过程中没有线程阻塞。4.回调地狱回调地狱,当Callback线程还需要执行下一次IO调用时,此时就会进入回调地狱模式。一个典型的应用场景是通过经纬度(反向地理接口)获取行政区域的adcode,然后根据获取的adcode获取当地的天气信息(天气接口)。在同步编程模型中,很少涉及此类问题。Callback方法的核心缺陷5.JDK1.8CompletableFuture那么有什么办法可以解决CallbackHell的问题呢?当然,CompletableFuture是在JDK1.8中提供的。让我们看看它是如何解决这个问题的。将逆向地理的Callback逻辑封装成一个独立的CompletableFuture,异步线程回调时调用future.complete(T)封装结果。天气执行的Call逻辑也被封装到一个独立的CompletableFuture中。完成后,逻辑同上。Compose连接,完成时输出:每个IO操作都可以封装成一个独立的CompletableFuture,从而避免回调地狱。CompletableFuture只有两个属性:result:Future的执行结果(EithertheresultorboxedAltResult)。stack:操作栈,用来定义这个Future的下一个操作的行为(TopofTreiberstackofdependentactions)。如何调用weatherFuture方法?通过栈可以发现是在reverseCodeFuture.complete(result)的时候,获取到的adcode也作为参数执行接下来的逻辑。这样就完美解决了回调地狱的问题。在主逻辑中,看起来是在同步编码。6、在Vert.xFutureInfo-Service中,广泛使用的Vert.xFuture也是类似的方案,只是在设计上使用了Handler的概念。core实现的逻辑类似:这当然不是Vertx的全部,当然这是题外话。7.ReactiveStreams异步编程对吞吐量和资源都有好处,但是有没有一个统一的抽象来解决这样的问题呢?答案是ReactiveStreams。核心抽象:PublisherSubscriberProcessorSubscription,在整个包中,只有这四个接口,并没有实现类。在JDK9中已经封装成java.util.concurrent.Flow作为规范:一个简单的例子:8.Reactor&Spring5&SpringWebFluxFlux&Mono作者:DeveloperAssistant_LS原文链接本文为阿里云原创内容,可能未经许可不得转载

最新推荐
猜你喜欢