- 1. JAVA開(kāi)發(fā)工具(集成圖形開(kāi)發(fā)環(huán)境和多線(xiàn)程調(diào)試器) V3...
- 2. 手機(jī)人人 (for Java校內(nèi)通手機(jī)客戶(hù)端) 官方安裝版
- 3. Java反編譯工具
- 4. 模擬器運(yùn)行需要JAVA虛擬機(jī)(手機(jī)軟件下載)
- 5. Seurat|基于Java的圖像處理工具 1.0.1
- 6. Sun Java SE Runtime Environment (JRE) for 6 Upda
- 7. Javascript混淆加密器1.2破解版
- 8. Sothink JavaScript Web Scroller (網(wǎng)頁(yè)卷動(dòng)條)V2....
- 9. eclipse java編譯器 中文免費(fèi)版
多線(xiàn)程開(kāi)發(fā)的捷徑:構(gòu)建Java并發(fā)模型框架
Java多線(xiàn)程特性為構(gòu)建高性能的應(yīng)用提供了極大的方便,但是也帶來(lái)了不少的麻煩。線(xiàn)程間同步、數(shù)據(jù)一致性等煩瑣的問(wèn)題需要細(xì)心的考慮,一不小心就會(huì)出現(xiàn)一些微妙的,難以調(diào)試的錯(cuò)誤。
另外,應(yīng)用邏輯和線(xiàn)程邏輯糾纏在一起,會(huì)導(dǎo)致程序的邏輯結(jié)構(gòu)混亂,難以復(fù)用和維護(hù)。本文試圖給出一個(gè)解決這個(gè)問(wèn)題的方案,通過(guò)構(gòu)建一個(gè)并發(fā)模型框架(framework),使得開(kāi)發(fā)多線(xiàn)程的應(yīng)用變得容易。
基礎(chǔ)知識(shí)
Java語(yǔ)言提供了對(duì)于線(xiàn)程很好的支持,實(shí)現(xiàn)方法小巧、優(yōu)雅。對(duì)于方法重入的保護(hù),信號(hào)量(semaphore)和臨界區(qū)(critical section)機(jī)制的實(shí)現(xiàn)都非常簡(jiǎn)潔?梢院苋菀椎膶(shí)現(xiàn)多線(xiàn)程間的同步操作從而保護(hù)關(guān)鍵數(shù)據(jù)的一致性。這些特點(diǎn)使得Java成為面向?qū)ο笳Z(yǔ)言中對(duì)于多線(xiàn)程特性支持方面的佼佼者(C++正在試圖把boost庫(kù)中的對(duì)于線(xiàn)程的支持部分納入語(yǔ)言標(biāo)準(zhǔn))。
Java中內(nèi)置了對(duì)于對(duì)象并發(fā)訪問(wèn)的支持,每一個(gè)對(duì)象都有一個(gè)監(jiān)視器(monitor),同時(shí)只允許一個(gè)線(xiàn)程持有監(jiān)視器從而進(jìn)行對(duì)對(duì)象的訪問(wèn),那些沒(méi)有獲得監(jiān)視器的線(xiàn)程必須等待直到持有監(jiān)視器的線(xiàn)程釋放監(jiān)視器。對(duì)象通過(guò)synchronized關(guān)鍵字來(lái)聲明線(xiàn)程必須獲得監(jiān)視器才能進(jìn)行對(duì)自己的訪問(wèn)。
synchronized聲明僅僅對(duì)于一些較為簡(jiǎn)單的線(xiàn)程間同步問(wèn)題比較有效,對(duì)于哪些復(fù)雜的同步問(wèn)題,比如帶有條件的同步問(wèn)題,Java提供了另外的解決方法,wait/notify/notifyAll。
獲得對(duì)象監(jiān)視器的線(xiàn)程可以通過(guò)調(diào)用該對(duì)象的wait方法主動(dòng)釋放監(jiān)視器,等待在該對(duì)象的線(xiàn)程等待隊(duì)列上,此時(shí)其他線(xiàn)程可以得到監(jiān)視器從而訪問(wèn)該對(duì)象,之后可以通過(guò)調(diào)用notify/notifyAll方法來(lái)喚醒先前因調(diào)用wait方法而等待的線(xiàn)程。
一般情況下,對(duì)于wait/notify/notifyAll方法的調(diào)用都是根據(jù)一定的條件來(lái)進(jìn)行的,比如:經(jīng)典的生產(chǎn)者/消費(fèi)者問(wèn)題中對(duì)于隊(duì)列空、滿(mǎn)的判斷。熟悉POSIX的讀者會(huì)發(fā)現(xiàn),使用wait/notify/notifyAll可以很容易的實(shí)現(xiàn)POSIX中的一個(gè)線(xiàn)程間的高級(jí)同步技術(shù):條件變量。
簡(jiǎn)單例子
本文將圍繞一個(gè)簡(jiǎn)單的例子展開(kāi)論述,這樣可以更容易突出我們解決問(wèn)題的思路、方法。本文想向讀者展現(xiàn)的正是這些思路、方法。這些思路、方法更加適用于解決大規(guī)模、復(fù)雜應(yīng)用中的并發(fā)問(wèn)題?紤]一個(gè)簡(jiǎn)單的例子,我們有一個(gè)服務(wù)提供者,它通過(guò)一個(gè)接口對(duì)外提供服務(wù),服務(wù)內(nèi)容非常簡(jiǎn)單,就是在標(biāo)準(zhǔn)輸出上打印Hello World。類(lèi)結(jié)構(gòu)圖如下:
代碼如下:
1.interface Service
2.{
3. public void sayHello();
4.}
5.class ServiceImp implements Service
6.{
7. public void sayHello() {
8. System.out.println("Hello World!");
9. }
10.}
11.class Client
12.{
13. public Client(Service s) {
14. _service = s;
15.}
16. public void requestService() {
17. _service.sayHello();
18. }
19. private Service _service;
20.}
如果現(xiàn)在有新的需求,要求該服務(wù)必須支持Client的并發(fā)訪問(wèn)。一種簡(jiǎn)單的方法就是在ServicImp類(lèi)中的每個(gè)方法前面加上synchronized聲明,來(lái)保證自己內(nèi)部數(shù)據(jù)的一致性(當(dāng)然對(duì)于本例來(lái)說(shuō),目前是沒(méi)有必要的,因?yàn)镾erviceImp沒(méi)有需要保護(hù)的數(shù)據(jù),但是隨著需求的變化,以后可能會(huì)有的)。但是這樣做至少會(huì)存在以下幾個(gè)問(wèn)題:
1.現(xiàn)在要維護(hù)ServiceImp的兩個(gè)版本:多線(xiàn)程版本和單線(xiàn)程版本(有些地方,比如其他項(xiàng)目,可能沒(méi)有并發(fā)的問(wèn)題),容易帶來(lái)同步更新和正確選擇版本的問(wèn)題,給維護(hù)帶來(lái)麻煩。
2.如果多個(gè)并發(fā)的Client頻繁調(diào)用該服務(wù),由于是直接同步調(diào)用,會(huì)造成Client阻塞,降低服務(wù)質(zhì)量。
3.很難進(jìn)行一些靈活的控制,比如:根據(jù)Client的優(yōu)先級(jí)進(jìn)行排隊(duì)等等。
4.這些問(wèn)題對(duì)于大型的多線(xiàn)程應(yīng)用服務(wù)器尤為突出,對(duì)于一些簡(jiǎn)單的應(yīng)用(如本文中的例子)可能根本不用考慮。本文正是要討論這些問(wèn)題的解決方案,文中的簡(jiǎn)單的例子只是提供了一個(gè)說(shuō)明問(wèn)題,展示思路、方法的平臺(tái)。
5.如何才能較好的解決這些問(wèn)題,有沒(méi)有一個(gè)可以重用的解決方案呢?讓我們先把這些問(wèn)題放一放,先來(lái)談?wù)労涂蚣苡嘘P(guān)的一些問(wèn)題。
框架概述
熟悉面向?qū)ο蟮淖x者一定知道面向?qū)ο蟮淖畲蟮膬?yōu)勢(shì)之一就是:軟件復(fù)用。通過(guò)復(fù)用,可以減少很多的工作量,提高軟件開(kāi)發(fā)生產(chǎn)率。復(fù)用本身也是分層次的,代碼級(jí)的復(fù)用和設(shè)計(jì)架構(gòu)的復(fù)用。
大家可能非常熟悉C語(yǔ)言中的一些標(biāo)準(zhǔn)庫(kù),它們提供了一些通用的功能讓你的程序使用。但是這些標(biāo)準(zhǔn)庫(kù)并不能影響你的程序結(jié)構(gòu)和設(shè)計(jì)思路,僅僅是提供一些機(jī)能,幫助你的程序完成工作。它們使你不必重頭編寫(xiě)一般性的通用功能(比如printf),它們強(qiáng)調(diào)的是程序代碼本身的復(fù)用性,而不是設(shè)計(jì)架構(gòu)的復(fù)用性。
那么什么是框架呢?所謂框架,它不同于一般的標(biāo)準(zhǔn)庫(kù),是指一組緊密關(guān)聯(lián)的(類(lèi))classes,強(qiáng)調(diào)彼此的配合以完成某種可以重復(fù)運(yùn)用的設(shè)計(jì)概念。這些類(lèi)之間以特定的方式合作,彼此不可或缺。它們相當(dāng)程度的影響了你的程序的形貌?蚣鼙旧硪(guī)劃了應(yīng)用程序的骨干,讓程序遵循一定的流程和動(dòng)線(xiàn),展現(xiàn)一定的風(fēng)貌和功能。這樣就使程序員不必費(fèi)力于通用性的功能的繁文縟節(jié),集中精力于專(zhuān)業(yè)領(lǐng)域。
有一點(diǎn)必須要強(qiáng)調(diào),放之四海而皆準(zhǔn)的框架是不存在的,也是最沒(méi)有用處的?蚣芡际轻槍(duì)某個(gè)特定應(yīng)用領(lǐng)域的,是在對(duì)這個(gè)應(yīng)用領(lǐng)域進(jìn)行深刻理解的基礎(chǔ)上,抽象出該應(yīng)用的概念模型,在這些抽象的概念上搭建的一個(gè)模型,是一個(gè)有形無(wú)體的框架。不同的具體應(yīng)用根據(jù)自身的特點(diǎn)對(duì)框架中的抽象概念進(jìn)行實(shí)現(xiàn),從而賦予框架生命,完成應(yīng)用的功能。
基于框架的應(yīng)用都有兩部分構(gòu)成:框架部分和特定應(yīng)用部分。要想達(dá)到框架復(fù)用的目標(biāo),必須要做到框架部分和特定應(yīng)用部分的隔離。使用面向?qū)ο蟮囊粋(gè)強(qiáng)大功能:多態(tài),可以實(shí)現(xiàn)這一點(diǎn)。在框架中完成抽象概念之間的交互、關(guān)聯(lián),把具體的實(shí)現(xiàn)交給特定的應(yīng)用來(lái)完成。其中一般都會(huì)大量使用了Template Method設(shè)計(jì)模式。Java中的Collection Framework以及微軟的MFC都是框架方面很好的例子。有興趣的讀者可以自行研究。
構(gòu)建框架
如何構(gòu)建一個(gè)Java并發(fā)模型框架呢?讓我們先回到原來(lái)的問(wèn)題,先來(lái)分析一下原因。造成要維護(hù)多線(xiàn)程和單線(xiàn)程兩個(gè)版本的原因是由于把應(yīng)用邏輯和并發(fā)邏輯混在一起,如果能夠做到把應(yīng)用邏輯和并發(fā)模型進(jìn)行很好的隔離,那么應(yīng)用邏輯本身就可以很好的被復(fù)用,而且也很容易把并發(fā)邏輯添加進(jìn)來(lái)而不會(huì)對(duì)應(yīng)用邏輯造成任何影響。造成Client阻塞,性能降低以及無(wú)法進(jìn)行額外的控制的原因是由于所有的服務(wù)調(diào)用都是同步的,解決方案很簡(jiǎn)單,改為異步調(diào)用方式,把服務(wù)的調(diào)用和服務(wù)的執(zhí)行分離。
首先來(lái)介紹一個(gè)概念,活動(dòng)對(duì)象(Active Object)。所謂活動(dòng)對(duì)象是相對(duì)于被動(dòng)對(duì)象(passive object)而言的,被動(dòng)對(duì)象的方法的調(diào)用和執(zhí)行都是在同一個(gè)線(xiàn)程中的,被動(dòng)對(duì)象方法的調(diào)用是同步的、阻塞的,一般的對(duì)象都屬于被動(dòng)對(duì)象;主動(dòng)對(duì)象的方法的調(diào)用和執(zhí)行是分離的,主動(dòng)對(duì)象有自己獨(dú)立的執(zhí)行線(xiàn)程,主動(dòng)對(duì)象的方法的調(diào)用是由其他線(xiàn)程發(fā)起的,但是方法是在自己的線(xiàn)程中執(zhí)行的,主動(dòng)對(duì)象方法的調(diào)用是異步的,非阻塞的。
本框架的核心就是使用主動(dòng)對(duì)象來(lái)封裝并發(fā)邏輯,然后把Client的請(qǐng)求轉(zhuǎn)發(fā)給實(shí)際的服務(wù)提供者(應(yīng)用邏輯),這樣無(wú)論是Client還是實(shí)際的服務(wù)提供者都不用關(guān)心并發(fā)的存在,不用考慮并發(fā)所帶來(lái)的數(shù)據(jù)一致性問(wèn)題。從而實(shí)現(xiàn)應(yīng)用邏輯和并發(fā)邏輯的隔離,服務(wù)調(diào)用和服務(wù)執(zhí)行的隔離。下面給出關(guān)鍵的實(shí)現(xiàn)細(xì)節(jié)。
本框架有如下幾部分構(gòu)成:
1.一個(gè)ActiveObject類(lèi),從Thread繼承,封裝了并發(fā)邏輯的活動(dòng)對(duì)象;
2.一個(gè)ActiveQueue類(lèi),主要用來(lái)存放調(diào)用者請(qǐng)求;
3.一個(gè)MethodRequest接口,主要用來(lái)封裝調(diào)用者的請(qǐng)求,Command設(shè)計(jì)模式的一種實(shí)現(xiàn)方式。它們的一個(gè)簡(jiǎn)單的實(shí)現(xiàn)如下:
1. //MethodRequest接口定義
2. interface MethodRequest
3.{
4. public void call();
5.}
6.//ActiveQueue定義,其實(shí)就是一個(gè)producer/consumer隊(duì)列
7. class ActiveQueue
8.{
9. public ActiveQueue() {
10. _queue = new Stack();
11. }
12. public synchronized void enqueue(MethodRequest mr) {
13. while(_queue.size() > QUEUE_SIZE) {
- 1. 多線(xiàn)程開(kāi)發(fā)的捷徑:構(gòu)建Java并發(fā)模型框架
- 2. Java包導(dǎo)入機(jī)制的研究與解析
- 3. JAVA中的main函數(shù)詳細(xì)講解
- 4. Java繼承和接口的區(qū)別
- 5. Java Eclipse反編譯配置
- 6. Java schedule與scheduleAtFixRate區(qū)別
- 7. Java控制臺(tái)打印九九乘法表
- 8. 通過(guò)JNI實(shí)現(xiàn)Java和C++的相互調(diào)用
- 9. Java中Json格式數(shù)據(jù)的應(yīng)用
- 10. Java回調(diào)函數(shù)