摘要:異步調(diào)用是一種非阻塞式調(diào)用方式,用于在處理比較耗時(shí)的任務(wù)時(shí)保證程序性能不受到影響。實(shí)現(xiàn)異步調(diào)用的關(guān)鍵在于要解決三個(gè)技術(shù)問題,它們分別是程序阻塞問題、異步消息的傳遞問題和超時(shí)問題。本文介紹的開發(fā)方法和步驟采用并發(fā)線程、回調(diào)機(jī)制和計(jì)時(shí)器圓滿地解決了異步調(diào)用的技術(shù)難題。
關(guān)鍵詞:程序阻塞;異步消息傳遞;Java回調(diào);線程;異步調(diào)用
中圖分類號(hào):TP311 文獻(xiàn)標(biāo)識(shí)碼:A
軟件模塊之間的調(diào)用關(guān)系可以分為兩大類:即同步調(diào)用和異步調(diào)用。在同步調(diào)用中,一段代碼(主調(diào)方)調(diào)用另一段代碼(被調(diào)方),主調(diào)方必須等待這段代碼執(zhí)行完成返回結(jié)果后,才能繼續(xù)往下執(zhí)行,所以,同步調(diào)用是一種阻塞式調(diào)用,主調(diào)方代碼一直阻塞等待直到被調(diào)方返回為止。同步調(diào)用相對比較直觀,也是大部分編程語言直接支持的一種調(diào)用方式。但是,同步調(diào)用在處理比較耗時(shí)的情況下會(huì)嚴(yán)重影響程序性能,影響人機(jī)交互的瞬時(shí)反應(yīng)。例如,某個(gè)程序需要訪問數(shù)據(jù)庫獲取大量數(shù)據(jù),然后根據(jù)這些數(shù)據(jù)進(jìn)行一系列處理,將處理結(jié)果顯示在程序主窗口。由于數(shù)據(jù)庫訪問和大量數(shù)據(jù)的處理都是耗時(shí)的工作,在這個(gè)工作完成之前,處理結(jié)果遲遲不能顯示,用戶點(diǎn)擊鼠標(biāo)也不會(huì)立即得到響應(yīng),讓用戶感到整個(gè)程序顯得很沉重。面對這樣一些需要比較長時(shí)間才能完成的應(yīng)用場景,我們需要采用一種非阻塞式調(diào)用方式,即異步調(diào)用方式。在異步調(diào)用中,主調(diào)方調(diào)用被調(diào)方后,不等待對方返回結(jié)果就繼續(xù)執(zhí)行后續(xù)代碼,被調(diào)方執(zhí)行完畢后,通過某種手段通知調(diào)用方:結(jié)果已經(jīng)出來,請酌情處理。我們可以對上面的例子改用異步調(diào)用將問題輕松化解:把整個(gè)耗時(shí)的工作放進(jìn)一個(gè)單獨(dú)的線程,由主調(diào)方啟動(dòng)此線程后繼續(xù)執(zhí)行后續(xù)代碼,線程在背后悄悄地處理費(fèi)時(shí)的工作,當(dāng)工作完成,采用回調(diào)的方式通知主調(diào)方工作完成,主調(diào)方將結(jié)果顯示在主窗口。經(jīng)過這樣的處理,主界面繼續(xù)進(jìn)行自己的工作而不必死等,就不會(huì)造成界面響應(yīng)遲鈍。
在實(shí)現(xiàn)異步調(diào)用機(jī)制時(shí),除了線程之外,還要用到回調(diào)?;卣{(diào)是一種雙向調(diào)用,也就是,被調(diào)方在被調(diào)用時(shí)也會(huì)調(diào)用主調(diào)方的代碼。在異步調(diào)用中,被調(diào)方需要在工作完成時(shí)通知主調(diào)方,即調(diào)用主調(diào)方的接口,這一機(jī)制通過回調(diào)實(shí)現(xiàn)。回調(diào)和異步調(diào)用的關(guān)系非常緊密,回調(diào)是異步調(diào)用的基礎(chǔ)[1]。
本文理論聯(lián)系實(shí)際,首先闡述如何使用Java實(shí)現(xiàn)回調(diào)機(jī)制,然后進(jìn)一步闡述使用Java回調(diào)和線程實(shí)現(xiàn)異步調(diào)用,最后,闡述在異步調(diào)用中如何處理超時(shí)問題。
1 Java回調(diào)機(jī)制的實(shí)現(xiàn)方法
實(shí)現(xiàn)Java回調(diào),需要做如下三件事情:
(1)定義一個(gè)回調(diào)接口CallbackInterface
接口中聲明回調(diào)方法handle,如圖1所示,回調(diào)方法就是一個(gè)普通的方法,接收一個(gè)消息字符串或者一個(gè)封裝了數(shù)據(jù)的事件。
(2)定義一個(gè)類實(shí)現(xiàn)回調(diào)接口
這個(gè)類其實(shí)就是消息接收者和處理者,如圖2所示,其中:回調(diào)方法是消息發(fā)生時(shí)實(shí)際處理消息的方法,此處簡化為一條打印語句。
(3)定義消息通知者
消息通知者必須具備兩種能力,第一,它必須知道誰是消息接收者,第二,當(dāng)消息發(fā)生時(shí),它能夠回調(diào)這些接收者的回調(diào)方法。為了獲得這兩種能力,消息通知者首先必須提供一個(gè)注冊方法register, 通過注冊的方式來注冊多個(gè)對此消息或事件感興趣的對象。然后提供一個(gè)消息通知方法notifyMessage,在這個(gè)方法中調(diào)用所有消息接收者的回調(diào)方法。具體代碼如圖3所示。
上述代碼使用了一個(gè)可變數(shù)組List
有了消息接收者和消息通知者,就可以完成消息通知機(jī)制了,圖4給出的是測試代碼。
這里有一個(gè)非常關(guān)鍵的步驟就是將消息接收者向消息通知者注冊,只有已注冊的對象才會(huì)收到消息通知。
值得一提的是,Java回調(diào)機(jī)制的使用非常廣泛, 比如Java的事件監(jiān)聽器模式和觀察者模式都包含了回調(diào)機(jī)制[2],在一些框架API提供的調(diào)用中不少也是以回調(diào)的形式提供的[3]。深刻理解Java回調(diào)機(jī)制是很有裨益的。
運(yùn)行結(jié)果如圖5所示。
2 使用Java回調(diào)和線程實(shí)現(xiàn)異步調(diào)用
線程是一個(gè)獨(dú)立的執(zhí)行流,其本質(zhì)是程序中一段并發(fā)執(zhí)行的代碼。在異步調(diào)用機(jī)制中引入線程,在線程中完成耗時(shí)的工作,其目的是讓調(diào)用方的主線程繼續(xù)執(zhí)行后續(xù)代碼而不需要等待被調(diào)方的結(jié)果返回。由于不需要等待,這樣我們就等于同時(shí)做了兩件事情,而這兩件事情分別是在不同的執(zhí)行流中執(zhí)行,主調(diào)者在當(dāng)前的主線程中執(zhí)行,被調(diào)者在另外一個(gè)線程中執(zhí)行,因此提高了程序的效率,避免了界面的響應(yīng)遲鈍。當(dāng)被調(diào)者執(zhí)行完成后,仍然采用回調(diào)通知主調(diào)者。
如圖6所示,LongTimeWorker是一個(gè)用于完成耗時(shí)工作的線程,同時(shí)又是消息通知者。其耗時(shí)工作在run方法中完成,另外提供一個(gè)注冊方法register, 和一個(gè)消息通知方法notifyMessage,在run方法的最后,即耗時(shí)工作完成以后,調(diào)用notifyMessage將消息廣播出去。
將前面的測試代碼做一點(diǎn)改動(dòng)就可以看到異步調(diào)用的效果如圖7所示。
3 異步調(diào)用中超時(shí)問題的處理
異步調(diào)用通常都要加入超時(shí)機(jī)制,因?yàn)槲覀兛偸窍M谝粋€(gè)指定的時(shí)間范圍內(nèi)返回一個(gè)結(jié)果,即使沒有得到結(jié)果也該有個(gè)超時(shí)通知。這時(shí)我們需要使用“限時(shí)線程回調(diào)方式”,它在原有線程回調(diào)的基礎(chǔ)上加上一個(gè)計(jì)時(shí)器Timer以計(jì)算消耗的時(shí)間,如果時(shí)間期限到了任務(wù)還沒有執(zhí)行完成即中斷線程,并將超時(shí)消息廣播出去。LongTimeWorker類需要修改部分的代碼如圖8和圖9所示。
首先LongTimeWorker線程類增加了一個(gè)構(gòu)造方法,其參數(shù)是超時(shí)時(shí)間timeout,構(gòu)造方法的主要任務(wù)是創(chuàng)建一個(gè)定時(shí)器,每秒鐘計(jì)時(shí)一次,若超時(shí)時(shí)間到則終止本線程,并廣播超時(shí)消息。LongTimeWorker線程類的第二個(gè)改變發(fā)生在其run方法中,線程一啟動(dòng)立即開始計(jì)時(shí),完成工作后停止計(jì)時(shí),并廣播消息。
4 結(jié)束語
異步調(diào)用是一種非阻塞式調(diào)用方式,用于在處理比較耗時(shí)的任務(wù)時(shí)保證程序性能不受到影響。實(shí)現(xiàn)異步調(diào)用的關(guān)鍵在于要解決三個(gè)技術(shù)難題,它們分別是程序阻塞問題、異步消息的傳遞問題和超時(shí)問題。本文介紹的方法采用并發(fā)線程、回調(diào)機(jī)制和計(jì)時(shí)器使上述問題得到了圓滿解決。
參考文獻(xiàn):
[1] 陳家朋.異步消息的傳遞-回調(diào)機(jī)制[EB/OL]. http://www.ibm.com/developerworks/cn/linux/l-callback/, 2003.
[2] Eric Freeman.Head First Design Pattern[M].O’Reilly Media, Inc. 2004:51-53.
[3] 羅時(shí)飛.精通Spring-深入Java EE開發(fā)核心技術(shù)[EB/OL] , 回調(diào)接口集合及其觸發(fā)順序http://book.51cto.com/art/201004/193405.htm.
作者簡介:
錢宇虹,女,計(jì)算機(jī)科學(xué)碩士.從事軟件開發(fā)與應(yīng)用、軟件
工程、軟件測試技術(shù)方面的教學(xué)、科研和開發(fā).