曹大有
CAO Da-you
(鄖陽師范高等??茖W(xué)校 計(jì)算機(jī)科學(xué)系,丹江口 442700)
數(shù)據(jù)流模型很早就被提了出來,一般一個(gè)數(shù)據(jù)流程序由多個(gè)actor 組成。傳統(tǒng)的細(xì)粒度數(shù)據(jù)流模型中,actor 的粒度是一個(gè)操作,而在粗粒度的數(shù)據(jù)流模型中,actor 的粒度可以是一個(gè)函數(shù)。actor 之間只能通過先入先出的緩沖隊(duì)列進(jìn)行通信。每個(gè)actor有一個(gè)相應(yīng)的觸發(fā)規(guī)則(firing rule)集合,當(dāng)其中某一規(guī)則滿足時(shí),該actor被觸發(fā),讀取輸入隊(duì)列上的數(shù)據(jù),產(chǎn)生輸出數(shù)據(jù)。actor 是沒有內(nèi)部狀態(tài)的,它的行為只由輸入數(shù)據(jù)和觸發(fā)規(guī)則決定。類似的模型還有進(jìn)程網(wǎng)絡(luò)(process network)。每個(gè)進(jìn)程是一小段串行程序,進(jìn)程之間只能通過先入先出的緩沖隊(duì)列進(jìn)行同步和通信。當(dāng)一個(gè)進(jìn)程讀一個(gè)空隊(duì)列或者寫一個(gè)滿隊(duì)列時(shí),它會被阻塞,直到操作完成。
在數(shù)據(jù)流和進(jìn)程網(wǎng)絡(luò)模型中,各個(gè)運(yùn)行單元之間的同步和通信是通過顯式的數(shù)據(jù)傳遞來完成的。由于禁止了運(yùn)行單元之間的隱式數(shù)據(jù)共享,避免了多線程模型的數(shù)據(jù)競爭和沖突,有利于程序的形式化分析和驗(yàn)證。數(shù)據(jù)流模型能夠幫助程序員自然地表達(dá)應(yīng)用程序的內(nèi)部并行性,減少編譯器并行化分析和優(yōu)化的難度。
數(shù)據(jù)流Java中最小的獨(dú)立運(yùn)行的單元叫做組件(component),它對應(yīng)于我們通常的進(jìn)程或線程。組件內(nèi)只能串行執(zhí)行。一個(gè)數(shù)據(jù)流 Java 程序可以擁有多個(gè)組件,各個(gè)組件之間可以獨(dú)立運(yùn)行。
組件可以定義自己的輸入和輸出端口(port),用于和外部通信。組件之間的顯式數(shù)據(jù)通信只能通過輸入和輸出端口之間進(jìn)行。通信時(shí)數(shù)據(jù)對象的發(fā)送和接收是異步的、先入先出的。當(dāng)某個(gè)組件通過一個(gè)輸出端口對多個(gè)組件的普通輸入端口發(fā)送數(shù)據(jù)對象時(shí),可以有兩種發(fā)送方式:將數(shù)據(jù)對象復(fù)制多個(gè)副本后發(fā)送到所有組件;或者以輪轉(zhuǎn)方式依次發(fā)送。如果是對多個(gè)組件的參數(shù)端口發(fā)送數(shù)據(jù)對象,那么也可以有兩種方式:將數(shù)據(jù)對象的引用發(fā)送給所有組件共享;或者以復(fù)制的方式發(fā)送。數(shù)據(jù)流 Java 采用隱式多線程模型,程序員需要知道組件運(yùn)行時(shí)可能有多個(gè)副本同時(shí)運(yùn)行。如果訪問參數(shù)端口傳遞的共享的數(shù)據(jù)對象,則需要保證操作是原子的。
反射是指一個(gè)系統(tǒng)表述和改變自身行為的能力[2],反射機(jī)制允許程序運(yùn)行時(shí)動態(tài)地加載一個(gè)類,生成該對象的實(shí)例和調(diào)用該實(shí)例的方法。下面我們就以數(shù)據(jù)流多態(tài)例,利用Java提供的反射機(jī)制來實(shí)現(xiàn)數(shù)據(jù)流Java的多態(tài)性。
設(shè)計(jì)出Java的類Shape、Rectangle和Triangle。然后針對類繼承體系中的每一個(gè)類設(shè)計(jì)出對應(yīng)的數(shù)據(jù)流Java的組件類,以類Rectangle為例,對應(yīng)的數(shù)據(jù)流Java的組件類為:
下面我們就要來研究如何利用Java語言提供的反射機(jī)制來實(shí)現(xiàn)數(shù)據(jù)流Java多態(tài)性的組件類,該組件類類名我們就命名為PolymorphismComponent。由于在該類體系中每個(gè)類只有一個(gè)構(gòu)造方法且該構(gòu)造方法有兩個(gè)double型的參數(shù),所以對組件類PolymorphismComponent的輸入?yún)?shù)類型設(shè)為形如"12.3,45.6,Shape"的String類型,其中12.3,45.6為構(gòu)造方法提供參數(shù),Shape為具體類名。
在組件類PolymorphismComponent的execute()方法中,首先通過輸入端口的receive()方法接收數(shù)據(jù),然后分離出需要動態(tài)加載的類名classStr,再通過以下過程:
來動態(tài)生成對象obj,其中parts[0]和parts[1]為構(gòu)造方法的參數(shù).對象obj生成后,再通過以下條件語句進(jìn)行數(shù)據(jù)的發(fā)送:
上面的條件語句實(shí)際上是在組件類PolymorphismComponent中維護(hù)了一個(gè)稱之為分派樹(dispatch tree)的數(shù)據(jù)結(jié)構(gòu),分派樹在運(yùn)行時(shí)構(gòu)建網(wǎng)絡(luò)的時(shí)候創(chuàng)建。輸入端口類型必須是輸出端口類型本身或者其子類,一個(gè)類型只允許有一個(gè)端口。運(yùn)行時(shí),系統(tǒng)根據(jù)類的層次關(guān)系建立一個(gè)反向的樹結(jié)構(gòu)。樹的每個(gè)節(jié)點(diǎn)記錄它的類型和對應(yīng)的出口。輸出端口在發(fā)送數(shù)據(jù)對象時(shí),根據(jù)分派樹從上向下進(jìn)行匹配,然后向匹配的端口進(jìn)行發(fā)送。這樣該組件類的主要邏輯就設(shè)計(jì)好了,至于一個(gè)輸入端口可在@InPort()中設(shè)計(jì)完成,多個(gè)輸出端口可在@OutPorts()中設(shè)計(jì)完成,最后通過方法openPorts()打開即可。
測試工作可在Network類的define()方法中完成.先用component()方法給組件類命名:
然后用connect()方法進(jìn)行組件類之間的連接:
最后用initialize()方法給組件類Polymorphism Component提供初始參數(shù):
由于初始參數(shù)中指定的類名為Shape,所以輸出的是Shape的面積;當(dāng)將類名換為Rectangle,輸出的是Rectangle的面積;當(dāng)將類名換為Triangle,輸出的是Triangle的面積.
若現(xiàn)在要增設(shè)Circle類,只需為Circle類開發(fā)一個(gè)對應(yīng)的組件類Circle Component,然后在Network類的define()方法中加上以下語句即可:
再在由條件語句組成的稱之為分派樹的數(shù)據(jù)結(jié)構(gòu)加上對Circle類對象實(shí)例的判斷即可,這樣當(dāng)將initialize()方法中的類名換為Circle,輸出的是Circle的面積。
本文通過Java語言提供的反射機(jī)制和類動態(tài)加載機(jī)制實(shí)現(xiàn)了數(shù)據(jù)流Java多態(tài)性的并行程序設(shè)計(jì)模型,并通過實(shí)例對該模型進(jìn)行了驗(yàn)證.從實(shí)現(xiàn)的過程中可以看出:該過程具有一定的通用性和實(shí)用性,有利于數(shù)據(jù)流Java程序的并行性和模塊化.實(shí)現(xiàn)的重點(diǎn)是稱之為分派樹的數(shù)據(jù)結(jié)構(gòu).
[1]劉弢,范彬,吳承勇,張兆慶.數(shù)據(jù)流 Java 并行程序設(shè)計(jì)模型的設(shè)計(jì)、實(shí)現(xiàn)及運(yùn)行時(shí)優(yōu)化[J].軟件學(xué)報(bào),2009,19(9):2184-2185.
[2]程峰,黃若波,章恒翀.Java2核心技術(shù)卷I:基礎(chǔ)知識[M].北京:機(jī)械工業(yè)出版社,2004:158-190.
[3]Flow·based programming[EB/OL].http://www.Jpaulmorrison.corn/fbp/.