91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

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

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

在Java中使用異步編程

發布時間:2021-06-18 09:47:59 來源:億速云 閱讀:122 作者:chen 欄目:編程語言

這篇文章主要介紹“在Java中使用異步編程”,在日常操作中,相信很多人在在Java中使用異步編程問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”在Java中使用異步編程”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

最近在學習協程,打算輸出幾篇文章來介紹一下協程。而協程與異步有很大的關聯,所以想先介紹一下異步。

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

程序同步和異步

同步是最簡單也是最符合我們人類思維方式的編程方式,所謂同步,就是程序會按照代碼一行行執行,執行完一句再執行下一句。

同步代碼看起來是這樣:

stepA(); stepB(); stepC(); ...

stepA執行完后,開始執行stepB,stepB執行完后,執行stepC。

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

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

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

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

異步有什么好處呢?

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

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

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

使用異步

在Java中使用異步一般是用多線程來實現的。

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

// 新建線程執行異步任務 new Thread(() -> stepB()).start();

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

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

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

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

stepA(); Future futureB = executor.submit(() -> stepB()); Future futureC = executor.submit(() -> stepC());  stepB1(futureB.get()); // 這一步必須等stepB1執行完 stepC1(futureC.get());

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

回調與回調地獄

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

假設我們的需求僅僅是把一個異步任務產生的結果字符串打印出來,我們可以這樣寫:

public static void main(String[] args) throws Exception {     Consumer<String> callback = System.out::println;     new Thread(() -> {         // 模擬api調用,省略try-catch         Thread.sleep(1000);         // 假設這是調用第三方api返回的字符串         String s = "hello";         callback.accept(s);     }).start();     System.out.println("started"); }

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

public static void main(String[] args) throws Exception {     new Thread(() -> {         // 模擬api調用,省略try-catch         Thread.sleep(1000);         // 假設這是調用第三方api返回的字符串         String s = "hello";         print(s);     }).start();     System.out.println("started"); }  private static void print(String str) {     System.out.println(str); }

那如果異步任務需要的回調太多呢?比如我們需要先異步請求接口A,拿到結果后再去異步請求接口B,拿到結果后再去異步請求接口C:

public static void main(String[] args) throws Exception {     new Thread(() -> {         String resultA = callAPI("input", "a");         new Thread(() -> {             String resultB = callAPI(resultA, "b");             new Thread(() -> {                 String resultC = callAPI(resultB, "c");                 System.out.println(resultC);             }).start();         }).start();     }).start();     System.out.println("started"); }  private static String callAPI(String param, String mockRes) {     // 模擬api調用,省略try-catch     Thread.sleep(1000);     return mockRes; }

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

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

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

響應式編程

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

在Java中使用異步編程

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

響應式編程的核心是“觀察者模式”。客戶端發送請求和,能夠立即得到一個Stream返回,客戶端訂閱這個Stream來接收通知。等服務端有數據時,就會往Stream上發布數據,客戶端就能夠收到數據了。

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

在Java中使用異步編程

協程

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

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

可以在適當的時候掛起當前程序片段,在適當的時候恢復,這是代碼可以控制的

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

后面有空再詳細介紹協程吧。

到此,關于“在Java中使用異步編程”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

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

AI

大渡口区| 类乌齐县| 瑞安市| 华蓥市| 宝清县| 宜昌市| 监利县| 集安市| 法库县| 苏尼特左旗| 平凉市| 绥宁县| 阿尔山市| 盘山县| 新和县| 兴仁县| 科技| 年辖:市辖区| 抚松县| 浏阳市| 洪雅县| 北安市| 西峡县| 深圳市| 三原县| 阿瓦提县| 满城县| 来安县| 柳江县| 呈贡县| 新野县| 太湖县| 彰化县| 松潘县| 怀仁县| 朔州市| 蒲江县| 安远县| 河北省| 玉龙| 佛山市|