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