日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
在Java中使用異步編程

 

創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),東源企業(yè)網(wǎng)站建設(shè),東源品牌網(wǎng)站建設(shè),網(wǎng)站定制,東源網(wǎng)站建設(shè)報價,網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,東源網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學(xué)習、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。

[[388836]]

本文轉(zhuǎn)載自微信公眾號「編了個程」,作者Yasin x。轉(zhuǎn)載本文請聯(lián)系編了個程公眾號。  

最近在學(xué)習協(xié)程,打算輸出幾篇文章來介紹一下協(xié)程。而協(xié)程與異步有很大的關(guān)聯(lián),所以想先介紹一下異步。

異步是一種程序的運行方式,各種編程語言語言或多或少都對它有所支持。異步對于Java后端程序員來說并不是一種特別熟悉的概念,而安卓或者前端的同學(xué)可能會對異步這個概念會更熟悉一些。

程序同步和異步

同步是最簡單也是最符合我們?nèi)祟愃季S方式的編程方式,所謂同步,就是程序會按照代碼一行行執(zhí)行,執(zhí)行完一句再執(zhí)行下一句。

同步代碼看起來是這樣:

 
 
 
 
  1. stepA(); 
  2. stepB(); 
  3. stepC(); 
  4. ... 

stepA執(zhí)行完后,開始執(zhí)行stepB,stepB執(zhí)行完后,執(zhí)行stepC。

而有時候我們會有這樣的需求:在后臺執(zhí)行一段程序。具體到我們這個案例來說,就是執(zhí)行完stepA后,要開始執(zhí)行stepB,但不用等stepB執(zhí)行完,現(xiàn)在可以立即執(zhí)行stepC。

于是異步編程就出來了。在Java語言里,我們可以創(chuàng)建一個新的線程(或者使用線程池)去執(zhí)行異步任務(wù):

 
 
 
 
  1. stepA(); 
  2. new Thread(() -> stepB()).start(); 
  3. stepC(); 

這樣,stepB就在另一個線程里面“異步”執(zhí)行了,而stepC還是繼續(xù)在當前線程里執(zhí)行。

異步有什么好處呢?

有一個顯而易見的好處:讓程序“響應(yīng)更快”。比如上述的case,如果stepB()任務(wù)比較耗時,比如發(fā)郵件操作。那使用同步的方式,程序需要等待卡在這里stepB完成才能往下走。而如果使用異步的方式,可以讓stepB“后臺”執(zhí)行,不影響當前程序往下執(zhí)行。

這在UI程序中尤為重要,畢竟界面的響應(yīng)時間對用戶的體驗很大。所以涉及到UI的語言、框架是最先研究和嘗試異步技術(shù)的。比如RxJava起源于安卓,Kotlin、Dart、JavaScript等語言也在UI程序中用得比較多。

而同樣的,對于IO密集型的程序,使用異步也能夠明顯提升性能,大家熟悉的nginx、redis、netty等,其底層都是利用的操作系統(tǒng)的系統(tǒng)調(diào)用(比如Linux的epoll)來實現(xiàn)異步,達到高性能的表現(xiàn)。

使用異步

在Java中使用異步一般是用多線程來實現(xiàn)的。

正如我們上文提到的,我們可以啟動一個新的線程去“后臺”執(zhí)行一個異步任務(wù)。當然,我們也可以把它扔進線程池里。

 
 
 
 
  1. // 新建線程執(zhí)行異步任務(wù) 
  2. new Thread(() -> stepB()).start(); 

但如果我們要使用異步的返回結(jié)果怎么辦呢?比如常見的場景是請求另一個微服務(wù)的接口。

JDK 1.5提供了Callable和Future接口,用于實現(xiàn)“有返回值”的多線程任務(wù)。使用的時候一般是配合線程池使用:

 
 
 
 
  1. public static void main(String[] args) throws Exception { 
  2.     ExecutorService executor = Executors.newSingleThreadExecutor(); 
  3.     Future future = executor.submit(() -> { 
  4.         // 模擬IO需要一秒 
  5.         Thread.sleep(1000); 
  6.         return "hello"; 
  7.     }); 
  8.     System.out.println("submitted"); 
  9.     // 這里會阻塞直到future.get返回值或者超時 
  10.     System.out.println(future.get(2, TimeUnit.SECONDS)); 
  11.     executor.shutdown(); 

如果使用Future,我們在調(diào)用future.get()方法的時候,會阻塞直到異步任務(wù)返回結(jié)果或者拋異?;蛘叱瑫r。試想一下我們有這個需求:任務(wù)B1需要任務(wù)B的結(jié)果,任務(wù)C1需要任務(wù)C的結(jié)果,但它們彼此是獨立的。如果使用Future我們得這樣做:

 
 
 
 
  1. stepA(); 
  2. Future futureB = executor.submit(() -> stepB()); 
  3. Future futureC = executor.submit(() -> stepC()); 
  4.  
  5. stepB1(futureB.get()); 
  6. // 這一步必須等stepB1執(zhí)行完 
  7. stepC1(futureC.get()); 

所以使用future其實還是會在調(diào)用get方法的時候阻塞主流程。那有沒有什么辦法不阻塞呢?解決辦法是使用回調(diào)。

回調(diào)與回調(diào)地獄

所謂回調(diào),在函數(shù)式編程語言中的說法就是,我傳一個函數(shù)進去,等異步任務(wù)完成后,就執(zhí)行這個函數(shù)。Java雖然不是函數(shù)式編程語言,但Java8也支持函數(shù)式編程。

假設(shè)我們的需求僅僅是把一個異步任務(wù)產(chǎn)生的結(jié)果字符串打印出來,我們可以這樣寫:

 
 
 
 
  1. public static void main(String[] args) throws Exception { 
  2.     Consumer callback = System.out::println; 
  3.     new Thread(() -> { 
  4.         // 模擬api調(diào)用,省略try-catch 
  5.         Thread.sleep(1000); 
  6.         // 假設(shè)這是調(diào)用第三方api返回的字符串 
  7.         String s = "hello"; 
  8.         callback.accept(s); 
  9.     }).start(); 
  10.     System.out.println("started"); 

甚至可以不用callback函數(shù),直接在把程序代碼段放到異步任務(wù)里面:

 
 
 
 
  1. public static void main(String[] args) throws Exception { 
  2.     new Thread(() -> { 
  3.         // 模擬api調(diào)用,省略try-catch 
  4.         Thread.sleep(1000); 
  5.         // 假設(shè)這是調(diào)用第三方api返回的字符串 
  6.         String s = "hello"; 
  7.         print(s); 
  8.     }).start(); 
  9.     System.out.println("started"); 
  10.  
  11. private static void print(String str) { 
  12.     System.out.println(str); 

那如果異步任務(wù)需要的回調(diào)太多呢?比如我們需要先異步請求接口A,拿到結(jié)果后再去異步請求接口B,拿到結(jié)果后再去異步請求接口C:

 
 
 
 
  1. public static void main(String[] args) throws Exception { 
  2.     new Thread(() -> { 
  3.         String resultA = callAPI("input", "a"); 
  4.         new Thread(() -> { 
  5.             String resultB = callAPI(resultA, "b"); 
  6.             new Thread(() -> { 
  7.                 String resultC = callAPI(resultB, "c"); 
  8.                 System.out.println(resultC); 
  9.             }).start(); 
  10.         }).start(); 
  11.     }).start(); 
  12.     System.out.println("started"); 
  13.  
  14. private static String callAPI(String param, String mockRes) { 
  15.     // 模擬api調(diào)用,省略try-catch 
  16.     Thread.sleep(1000); 
  17.     return mockRes; 

有沒有感覺這層層嵌套的代碼比較難看?這就是臭名昭著的“回調(diào)地獄”。

Java 8提供了一個叫CompletableFuture類來支持一些異步功能,包括回調(diào)。它支持「鏈式調(diào)用」,可以在一定程度上解決“回調(diào)地獄”的問題。上述代碼可以用CompletableFuture這樣寫:

 
 
 
 
  1. public static void main(String[] args) throws Exception { 
  2.     CompletableFuture.supplyAsync(() -> callAPI("input", "a")) 
  3.         .thenApply(res -> callAPI(res, "b")) 
  4.         .thenApply(res -> callAPI(res, "c")) 
  5.         .thenAccept(System.out::println); 
  6.     System.out.println("started"); 
  7.     // 等異步任務(wù)輸出 
  8.     Thread.sleep(20000); 

響應(yīng)式編程

是另一種異步解決方案。它的主要應(yīng)用場景是異步處理數(shù)據(jù)集合。對標的是同步的Iterable。這里有一個對比圖:

 

比較典型的場景是UI產(chǎn)生的事件流(比如點擊事件等)。

響應(yīng)式編程的核心是“觀察者模式”??蛻舳税l(fā)送請求和,能夠立即得到一個Stream返回,客戶端訂閱這個Stream來接收通知。等服務(wù)端有數(shù)據(jù)時,就會往Stream上發(fā)布數(shù)據(jù),客戶端就能夠收到數(shù)據(jù)了。

Spring 5也支持響應(yīng)式編程,并認為它將是未來web編程的一大趨勢。響應(yīng)流 API java.util.concurrent.flow 已正式成為 Java 9 的一部分。但目前發(fā)展還比較緩慢,大家對這個東西的接受度一般,可能是因為切換成本比較高,且目前webmvc能夠滿足大多數(shù)需求吧。

 

協(xié)程

看了一圈資料,很多文章在討論協(xié)程是什么。我初步總結(jié)下來協(xié)程主要有兩個作用:

可以用同步的方式寫異步代碼

可以在適當?shù)臅r候掛起當前程序片段,在適當?shù)臅r候恢復(fù),這是代碼可以控制的

協(xié)程由程序控制,在同一個線程內(nèi)部工作,在IO成為瓶頸的絕大多數(shù)應(yīng)用場景下,可以代替當前主流的多線程模型,省去線程切換的開銷,提升吞吐量。

后面有空再詳細介紹協(xié)程吧。

關(guān)于作者

 

我是Yasin,一個愛寫博客的技術(shù)人個人網(wǎng)站:https://yasinshaw.com


網(wǎng)站名稱:在Java中使用異步編程
分享鏈接:http://www.dlmjj.cn/article/dpegpio.html