用Real-Time Java編寫Real-Time程序(by Cocia Lin) 概述Real-Time Specification for Java縮寫就是RTSJ。RTSJ是Java的適應(yīng)實(shí)時(shí)計(jì)算要求而開發(fā)。關(guān)于對(duì)實(shí)時(shí)系統(tǒng)的介紹和特性說明,請(qǐng)參見其他文章,這里重點(diǎn)是Java針對(duì)實(shí)時(shí)系統(tǒng)開發(fā)所做的改進(jìn)做詳細(xì)的介紹。 RTSJ在6各方面對(duì)Java做了增強(qiáng): 1.增加實(shí)時(shí)線程。實(shí)時(shí)線程提供了比普通線程更完善和細(xì)致的控制屬性和操作內(nèi)容,例如更大的優(yōu)先級(jí)范圍,控制內(nèi)存分配等等; 2.增加了新的工具類和編程方式,比如內(nèi)存工具類。利用這些工具類,可以完成不需要垃圾回收的Java程序; 3.增加了異步事件處理器類和相應(yīng)的機(jī)制。把異步事件和jvm外界發(fā)生的事件直接關(guān)聯(lián)起來; 4.增加了異步傳輸?shù)目刂茩C(jī)制,允許一個(gè)線程更改另一個(gè)線程中的控制流。也就是說,可以中斷或者繼續(xù)另一個(gè)線程的運(yùn)行; 5.增加一種機(jī)制,能夠控制對(duì)象分配到內(nèi)存的位置; 6.增加一種機(jī)制,能夠訪問特定地址的內(nèi)存。 RTSJ對(duì)傳統(tǒng)java的增強(qiáng)的過程中本著一個(gè)原則,就是不改變?cè)械膉ava的任何語法。這樣做的目的就是保持原有java的連續(xù)。也就是說,在普通 jvm上能夠運(yùn)行的程序,理論上,可以完全相同的在rt jvm上運(yùn)行;反過來則不行。之所以強(qiáng)調(diào)是在理論上情形,原因是現(xiàn)有的rt jvm還沒有實(shí)現(xiàn)完全的java類庫,比如awt,swing,text等。而一些常用的lang,util,io等都有實(shí)現(xiàn)。這也是現(xiàn)有的rt jvm選擇實(shí)現(xiàn)的優(yōu)先級(jí)別。 對(duì)于rtsj的核心和基礎(chǔ),我認(rèn)為有以下的幾點(diǎn): 1.領(lǐng)域內(nèi)存 Scope Memory 2.實(shí)時(shí)線程 Realtime Thread 3.高精度時(shí)間 High Resolution Time 這當(dāng)然不是說,其他的不重要。其他的rtsj能力就像awt對(duì)你的重要程度一樣,如果你在作客戶界面程序,那awt對(duì)你就很重要;如果你在做servlet,jsp等形式的j2ee程序,awt可能對(duì)你完全沒有意義。所以這里只介紹核心和基礎(chǔ)的幾點(diǎn)內(nèi)容。 領(lǐng)域內(nèi)存 Scope MemoryRtsj增加領(lǐng)域內(nèi)存根本目的就是解決jvm的垃圾收集問題。我們知道,jvm的垃圾收集機(jī)制具有 不確定性。例如,我們不知道jvm什么時(shí)候?qū)⒁M(jìn)行垃圾收集,收集操作需要多長時(shí)間,收集線程本身占用多少內(nèi)存,多少cpu時(shí)間,是否有其他的i/o資源 占用等等。而實(shí)時(shí)應(yīng)用程序的一個(gè)很重要的指標(biāo)就是,能夠預(yù)計(jì)一個(gè)操作的準(zhǔn)確時(shí)間,占用的資源。rtsj要能夠在實(shí)時(shí)應(yīng)用中得到應(yīng)用,首先就要解決垃圾收集 這個(gè)頭疼的問題。Rtsj并沒有放棄垃圾收集機(jī)制,如果真的這樣做,也是不可接受的。Java能夠存在和發(fā)展到現(xiàn)在的這個(gè)地步,頭號(hào)功臣可能就是這種機(jī)制。它讓 開發(fā)者從細(xì)節(jié)內(nèi)存管理的泥潭中解放出來。如果去掉這種機(jī)制,rtsj和c++又有什么區(qū)別?rtsj的做法是,讓有實(shí)時(shí)要求的代碼使用領(lǐng)域內(nèi)存機(jī)制,避開 垃圾收集,而沒有此項(xiàng)要求的代碼,和標(biāo)準(zhǔn)的java一樣書寫,享受垃圾收集給你帶來的快樂。對(duì)我來說,可以自由的用碗來吃飯,而不用自己來刷碗,應(yīng)該不算 一件壞事。 Rtsj的這種做法,是基于這樣的兩條結(jié)論:不產(chǎn)生垃圾的代碼不會(huì)導(dǎo)致請(qǐng)求式的垃圾收集;不引用堆中對(duì)象的代碼可以搶占垃圾收集器的線程。 領(lǐng)域內(nèi)存的機(jī)制,其實(shí)就是內(nèi)存管理的機(jī)制,開發(fā)者自己管理內(nèi)存的機(jī)制。對(duì)照上段的文字,你可能有了疑問:需要我自己管理內(nèi)存,這又和我使用c++ 有什么區(qū)別?這個(gè)問題的答案,不同的人理解不同,最終結(jié)論就會(huì)有所不同。我的理解是,rtsj提供了比c++簡單的和高級(jí)別的內(nèi)存管理方式,是開發(fā)者學(xué)習(xí) 幾個(gè)工具類和一種編程風(fēng)格,就能實(shí)現(xiàn)與c++等相近的效率和功能。你的理解是什么呢?這和Java公認(rèn)的比c++慢很多倍,但卻被人們普遍接受的情況,有 著異曲同工之處。 讀者臉色有變,現(xiàn)在進(jìn)入正題。 為了實(shí)現(xiàn)內(nèi)存管理,rtsj提供了一組內(nèi)存管理類,和一種編程方法,或者說的編程規(guī)范。
圖 1 內(nèi)存管理類 ImmortalMemory的中文名字是不朽內(nèi)存,他是靜態(tài)的,或者說是與jvm共存亡的。只要jvm在運(yùn)行,不朽內(nèi)存就存在,并且可 用。他和static靜態(tài)的對(duì)象行為一樣。存放在不朽內(nèi)存中的對(duì)象不會(huì)被垃圾收集。但是要當(dāng)心,不朽內(nèi)存不會(huì)被回收,所以在其中放入太多的東西,會(huì)導(dǎo)致系 統(tǒng)內(nèi)存耗盡。所以,在有其他選擇的情況下,不要使用不朽內(nèi)存。 HeapMemory中文名字是堆內(nèi)存。他和我們平時(shí)在java程序中使用new 這個(gè)關(guān)鍵字生成對(duì)象的行為是一樣的。其中的對(duì)象數(shù)據(jù)在無用的時(shí)候會(huì)被垃圾回收。 ScopeMemory就是領(lǐng)域內(nèi)存。他有各種不同的實(shí)現(xiàn)。這里只拿出兩個(gè)典型的,并且已經(jīng)被現(xiàn)有的幾種rtjvm支持的領(lǐng)域內(nèi)存實(shí)現(xiàn): LTMemory和VTMemory。這兩者的區(qū)別,是在他們分配存儲(chǔ)空間的時(shí)間要求上,LTMemory隨著分配空間的增加,分配的時(shí)間以固定的線性增 長。在rtsj規(guī)范中,沒有說明VTMemory相對(duì)LTMemory的任何優(yōu)點(diǎn),但規(guī)范對(duì)LTMemory的分配時(shí)間有嚴(yán)格要求,而對(duì)VTMemory 沒有。所以可以簡單的這樣理解:LTMemory分配時(shí)間很短,但內(nèi)存使用效率可能不高;而VTMemory分配時(shí)間可能長一點(diǎn),但內(nèi)存使用效率可能會(huì) 高。個(gè)人認(rèn)為,現(xiàn)階段不用考慮他們的具體區(qū)別,全部使用LTMemory也不會(huì)有任何問題,我以我的房東的房子做保證。 下面提供一段代碼,示范一下,使用領(lǐng)域內(nèi)存的情景。 import javax.realtime.*; public class Foo { class Bar implements Runnable{ // run()在運(yùn)行sm.enter(this)時(shí)被調(diào)用 public void run(){ //newObject 被分配在LTMemory sm中 Object newObject = new Object(); }//方法退出后,newObject對(duì)象的空間立即釋放 } void createSomeObjects() { //分配1024字節(jié)空間 ScopeMemory sm = new LTMemory(1024, 1024); // 1.直接分配 Bar b = (Bar)sm.newInstance(Bar.class); // 2.進(jìn)入領(lǐng)域 sm.enter(b); }//在這里,垃圾收集器準(zhǔn)備回收sm // //這樣的情形下,sm.enter()方法中分配的對(duì)象, //在運(yùn)行期間即使引用懸空,也不會(huì)被垃圾收集, //而在此方法退出后,其中所有的分配空間被立即釋放, //也不通過垃圾收集機(jī)制。 Public static void main(String[] args){ :Foo foo = new Foo(); Foo. createSomeObjects(); } } 這個(gè)例子比較簡陋,但他是完整的,并且說明了rtsj編寫程序的幾個(gè)兩個(gè)基本要求:內(nèi)存管理類的使用和程序編寫風(fēng)格要求。內(nèi)存管理類在前面的例子 程序的注釋中有所介紹。而所謂程序編寫風(fēng)格,其實(shí)就是sm.enter(b)的使用的方式,包括Bar需要實(shí)現(xiàn)run方法。這樣的風(fēng)格稱做 closure,中文為閉包。閉包的意思是,一個(gè)程序段相對(duì)的獨(dú)立和封閉,這樣的程序段可以很容易的控制內(nèi)存這樣的資源的分配控制。比如,我在進(jìn)入這個(gè)程 序段時(shí)準(zhǔn)備好內(nèi)存空間,例子中的分配空間是1024字節(jié),在退出這個(gè)程序段時(shí)將這些內(nèi)存釋放掉,因?yàn)樵陂]包的環(huán)境下,相當(dāng)于告訴jvm,過了這段程序,這 些內(nèi)存空間中的對(duì)象對(duì)我已經(jīng)沒有用了。 我們來假想一下sm.enter(b)中的情形: enter( b ){ prepareResource(b); b.run(); releaseResource(b); } 對(duì)于內(nèi)存管理,淺嘗則止,只說到概念。這些內(nèi)容可能還不足以開始真正的工作,還有很多內(nèi)存管理方面內(nèi)容沒有說明。更詳盡的內(nèi)容描述可能在其它的更深入的資料中找到。 實(shí)時(shí)線程 Realtime Thread在介紹RealtimeThread之前,我們先來看一個(gè)HelloWorld.Import javax.realtime.*; Public class Hello{ Public static void main(String[] args){ RealtimeThread rt = new RealtimeThread(){ Public void run(){ System.out.println("Hello RealTime World!"); } }; //準(zhǔn)入控制,分析可行性。如果發(fā)現(xiàn)此線程某些資源不能得到,則退出。 //注意:分析可行性是一項(xiàng)可選功能,有的rtsj jvm可能沒有實(shí)現(xiàn), //但是這個(gè)特性確實(shí)很吸引人。 if(!rt.getScheduler().isFeasible()){ System.out.println("rt thread is not feasible.\nsorry!"); System.exit(1); }else{ rt.start(); } // try{ rt.join(); }catch(InterruptedException e){} System.exit(0); } }
圖 2 實(shí)時(shí)線程類 從上圖中可以看出,RealtimeThread擴(kuò)展了Thread,也就是說,他具有了普通Thread所有的特性和功能。 說到線程,就不得不說到線程的優(yōu)先級(jí)了。普通java的線程有1-10,10個(gè)優(yōu)先級(jí),而rtsj的線程有32個(gè)優(yōu)先級(jí)。而且只要是RealtimeThread的優(yōu)先級(jí),肯定比普通Thread優(yōu)先級(jí)高。換句話說,RealtimeThread優(yōu)先級(jí)肯定大于10。 RealtimeThread相對(duì)于Thread,增加了更多的細(xì)致的控制能力,這些從RealtimeThread的構(gòu)造函數(shù)中,就能看得出來。 public RealtimeThread(SchedulingParameters scheduling, ReleaseParameters release, MemoryParameters memory, MemoryArea area, ProcessingGroupParameters group, java.lang.Runnable logic) 1.SchedulingParameters 調(diào)度參數(shù):此參數(shù)中可能包含優(yōu)先級(jí)信息,此線程可能就按照這個(gè)優(yōu)先級(jí)運(yùn)行; 2.ReleaseParameters 釋放參數(shù):釋放參數(shù)在線程使用waitForNextPeriod方法和要求使用準(zhǔn)入控制的時(shí)候起作用; 3.MemoryParameters 內(nèi)存參數(shù):此參數(shù)可以約束線程對(duì)領(lǐng)域內(nèi)存的使用。 4.MemoryArea 內(nèi)存區(qū)域:可選擇的有,堆,不朽內(nèi)存,領(lǐng)域內(nèi)存; 5.ProcessingGroupParameters 處理組:用來控制非周期性的活動(dòng); 6.Runnable 邏輯:也就是Runnable對(duì)象。 這六個(gè)參數(shù)這樣介紹,真是糊里糊涂?,F(xiàn)在用一個(gè)例子,看看能不能幫助你。 //Note:此代碼引自<實(shí)時(shí)Java平臺(tái)編程>,page 117 import javax.realtime.*; public class FullConstr{ public static void main(String[] args){ //1.將優(yōu)先級(jí)信息封裝在此參數(shù)中 SchedulingParameters scheduling = new PriorityParameters( PriorityScheduler.MIN_PRIORITY + 20 ); //在線程執(zhí)行完一個(gè)周期之后,也就是waitForNextPeriod時(shí) //會(huì)使用此參數(shù)中的信息。這里全部用null構(gòu)造,就是不起作用 ReleaseParameters release = new AperiodicParameters( null,null,null,null ); //指定內(nèi)存使用的類型 MemoryParameters memory = new MemoryParameters( MemoryParameters.NO_MAX,//Heap 0);//Immortal memory //內(nèi)存區(qū)域。這里使用了堆內(nèi)存,和我們使用new沒有區(qū)別 MemoryArea area = HeapMemory.instance(); //這里不討論此參數(shù)。雖然不能靠這個(gè)參數(shù)幫助你, //但我保證,特不會(huì)妨礙你。 ProcessingGroupParameters group = null; //這個(gè)參數(shù)熟悉吧,就是我們要運(yùn)行的代碼。 //這里使用MyThread,假設(shè)已經(jīng)準(zhǔn)備好了要運(yùn)行的代碼。 Runnable logic = new MyThread(); RealtimeThread rt = new RealtimeThread(scheduling, release, memory, area, group, logic); rt.start(); try{ //等待此線程,直到結(jié)束。 rt.join(); }catch(Exception e){}; } } 實(shí)時(shí)線程中,還有一個(gè)重要的問題是,資源的搶占和優(yōu)先級(jí)的關(guān)系。現(xiàn)在的rt jvm實(shí)現(xiàn)中,實(shí)現(xiàn)的都是優(yōu)先級(jí)繼承機(jī)制。Rtsj推薦的另一種搶占機(jī)制是優(yōu)先級(jí)限高機(jī)制。這里不作詳細(xì)介紹。這些內(nèi)容會(huì)在本文的后續(xù)專題文章中出現(xiàn)。 在rtsj中,很多問題的解決方法都是衍生于實(shí)時(shí)線程的,例如事件觸發(fā)機(jī)制。這里所說的事件,擴(kuò)展了java awt & swing中的事件思想,rtsj把jvm外部的事件也映射到j(luò)vm中,例如i/o的網(wǎng)絡(luò)操作。為了實(shí)現(xiàn)高效的事件操作,體現(xiàn)實(shí)時(shí)特性,就需要借助于實(shí)時(shí) 線程,線程池這樣的技術(shù)。異步事件,異步傳輸?shù)目刂茩C(jī)制就是結(jié)合實(shí)時(shí)線程,加上各種資源的封裝分配的結(jié)果。這樣說來,實(shí)時(shí)線程是rtsj很基礎(chǔ)的內(nèi)容。 線程介紹了這么多,是不是覺得有點(diǎn)復(fù)雜?對(duì),不但復(fù)雜,而且能完成更多的工作。如果你能夠把這兩段程序運(yùn)行一下,并試著根據(jù)api修改一下,我認(rèn)為,本文的介紹目的就達(dá)到了。 淺嘗則止。 高精度時(shí)間 High Resolution Time這里所說的高精度時(shí)間,是相對(duì)于普通java而言。普通java的時(shí)間精度是毫秒,而rtsj的高精度時(shí)間可表示的精度為納秒。高精度時(shí)間用一個(gè)64位表示的毫秒值和一個(gè)32位表示的納秒值組成,時(shí)間長度就是: 10e64 (毫秒) + 10e32 * 1000 (納秒) = 時(shí)間總和 (毫秒) 這個(gè)時(shí)間總和相當(dāng)于大約2.92億年。 這樣,從時(shí)間精度上和時(shí)間表示長度上,都對(duì)以前的java時(shí)間做了很大的提升。 不過,現(xiàn)在的軟件系統(tǒng)能夠響應(yīng)1個(gè)毫秒級(jí)也決非易事,更何況是納秒了。這可能是rtsj規(guī)范的制定者對(duì)千年蟲問題還是心有余悸的緣故吧。
圖 3 高精度時(shí)間類 HighResolutionTime是這些類中的抽象基類,提供了最基本的時(shí)間方法的實(shí)現(xiàn)。 AbsoluteTime表示絕對(duì)時(shí)間,例如2003年5月8日23:06:10 .177 就是一個(gè)絕對(duì)的時(shí)間,如果有需要,這個(gè)時(shí)間可以精確到納秒級(jí)。可能在這個(gè)時(shí)間需要引爆一個(gè)原子彈,也可能這個(gè)時(shí)間我正寫的頭昏腦脹需要大喊一聲,不管怎樣,這個(gè)時(shí)間可是納秒不差。 RelativeTime是一個(gè)相對(duì)時(shí)間,一個(gè)時(shí)間段,一個(gè)期限,一個(gè)deadline time。這個(gè)功能類的強(qiáng)項(xiàng)就是時(shí)間的前推后算。對(duì)于實(shí)時(shí)程序而言,很多的操作需要設(shè)置超時(shí)時(shí)間。比如,一輛汽車正在駛向懸崖邊緣,要求必須在15個(gè)納秒 內(nèi)啟動(dòng)轉(zhuǎn)向停車操作,如果在這個(gè)規(guī)定期限內(nèi)此操作沒有完成,那就讓駕駛員跳車吧,否則人財(cái)兩空。這個(gè)15納秒就用RelativeTime來表示,也只有 這個(gè)工具類可以表示這樣精度的時(shí)間段。但在我看來,汽車比我貴重多了,所以,如果是我,我就把轉(zhuǎn)向停車操作的時(shí)限調(diào)到2.92億年,誓與汽車共存亡。 RationalTime表示的是,在一個(gè)時(shí)間段內(nèi),某個(gè)操作發(fā)生的頻率。從表現(xiàn)來說,他的行為類似于Java的Timer類,不同的時(shí),RationalTime表示的時(shí)間精度更高。 關(guān)于高精度時(shí)間,這里沒有給出例子。原因是我覺得他們和普通Java中提供的工具類太想象了,不同的就是時(shí)間精度。而Real-Time本身強(qiáng)調(diào)的很多問題就是時(shí)間的問題,所以在這里,才把時(shí)間部分的工具類單獨(dú)拿出來作介紹。 告一段落本人能力有限,如果你覺得沒有把這些東西說明白,也可以理解。不過這些東西也是不太容易理解的。光說不練肯定不行。所以現(xiàn)在你還是沒有搞清楚,也不用多浪費(fèi)腦細(xì)胞。親手試試,也許會(huì)有豁然開朗的感覺。在前面也說過,本文重在概念性方面。具體每一個(gè)技術(shù)細(xì)節(jié)問題,后續(xù)會(huì)有相應(yīng)的專題文章介紹。你也可以參考相關(guān)的書籍和資料。 參考Huihoo Power! 企業(yè)計(jì)算研究中心 http://www. 開放企業(yè)基金會(huì) http://www. Real-Time Java Expert Group http://www. Reference Implementation,RTSJ JVM http://www. <<實(shí)時(shí)Java平臺(tái)編程>> Peter C. Dibble 騰啟明 等譯 機(jī)械工業(yè)出版社 Efficient MemoryReference Checks for Realtime Java Angelo Corsaro and Ron K. Cytron Department of Computer Science and Engineering Washington University St. Louis, MO, 63130, USA {corsaro, cytron}@cse.wustl.edu Doing Real-Time With Java Kelvin Nilsen, Chair of the Real-Time Java Working Group |
|
|