曹大有
CAO Da-you
(鄖陽師范高等專科學(xué)校 計算機科學(xué)系,丹江口 442700)
數(shù)據(jù)流模型很早就被提了出來,一般一個數(shù)據(jù)流程序由多個actor 組成。傳統(tǒng)的細(xì)粒度數(shù)據(jù)流模型中,actor 的粒度是一個操作,而在粗粒度的數(shù)據(jù)流模型中,actor 的粒度可以是一個函數(shù)。actor 之間只能通過先入先出的緩沖隊列進(jìn)行通信。每個actor有一個相應(yīng)的觸發(fā)規(guī)則(firing rule)集合,當(dāng)其中某一規(guī)則滿足時,該actor被觸發(fā),讀取輸入隊列上的數(shù)據(jù),產(chǎn)生輸出數(shù)據(jù)。actor 是沒有內(nèi)部狀態(tài)的,它的行為只由輸入數(shù)據(jù)和觸發(fā)規(guī)則決定。類似的模型還有進(jìn)程網(wǎng)絡(luò)(process network)。每個進(jìn)程是一小段串行程序,進(jìn)程之間只能通過先入先出的緩沖隊列進(jìn)行同步和通信。當(dāng)一個進(jìn)程讀一個空隊列或者寫一個滿隊列時,它會被阻塞,直到操作完成。
在數(shù)據(jù)流和進(jìn)程網(wǎng)絡(luò)模型中,各個運行單元之間的同步和通信是通過顯式的數(shù)據(jù)傳遞來完成的。由于禁止了運行單元之間的隱式數(shù)據(jù)共享,避免了多線程模型的數(shù)據(jù)競爭和沖突,有利于程序的形式化分析和驗證。數(shù)據(jù)流模型能夠幫助程序員自然地表達(dá)應(yīng)用程序的內(nèi)部并行性,減少編譯器并行化分析和優(yōu)化的難度。
數(shù)據(jù)流Java中最小的獨立運行的單元叫做組件(component),它對應(yīng)于我們通常的進(jìn)程或線程。組件內(nèi)只能串行執(zhí)行。一個數(shù)據(jù)流 Java 程序可以擁有多個組件,各個組件之間可以獨立運行。
組件可以定義自己的輸入和輸出端口(port),用于和外部通信。組件之間的顯式數(shù)據(jù)通信只能通過輸入和輸出端口之間進(jìn)行。通信時數(shù)據(jù)對象的發(fā)送和接收是異步的、先入先出的。當(dāng)某個組件通過一個輸出端口對多個組件的普通輸入端口發(fā)送數(shù)據(jù)對象時,可以有兩種發(fā)送方式:將數(shù)據(jù)對象復(fù)制多個副本后發(fā)送到所有組件;或者以輪轉(zhuǎn)方式依次發(fā)送。如果是對多個組件的參數(shù)端口發(fā)送數(shù)據(jù)對象,那么也可以有兩種方式:將數(shù)據(jù)對象的引用發(fā)送給所有組件共享;或者以復(fù)制的方式發(fā)送。數(shù)據(jù)流 Java 采用隱式多線程模型,程序員需要知道組件運行時可能有多個副本同時運行。如果訪問參數(shù)端口傳遞的共享的數(shù)據(jù)對象,則需要保證操作是原子的。
反射是指一個系統(tǒng)表述和改變自身行為的能力[2],反射機制允許程序運行時動態(tài)地加載一個類,生成該對象的實例和調(diào)用該實例的方法。下面我們就以數(shù)據(jù)流多態(tài)例,利用Java提供的反射機制來實現(xiàn)數(shù)據(jù)流Java的多態(tài)性。
設(shè)計出Java的類Shape、Rectangle和Triangle。然后針對類繼承體系中的每一個類設(shè)計出對應(yīng)的數(shù)據(jù)流Java的組件類,以類Rectangle為例,對應(yīng)的數(shù)據(jù)流Java的組件類為:

下面我們就要來研究如何利用Java語言提供的反射機制來實現(xiàn)數(shù)據(jù)流Java多態(tài)性的組件類,該組件類類名我們就命名為PolymorphismComponent。由于在該類體系中每個類只有一個構(gòu)造方法且該構(gòu)造方法有兩個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ā)送:


上面的條件語句實際上是在組件類PolymorphismComponent中維護(hù)了一個稱之為分派樹(dispatch tree)的數(shù)據(jù)結(jié)構(gòu),分派樹在運行時構(gòu)建網(wǎng)絡(luò)的時候創(chuàng)建。輸入端口類型必須是輸出端口類型本身或者其子類,一個類型只允許有一個端口。運行時,系統(tǒng)根據(jù)類的層次關(guān)系建立一個反向的樹結(jié)構(gòu)。樹的每個節(jié)點記錄它的類型和對應(yīng)的出口。輸出端口在發(fā)送數(shù)據(jù)對象時,根據(jù)分派樹從上向下進(jìn)行匹配,然后向匹配的端口進(jìn)行發(fā)送。這樣該組件類的主要邏輯就設(shè)計好了,至于一個輸入端口可在@InPort()中設(shè)計完成,多個輸出端口可在@OutPorts()中設(shè)計完成,最后通過方法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ā)一個對應(yīng)的組件類Circle Component,然后在Network類的define()方法中加上以下語句即可:

再在由條件語句組成的稱之為分派樹的數(shù)據(jù)結(jié)構(gòu)加上對Circle類對象實例的判斷即可,這樣當(dāng)將initialize()方法中的類名換為Circle,輸出的是Circle的面積。
本文通過Java語言提供的反射機制和類動態(tài)加載機制實現(xiàn)了數(shù)據(jù)流Java多態(tài)性的并行程序設(shè)計模型,并通過實例對該模型進(jìn)行了驗證.從實現(xiàn)的過程中可以看出:該過程具有一定的通用性和實用性,有利于數(shù)據(jù)流Java程序的并行性和模塊化.實現(xiàn)的重點是稱之為分派樹的數(shù)據(jù)結(jié)構(gòu).
[1]劉弢,范彬,吳承勇,張兆慶.數(shù)據(jù)流 Java 并行程序設(shè)計模型的設(shè)計、實現(xiàn)及運行時優(yōu)化[J].軟件學(xué)報,2009,19(9):2184-2185.
[2]程峰,黃若波,章恒翀.Java2核心技術(shù)卷I:基礎(chǔ)知識[M].北京:機械工業(yè)出版社,2004:158-190.
[3]Flow·based programming[EB/OL].http://www.Jpaulmorrison.corn/fbp/.