最近2018中文字幕在日韩欧美国产成人片_国产日韩精品一区二区在线_在线观看成年美女黄网色视频_国产精品一区三区五区_国产精彩刺激乱对白_看黄色黄大色黄片免费_人人超碰自拍cao_国产高清av在线_亚洲精品电影av_日韩美女尤物视频网站

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

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
怎么處理java異步事件的阻塞和非阻塞

本篇內(nèi)容主要講解“怎么處理java異步事件的阻塞和非阻塞”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“怎么處理java異步事件的阻塞和非阻塞”吧!

為海州等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計(jì)制作服務(wù),及海州網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作、海州網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!

前言

由于多核系統(tǒng)普遍存在,并發(fā)性編程的應(yīng)用無疑比以往任何時(shí)候都要廣泛。但并發(fā)性很難正確實(shí)現(xiàn),用戶需要借助新工具來使用它。很多基于 JVM 的語言都屬于這類開發(fā)工具,Scala 在這一領(lǐng)域尤為活躍。本系列文章將介紹一些針對(duì) Java 和 Scala 語言的較新的并發(fā)性編程方法。

在任何并發(fā)性應(yīng)用程序中,異步事件處理都至關(guān)重要。事件來源可能是不同的計(jì)算任務(wù)、I/O 操作或與外部系統(tǒng)的交互。無論來源是什么,應(yīng)用程序代碼都必須跟蹤事件,協(xié)調(diào)為響應(yīng)事件而采取的操作。

Java 應(yīng)用程序可采用兩種基本的異步事件處理方法:該應(yīng)用程序有一個(gè)協(xié)調(diào)線程等待事件,然后采取操作,或者事件可在完成時(shí)直接執(zhí)行某項(xiàng)操作(通常采取執(zhí)行應(yīng)用程序所提供的代碼的方式)。讓線程等待事件的方法被稱為阻塞 方法。讓事件執(zhí)行操作、線程無需顯式等待事件的方法被稱為非阻塞 方法。

在計(jì)算中,根據(jù)具體上下文,阻塞 和非阻塞 這兩個(gè)詞的使用通常會(huì)有所不同。舉例而言,共享數(shù)據(jù)結(jié)構(gòu)的非阻塞算法不需要線程等待訪問數(shù)據(jù)結(jié)構(gòu)。在非阻塞 I/O 中,應(yīng)用程序線程可以啟動(dòng)一個(gè) I/O 操作,然后離開執(zhí)行其他事情,同時(shí)該操作會(huì)異步地執(zhí)行。在本文中,非阻塞 指的是在無需等待線程的情況下完成某個(gè)執(zhí)行操作的事件。這些用法中的一個(gè)共同概念是,阻塞操作需要一個(gè)線程來等待某個(gè)結(jié)果,而非阻塞操作不需要。

合成事件

等待事件的完成很簡(jiǎn)單:您有一個(gè)線程等待該事件,線程恢復(fù)運(yùn)行時(shí),您就可以知道該事件已經(jīng)完成。如果您的線程在此期間有其他事要做,它會(huì)做完這些事再等待。該線程甚至可以使用輪詢方法,通過該方法中斷它的其他活動(dòng),從而檢查事件是否已完成。但基本原理是相同的:需要事件的結(jié)果時(shí),您會(huì)讓線程???(park),以便等待事件完成。

阻塞很容易完成且相對(duì)簡(jiǎn)單,只要您有一個(gè)等待事件完成的單一主線程。使用多個(gè)因?yàn)楸舜说却枞木€程時(shí),可能遇到一些問題,比如:

死鎖:兩個(gè)或更多線程分別控制其他線程繼續(xù)執(zhí)行所需的資源。  饑餓 (Starvation):一些線程可能無法繼續(xù)執(zhí)行,因?yàn)槠渌€程貪婪地消耗著共享資源。  活鎖:線程嘗試針對(duì)彼此而調(diào)整,但最終沒有進(jìn)展。

非阻塞方法為創(chuàng)造力留出的空間要多得多?;卣{(diào)是非阻塞事件處理的一種常見技術(shù)?;卣{(diào)是靈活性的象征,因?yàn)槟梢栽诎l(fā)生事件時(shí)執(zhí)行任何想要的代碼?;卣{(diào)的缺點(diǎn)是,在使用回調(diào)處理許多事件時(shí),您的代碼會(huì)變得凌亂。而且回調(diào)特別難調(diào)試,因?yàn)榭刂屏髋c應(yīng)用程序中的代碼順序不匹配。

Java 8 CompletableFuture 同時(shí)支持阻塞和非阻塞的事件處理方法,包括常規(guī)回調(diào)。CompletableFuture 也提供了多種合成和組合事件的方式,實(shí)現(xiàn)了回調(diào)的靈活性以及干凈、簡(jiǎn)單、可讀的代碼。在本節(jié)中,您將看到處理由 CompletableFuture 表示的事件的阻塞和非阻塞方法的示例。

任務(wù)和排序

應(yīng)用程序在一個(gè)特定操作中通常必須執(zhí)行多個(gè)處理步驟。例如,在向用戶返回結(jié)果之前,Web 應(yīng)用程序可能需要:

1.在一個(gè)數(shù)據(jù)庫中查找用戶的信息2.使用查找到的信息來執(zhí)行 Web 服務(wù)調(diào)用,并執(zhí)行另一次數(shù)據(jù)庫查詢。3.基于來自上一步的結(jié)果而執(zhí)行數(shù)據(jù)庫更新。

圖 1 演示了這種結(jié)構(gòu)類型。

圖 1. 應(yīng)用程序任務(wù)流

圖 1 將處理過程分解為 4 個(gè)不同的任務(wù),它們通過表示順序依賴關(guān)系的箭頭相連接。任務(wù) 1 可直接執(zhí)行,任務(wù) 2 和任務(wù) 3 都在任務(wù) 1 完成后執(zhí)行,任務(wù) 4 在任務(wù) 2 和任務(wù) 3 都完成后執(zhí)行。這是我在本文中用于演示異步事件處理的任務(wù)結(jié)構(gòu)。真實(shí)應(yīng)用程序(尤其是具有多個(gè)移動(dòng)部分的服務(wù)器應(yīng)用程序)可能要復(fù)雜得多,但這個(gè)簡(jiǎn)單的示例僅用于演示所涉及的原理。

建模異步事件

在真實(shí)系統(tǒng)中,異步事件的來源一般是并行計(jì)算或某種形式的 I/O 操作。但是,使用簡(jiǎn)單的時(shí)間延遲來建模這種系統(tǒng)會(huì)更容易,這也是本文所采用的方法。清單 1 顯示了我用于生成事件的基本的賦時(shí)事件 (timed-event) 代碼,這些事件采用了 CompletableFuture 格式。

清單 1. 賦時(shí)事件代碼

import java.util.Timer;import java.util.TimerTask;import java.util.concurrent.CompletableFuture;public class TimedEventSupport {private static final Timer timer = new Timer();/*** Build a future to return the value after a delay.** @param delay* @param value* @return future*/public static CompletableFuture delayedSuccess(int delay, T value) {CompletableFuture future = new CompletableFuture();TimerTask task = new TimerTask() {public void run() {future.complete(value);}};timer.schedule(task, delay * 1000);return future;}/*** Build a future to return a throwable after a delay.* * @param delay* @param t* @return future*/public static CompletableFuture delayedFailure(int delay, Throwable t) {CompletableFuture future = new CompletableFuture();TimerTask task = new TimerTask() {public void run() {future.completeExceptionally(t);}};timer.schedule(task, delay * 1000);return future;}}

為什么不采用 lambda?

清單 1 中的 TimerTask 被實(shí)現(xiàn)為一個(gè)匿名內(nèi)部類,僅包含一個(gè) run() 方法。您可能認(rèn)為這里可以使用 lambda 代替內(nèi)部類。但是,lambda 僅能用作接口的實(shí)例,而 TimerTask 被定義為一種抽象類。除非 lambda 特性的 future 擴(kuò)展添加了對(duì)抽象類的支持(有可能,但由于設(shè)計(jì)問題,未必行得通),或者為 TimerTask 等情形定義了并行接口,否則您必須繼續(xù)使用 Java 內(nèi)部類創(chuàng)建單一方法實(shí)現(xiàn)。

清單 1 的代碼使用一個(gè) java.util.Timer 來計(jì)劃 java.util.TimerTask 在一定的延遲后執(zhí)行。每個(gè) TimerTask 在運(yùn)行時(shí)完成一個(gè)有關(guān)聯(lián)的 future。delayedSuccess() 計(jì)劃一個(gè)任務(wù)來成功完成一個(gè) CompletableFuture 并將 future 返回調(diào)用方。delayedFailure() 計(jì)劃了一個(gè)任務(wù)來完成一個(gè) CompletableFuture 并拋出異常,然后將 future 返回給調(diào)用方。

清單 2 展示了如何使用 清單 1 中的代碼創(chuàng)建 CompletableFuture 形式的事件,這些事件與 圖 1 中的 4 個(gè)任務(wù)相匹配。(此代碼來自示例代碼中的 EventComposition 類。)

清單 2. 示例任務(wù)的事件

// task definitionsprivate static CompletableFuture task1(int input) {return TimedEventSupport.delayedSuccess(1, input + 1);}private static CompletableFuture task2(int input) {return TimedEventSupport.delayedSuccess(2, input + 2);}private static CompletableFuture task3(int input) {return TimedEventSupport.delayedSuccess(3, input + 3);}private static CompletableFuture task4(int input) {return TimedEventSupport.delayedSuccess(1, input + 4);}

清單 2 中 4 個(gè)任務(wù)方法中的每一個(gè)都為該任務(wù)的完成時(shí)刻使用了特定的延遲值:task1 為 1 秒,task2 為 2 秒,task3 為 3 秒,task4 重新變?yōu)?1 秒。每個(gè)任務(wù)還接受一個(gè)輸入值,是該輸入加上任務(wù)編號(hào)作為 future 的(最終)結(jié)果值。這些方法都使用了 future 的成功形式;稍后我們將會(huì)查看一些使用失敗形式的例子。

這些任務(wù)要求您按 圖 1 中所示的順序運(yùn)行它們,向每個(gè)任務(wù)傳遞上一個(gè)任務(wù)返回的結(jié)果值(或者對(duì)于 task4,傳遞前兩個(gè)任務(wù)結(jié)果的和)。如果中間兩個(gè)任務(wù)是同時(shí)執(zhí)行的,那么總執(zhí)行時(shí)間大約為 5 秒(1 秒 + (2 秒、3 秒中的最大值)+ 1 秒。

如果 task1 的輸入為 1,那么結(jié)果為 2。如果該結(jié)果傳遞給 task2 和 task3,結(jié)果將為 4 和 5。如果這兩個(gè)結(jié)果的和 (9) 作為輸入傳遞給 task4,最終結(jié)果將為 13。

阻塞等待

在設(shè)置了執(zhí)行環(huán)境后,是時(shí)候設(shè)置一些操作了。協(xié)調(diào) 4 個(gè)任務(wù)的執(zhí)行的最簡(jiǎn)單方式是使用阻塞等待:主要線程等待每個(gè)任務(wù)完成。清單 3(同樣來自示例代碼中的 EventComposition 類)給出了此方法。

清單 3. 阻塞等待任務(wù)執(zhí)行

private static CompletableFuture runBlocking() {Integer i1 = task1(1).join();CompletableFuture future2 = task2(i1);CompletableFuture future3 = task3(i1);Integer result = task4(future2.join() + future3.join()).join();return CompletableFuture.completedFuture(result);}

清單 3 使用 CompletableFuture 的 join() 方法來完成阻塞等待。join() 等待任務(wù)完成,然后,如果成功完成任務(wù),則返回結(jié)果值,或者如果失敗或被取消,則拋出一個(gè)未經(jīng)檢查的異常。該代碼首先等待 task1 的結(jié)果,然后同時(shí)啟動(dòng) task2 和 task3,并等待兩個(gè)任務(wù)依次返回 future,最后等待 task4 的結(jié)果。runBlocking() 返回一個(gè) CompletableFuture,以便與我接下來將展示的非阻塞形式保持一致,但在本例中,future 實(shí)際上將在該方法返回之前完成。

合成和組合 future

清單 4(同樣來自示例代碼中的 EventComposition 類)展示了如何將 future 連接在一起,以便按正確順序并使用正確的依賴關(guān)系執(zhí)行任務(wù),而不使用阻塞。

清單 4. 非阻塞的合成和組合

private static CompletableFuture runNonblocking() {return task1(1).thenCompose(i1 -> ((CompletableFuture)task2(i1).thenCombine(task3(i1), (i2,i3) -> i2+i3))).thenCompose(i4 -> task4(i4));}

清單 4 中的代碼基本上構(gòu)造了一個(gè)執(zhí)行計(jì)劃,指定不同的任務(wù)如何執(zhí)行和它們彼此有何關(guān)聯(lián)。此代碼精美而簡(jiǎn)潔,但是,如果您不熟悉 CompletableFuture 方法,或許難以理解該代碼。清單 5 通過將 task2 和 task3 部分分離到一個(gè)新方法 runTask2and3 中,將同樣的代碼重構(gòu)為更容易理解的形式。

清單 5. 重構(gòu)后的非阻塞的合成和組合

private static CompletableFuture runTask2and3(Integer i1) {CompletableFuture task2 = task2(i1);CompletableFuture task3 = task3(i1);BiFunction sum = (a, b) -> a + b;return task2.thenCombine(task3, sum);}private static CompletableFuture runNonblockingAlt() {CompletableFuture task1 = task1(1);CompletableFuture comp123 = task1.thenCompose(EventComposition::runTask2and3);return comp123.thenCompose(EventComposition::task4); }

在 清單 5 中,runTask2and3() 方法表示任務(wù)流的中間部分,其中 task2 和 task3 同時(shí)執(zhí)行,然后將它們的結(jié)果值組合在一起。此順序是使用一個(gè) future 上的 thenCombine() 方法來編碼的,該方法接受另一個(gè) future 作為它的第一個(gè)參數(shù),接受一個(gè)二進(jìn)制函數(shù)實(shí)例(其輸入類型與 future 的結(jié)果類型匹配)作為其第二個(gè)參數(shù)。thenCombine() 返回了第三個(gè) future,表示應(yīng)用到最初的兩個(gè) future 的結(jié)果上的函數(shù)的值。在本例中,兩個(gè) future 是 task2 和 task3,該函數(shù)將結(jié)果值求和。

runNonblockingAlt() 方法使用在一個(gè) future 上調(diào)用了 thenCompose() 方法兩次。thenCompose() 的參數(shù)是一個(gè)函數(shù)實(shí)例,它接收原始 future 的值類型作為輸入,返回另一個(gè) future 作為輸出。thenCompose() 的結(jié)果是第三個(gè) future,具有與該函數(shù)相同的結(jié)果類型。這個(gè) future 用作在原始 future 完成后,該函數(shù)最終將返回的 future 的占位符。

對(duì) task1.thenCompose() 的調(diào)用將會(huì)返回一個(gè) future,表示對(duì) task1 的結(jié)果應(yīng)用 runTask2and3() 函數(shù)的結(jié)果,該結(jié)果被保存為 comp123。對(duì) comp123.thenCompose() 的調(diào)用返回一個(gè) future,表示對(duì)第一個(gè) henCompose() 的結(jié)果應(yīng)用 task4() 函數(shù)的結(jié)果,這是執(zhí)行所有任務(wù)的總體結(jié)果。

試用示例

示例代碼包含一個(gè) main() 方法,以便依次運(yùn)行事件代碼的每個(gè)版本,并顯示完成事件(約 5 秒)和結(jié)果 (13) 是正確的。清單 6 顯示了從一個(gè)控制臺(tái)運(yùn)行這個(gè) main() 方法的結(jié)果。

清單 6. 運(yùn)行 main() 方法

dennis@linux-guk3:~/devworks/scala3/code/bin> java com.sosnoski.concur.article3.EventCompositionStarting runBlockingrunBlocking returned 13 in 5008 ms.Starting runNonblockingrunNonblocking returned 13 in 5002 ms.Starting runNonblockingAltrunNonblockingAlt returned 13 in 5001 ms.

不順利的道路

目前為止,您看到了以 future 形式協(xié)調(diào)事件的代碼,這些代碼總是能夠成功完成。在真實(shí)應(yīng)用程序中,不能寄希望于事情總是這么順利。處理任務(wù)過程中將發(fā)生問題,而且在 Java 術(shù)語中,這些問題通常表示為 Throwable。

更改 清單 2 中的任務(wù)定義很容易,只需使用 delayedFailure() 代替 delayedSuccess() 方法即可,如這里的 task4 所示:

private static CompletableFuture task4(int input) {return TimedEventSupport.delayedFailure(1, new IllegalArgumentException("This won't work!"));}

如果運(yùn)行 清單 3 并且僅將 task4 修改為完成時(shí)拋出異常,那么您會(huì)得到 task4 上的 join() 調(diào)用所拋出的預(yù)期的 IllegalArgumentException。如果在 runBlocking() 方法中沒有捕獲該問題,該異常會(huì)在調(diào)用鏈中一直傳遞,最終如果仍未捕獲問題,則會(huì)終止執(zhí)行線程。幸運(yùn)的是,修改該代碼很容易,因此,如果在任何任務(wù)完成時(shí)拋出異常,該異常會(huì)通過返回的 future 傳遞給調(diào)用方來處理。清單 7 展示了這一更改。

清單 7. 具有異常的阻塞等待

private static CompletableFuture runBlocking() {try {Integer i1 = task1(1).join();CompletableFuture future2 = task2(i1);CompletableFuture future3 = task3(i1);Integer result = task4(future2.join() + future3.join()).join();return CompletableFuture.completedFuture(result);} catch (CompletionException e) {CompletableFuture result = new CompletableFuture();result.completeExceptionally(e.getCause());return result;}}

清單 7 非常淺顯易懂。最初的代碼包裝在一個(gè) try/catch 中,catch 在返回的 future 完成時(shí)傳回異常。此方法稍微增加了一點(diǎn)復(fù)雜性,但任何 Java 開發(fā)人員應(yīng)該仍然很容易理解它。

清單 4 中的非阻塞代碼甚至不需要添加 try/catch。CompletableFuture 合成和組合操作負(fù)責(zé)自動(dòng)為您傳遞異常,以便依賴的 future 也會(huì)在完成時(shí)拋出異常。

阻塞還是不阻塞

您已經(jīng)查看了由 CompletableFuture 表示的事件的阻塞和非阻塞處理方法。至少對(duì)于本文中建模的基本的任務(wù)流,兩種方法都非常簡(jiǎn)單。對(duì)于更復(fù)雜的任務(wù)流,該代碼也會(huì)更加復(fù)雜。

在阻塞情況下,增加的復(fù)雜性不是大問題,您仍然只需要等待事件完成。如果要在線程之間執(zhí)行其他類型的同步,則會(huì)遇到線程饑餓問題,甚至是死鎖問題。

在非阻塞情況下,事件的完成所觸發(fā)的代碼執(zhí)行很難調(diào)試。在執(zhí)行許多類型的事件且事件之間存在許多交互時(shí),跟蹤哪個(gè)事件觸發(fā)了哪次執(zhí)行就會(huì)變得很難。這種情形基本上就是回調(diào)的噩夢(mèng),無論是使用傳統(tǒng)的回調(diào)還是 CompletableFuture 組合和合成操作。

總而言之,阻塞代碼通常具有簡(jiǎn)單性優(yōu)勢(shì)。那么為什么有人希望使用非阻塞方法?本節(jié)將給出一些重要的理由。

切換的成本

一個(gè)線程阻塞時(shí),以前執(zhí)行該線程的處理器核心會(huì)轉(zhuǎn)而執(zhí)行另一個(gè)線程。以前執(zhí)行的線程的執(zhí)行狀態(tài)必須保存到內(nèi)存中,并加載新線程的狀態(tài)。這種將核心從運(yùn)行一個(gè)線程切換到運(yùn)行另一個(gè)線程的操作稱為上下文切換。

除了直接的上下文切換性能成本,新線程一般會(huì)使用來自前一個(gè)線程的不同數(shù)據(jù)。內(nèi)存訪問比處理器時(shí)鐘慢得多,所以現(xiàn)代系統(tǒng)會(huì)在處理器核心與主要內(nèi)存之間使用多層緩存。盡管比主要內(nèi)存快得多,但緩存的容量也小得多(一般而言,緩存越快,容量越?。匀魏螘r(shí)刻只能在緩存中保存總內(nèi)存的小部分。

發(fā)生線程切換且一個(gè)核心開始執(zhí)行一個(gè)新線程時(shí),新線程需要的內(nèi)存數(shù)據(jù)可能不在緩存中,所以該核心必須等待該數(shù)據(jù)從主要內(nèi)存加載。

組合的上下文切換和內(nèi)存訪問延遲,會(huì)導(dǎo)致直接的顯著性能成本。圖 2 顯示了我使用 Oracle 的 Java 8 for 64-bit Linux 的四核 AMD 系統(tǒng)上的線程切換開銷。

此測(cè)試使用了可變數(shù)量的線程,數(shù)量從 1 到 4,096 按 2 的冪次變化,每個(gè)線程的內(nèi)存塊大小也是可變的,介于 0 到 64KB 之間。線程依次執(zhí)行,使用 CompletableFuture 來觸發(fā)線程執(zhí)行。每次一個(gè)線程執(zhí)行時(shí),它首先使用針對(duì)線程的數(shù)據(jù)返回一個(gè)簡(jiǎn)單計(jì)算結(jié)果,以顯示將此數(shù)據(jù)加載到緩存中的開銷,然后增加一個(gè)共享的靜態(tài)變量。

最后創(chuàng)建一個(gè)新 CompletableFuture 實(shí)例來觸發(fā)它的下一次執(zhí)行,然后通過完成該線程等待的 CompletableFuture 來啟動(dòng)序列中的下一個(gè)線程。最后,如果需要再次執(zhí)行它,那么該線程會(huì)等待新創(chuàng)建的 CompletableFuture 完成。

圖 2. 線程切換成本

可以在圖 2 的圖表中看到線程數(shù)量和每個(gè)線程的內(nèi)存的影響。線程數(shù)量為 4 個(gè)時(shí)影響最大,只要特定于線程的數(shù)據(jù)足夠小,兩個(gè)線程的運(yùn)行速度幾乎與單個(gè)線程一樣快。線程數(shù)量超過 4 個(gè)后,對(duì)性能的影響相對(duì)較小。每個(gè)線程的內(nèi)存量越大,兩層緩存就會(huì)越快溢出,導(dǎo)致切換成本增高。

圖 2 中所示的時(shí)間值來自我的有點(diǎn)過時(shí)的主要系統(tǒng)。您系統(tǒng)上相應(yīng)的時(shí)間將不同,可能會(huì)小得多。但是,曲線的形狀應(yīng)大體相同。

圖 2 顯示了一次線程切換的微秒級(jí)開銷,所以即使線程切換的成本達(dá)到數(shù)萬個(gè)處理器時(shí)鐘,但絕對(duì)數(shù)字并不大。對(duì)于中等數(shù)量的線程,16KB 數(shù)據(jù)具有 12.5 微秒的切換時(shí)間(圖表中的黃線),系統(tǒng)每秒可執(zhí)行 80,000 次線程切換。與您在任何精心編寫的單用戶應(yīng)用程序以及甚至許多服務(wù)器應(yīng)用程序中看到的結(jié)果相比,這一線程切換次數(shù)可能多得多。但對(duì)于每秒處理數(shù)千個(gè)事件的高性能服務(wù)器應(yīng)用程序,阻塞的開銷可能成為影響性能的主要因素。對(duì)于這種應(yīng)用程序,盡可能使用非阻塞代碼來最大限度減少線程切換非常重要。

認(rèn)識(shí)到這些時(shí)間數(shù)據(jù)來自最理想的場(chǎng)景也很重要。運(yùn)行線程切換程序時(shí),會(huì)運(yùn)行足夠的 CPU 活動(dòng)來讓所有核心全速運(yùn)行(至少在我的系統(tǒng)上是這樣)。在真實(shí)應(yīng)用程序中,處理負(fù)載可能具有更大的突發(fā)性。在活動(dòng)量低的時(shí)間,現(xiàn)代處理器將一些核心過渡到休眠狀態(tài),以減少總功耗和產(chǎn)生的熱量。這個(gè)降低功耗的過程的惟一問題是,在需求增多時(shí),它需要時(shí)間來將核心從休眠狀態(tài)喚醒。從深度休眠狀態(tài)過渡到全速運(yùn)行所需的時(shí)間可能達(dá)到微秒級(jí)別,而不是在這個(gè)線程切換時(shí)間示例中看到的毫秒級(jí)。

反應(yīng)式應(yīng)用程序

對(duì)于許多應(yīng)用程序,不在特定線程上阻塞的另一個(gè)原因是,這些線程用于處理需要及時(shí)響應(yīng)的事件。經(jīng)典的例子就是 UI 線程。如果在 UI 線程中執(zhí)行會(huì)阻塞來等待異步事件完成的代碼,那么您會(huì)延遲用戶輸入事件的處理。沒有人喜歡等待應(yīng)用程序響應(yīng)他們的鍵入、單擊或觸控操作,所以 UI 線程中的阻塞可能很快在來自用戶的錯(cuò)誤報(bào)告中反映出來。

UI 線程概念以一種更一般性的原則作為支撐。許多類型的應(yīng)用程序,甚至非 GUI 應(yīng)用程序,也必須迅速響應(yīng)事件,而且在許多情況下,保持較短的響應(yīng)事件至關(guān)重要。對(duì)于這些類型的應(yīng)用程序,阻塞等待不是可接受的選擇。

反應(yīng)式編程 這個(gè)名稱表示為響應(yīng)靈敏且可擴(kuò)展的應(yīng)用程序采用的編程風(fēng)格。反應(yīng)式編程的核心原則是應(yīng)用程序應(yīng)能夠:

對(duì)事件做出反應(yīng):應(yīng)用程序應(yīng)是事件驅(qū)動(dòng)的,在由異步通信所鏈接的每個(gè)級(jí)別上具有松散耦合的組件。  對(duì)負(fù)載做出反應(yīng):應(yīng)用程序應(yīng)該是可擴(kuò)展的,以便可以輕松地升級(jí)應(yīng)用程序來處理增加的需求。  對(duì)故障做出反應(yīng):應(yīng)用程序應(yīng)具有恢復(fù)能力,能將故障的影響局部化并迅速更正。  對(duì)用戶做出反應(yīng):應(yīng)用程序應(yīng)能迅速響應(yīng)用戶,甚至在具有負(fù)載和存在故障的情況下。

使用阻塞式事件處理方法的應(yīng)用程序無法滿足這些原則。線程是有限的資源,所以在阻塞等待中占用它們會(huì)限制可伸縮性,還會(huì)增加延遲(應(yīng)用程序響應(yīng)時(shí)間),因?yàn)樽枞木€程無法立即響應(yīng)事件。非阻塞應(yīng)用程序可更快地響應(yīng)事件,降低成本,同時(shí)減少線程切換開銷并改善吞吐量。

反應(yīng)式編程比非阻塞代碼的復(fù)雜得多。反應(yīng)式編程涉及到關(guān)注您應(yīng)用程序中的數(shù)據(jù)流并將這些數(shù)據(jù)流實(shí)現(xiàn)為異步交互,而不會(huì)讓接收方負(fù)擔(dān)過重或讓發(fā)送方積滯。這種對(duì)數(shù)據(jù)流的關(guān)注,有助于避免傳統(tǒng)的并發(fā)編程的許多復(fù)雜性。

到此,相信大家對(duì)“怎么處理java異步事件的阻塞和非阻塞”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!


文章標(biāo)題:怎么處理java異步事件的阻塞和非阻塞
網(wǎng)站網(wǎng)址:http://fisionsoft.com.cn/article/ighess.html