中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中怎么實現異步非阻塞編程

發布時間:2021-07-02 14:10:56 來源:億速云 閱讀:361 作者:Leah 欄目:云計算

本篇文章給大家分享的是有關Java中怎么實現異步非阻塞編程,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

一 從一個同步的Http調用說起

一個很簡單的業務邏輯,其他后端服務提供了一個接口,我們需要通過接口調用,獲取到響應的數據。

逆地理接口:通過經緯度獲取這個經緯度所在的省市區縣以及響應的code:

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

服務端執行,最簡單的同步調用方式:

Java中怎么實現異步非阻塞編程

服務端響應之前,IO會阻塞在:
java.net.SocketInputStream#socketRead0 的native方法上:

Java中怎么實現異步非阻塞編程

通過jstack日志,可以發現,此時這個Thread會一直在runable的狀態:

"main"#1 prio=5 os_prio=31 tid=0x00007fed0c810000 nid=0x1003 runnable [0x000070000ce14000]   java.lang.Thread.State: RUNNABLE
        at 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)
                .......

線程模型示例:

Java中怎么實現異步非阻塞編程

同步最大的問題是在IO等待的過程中,線程資源沒有得到充分的利用,對于大量IO場景的業務吞吐量會有一定限制。

二 JDK NIO & Future

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

Java中怎么實現異步非阻塞編程

Java中怎么實現異步非阻塞編程

當然并不是所有的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: RUNNABLE
        at 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)

Java中怎么實現異步非阻塞編程

主線程在等待結果返回過程中依然需要等待,沒有根本解決此問題。

三 使用Callback回調方式

第二節中,依然需要主線程等待,獲取結果,那么可不可以在主線程完成發送請求后,再也不用關心這個邏輯,去執行其他的邏輯?那就可以使用Callback機制。

Java中怎么實現異步非阻塞編程

如此一來,主線程再也不需要關心發起IO后的業務邏輯,發送完請求后,就可以徹底去干其他事情,或者回到線程池中再供調度。如果是HttpServer,那么需要結合Servlet 3.1的異步Servlet。

Java中怎么實現異步非阻塞編程

Java中怎么實現異步非阻塞編程

異步Servelt參考資料

https://www.cnblogs.com/davenkin/p/async-servlet.html

使用Callback方式,從線程模型中看,發現線程資源已經得到了比較充分的利用,整個過程中已經沒有線程阻塞。

四 Callback hell

回調地獄,當Callback的線程還需要執行下一個IO調用的時候,這個時候進入回調地獄模式。

典型的應用場景如,通過經緯度獲取行政區域adcode(逆地理接口),然后再根據獲得的adcode,獲取當地的天氣信息(天氣接口)。

在同步的編程模型中,幾乎不會涉及到此類問題。

Java中怎么實現異步非阻塞編程

Callback方式的核心缺陷

五 JDK 1.8 CompletableFuture

那么有沒有辦法解決Callback Hell的問題?當然有,JDK 1.8中提供了CompletableFuture,先看看它是怎么解決這個問題的。

將逆地理的Callback邏輯,封裝成一個獨立的CompletableFuture,當異步線程回調時,調用 future.complete(T) ,將結果封裝。

Java中怎么實現異步非阻塞編程

將天氣執行的Call邏輯,也封裝成為一個獨立的CompletableFuture ,完成之后,邏輯同上。

Java中怎么實現異步非阻塞編程

compose銜接,whenComplete輸出:

Java中怎么實現異步非阻塞編程

每一個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作為參數執行接下來的邏輯。

Java中怎么實現異步非阻塞編程

這樣一來,就完美解決回調地獄問題,在主的邏輯中,看起來像是在同步的進行編碼。

六 Vert.x Future

Info-Service中,大量使用的 Vert.x Future 也是類似的解決的方案,不過設計上使用Handler的概念。

Java中怎么實現異步非阻塞編程

核心執行的邏輯差不多:

Java中怎么實現異步非阻塞編程

這當然不是Vertx的全部,當然這是題外話了。

七 Reactive Streams

異步編程對吞吐量以及資源有好處,但是有沒有統一的抽象去解決此類問題內,答案是 Reactive Streams。

核心抽象:Publisher Subscriber Processor Subscription ,整個包里面,只有這四個接口,沒有實現類。

Java中怎么實現異步非阻塞編程

在JDK 9里面,已經被作為一種規范封裝到 java.util.concurrent.Flow :

Java中怎么實現異步非阻塞編程

Java中怎么實現異步非阻塞編程

參考資料

https://www.baeldung.com/java-9-reactive-streams

http://ypk1226.com/2019/07/01/reactive/reactive-streams/

https://www.reactivemanifesto.org/

https://projectreactor.io/learn

一個簡單的例子:

Java中怎么實現異步非阻塞編程

八 Reactor & Spring 5 & Spring WebFlux

Flux & Mono

Java中怎么實現異步非阻塞編程

Java中怎么實現異步非阻塞編程

以上就是Java中怎么實現異步非阻塞編程,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

安远县| 扬州市| 林西县| 大埔区| 宕昌县| 比如县| 彭州市| 大渡口区| 荔波县| 安顺市| 原阳县| 姜堰市| 乐亭县| 龙门县| 海城市| 衡南县| 阿拉尔市| 平远县| 蓝田县| 同江市| 临高县| 天气| 东明县| 华容县| 巍山| 东乌珠穆沁旗| 平阴县| 山东省| 青田县| 封丘县| 洮南市| 上饶县| 乐亭县| 碌曲县| 通城县| 卢氏县| 鄂托克前旗| 福清市| 交城县| 井研县| 榆树市|