(by huihoo.org Cocia Lin )
Real-Time Specification for Java缩写就是RTSJ。
RTSJ是Java的适应实时计算要求而开发。关于对实时系统的介绍和特性说明,请参见其他文章,这里重点是Java针对实时系统开发所做的改进做详细的介绍。
RTSJ在6各方面对Java做了增强:
1.增加实时线程。实时线程提供了比普通线程更完善和细致的控制属性和操作内容,例如更大的优先级范围,控制内存分配等等;
2.增加了新的工具类和编程方式,比如内存工具类。利用这些工具类,可以完成不需要垃圾回收的Java程序;
3.增加了异步事件处理器类和相应的机制。把异步事件和jvm外界发生的事件直接关联起来;
4.增加了异步传输的控制机制,允许一个线程更改另一个线程中的控制流。也就是说,可以中断或者继续另一个线程的运行;
5.增加一种机制,能够控制对象分配到内存的位置;
6.增加一种机制,能够访问特定地址的内存。
RTSJ对传统java的增强的过程中本着一个原则,就是不改变原有的java的任何语法。这样做的目的就是保持原有java的连续。也就是说,在普通 jvm上能够运行的程序,理论上,可以完全相同的在rt jvm上运行;反过来则不行。之所以强调是在理论上情形,原因是现有的rt jvm还没有实现完全的java类库,比如awt,swing,text等。而一些常用的lang,util,io等都有实现。这也是现有的rt jvm选择实现的优先级别。
对于rtsj的核心和基础,我认为有以下的几点:
1.领域内存 Scope Memory
2.实时线程 Realtime Thread
3.高精度时间 High Resolution Time
这当然不是说,其他的不重要。其他的rtsj能力就像awt对你的重要程度一样,如果你在作客户界面程序,那awt对你就很重要;如果你在做servlet,jsp等形式的j2ee程序,awt可能对你完全没有意义。所以这里只介绍核心和基础的几点内容。
Rtsj增加领域内存根本目的就是解决jvm的垃圾收集问题。我们知道,jvm的垃圾收集机制具有 不确定性。例如,我们不知道jvm什么时候将要进行垃圾收集,收集操作需要多长时间,收集线程本身占用多少内存,多少cpu时间,是否有其他的i/o资源 占用等等。而实时应用程序的一个很重要的指标就是,能够预计一个操作的准确时间,占用的资源。rtsj要能够在实时应用中得到应用,首先就要解决垃圾收集 这个头疼的问题。
Rtsj并没有放弃垃圾收集机制,如果真的这样做,也是不可接受的。Java能够存在和发展到现在的这个地步,头号功臣可能就是这种机制。它让 开发者从细节内存管理的泥潭中解放出来。如果去掉这种机制,rtsj和c++又有什么区别?rtsj的做法是,让有实时要求的代码使用领域内存机制,避开 垃圾收集,而没有此项要求的代码,和标准的java一样书写,享受垃圾收集给你带来的快乐。对我来说,可以自由的用碗来吃饭,而不用自己来刷碗,应该不算 一件坏事。
Rtsj的这种做法,是基于这样的两条结论:不产生垃圾的代码不会导致请求式的垃圾收集;不引用堆中对象的代码可以抢占垃圾收集器的线程。
领域内存的机制,其实就是内存管理的机制,开发者自己管理内存的机制。对照上段的文字,你可能有了疑问:需要我自己管理内存,这又和我使用c++ 有什么区别?这个问题的答案,不同的人理解不同,最终结论就会有所不同。我的理解是,rtsj提供了比c++简单的和高级别的内存管理方式,是开发者学习 几个工具类和一种编程风格,就能实现与c++等相近的效率和功能。你的理解是什么呢?这和Java公认的比c++慢很多倍,但却被人们普遍接受的情况,有 着异曲同工之处。
读者脸色有变,现在进入正题。
为了实现内存管理,rtsj提供了一组内存管理类,和一种编程方法,或者说的编程规范。
ImmortalMemory的中文名字是不朽内存,他是静态的,或者说是与jvm共存亡的。只要jvm在运行,不朽内存就存在,并且可 用。他和static静态的对象行为一样。存放在不朽内存中的对象不会被垃圾收集。但是要当心,不朽内存不会被回收,所以在其中放入太多的东西,会导致系 统内存耗尽。所以,在有其他选择的情况下,不要使用不朽内存。
HeapMemory中文名字是堆内存。他和我们平时在java程序中使用new 这个关键字生成对象的行为是一样的。其中的对象数据在无用的时候会被垃圾回收。
ScopeMemory就是领域内存。他有各种不同的实现。这里只拿出两个典型的,并且已经被现有的几种rtjvm支持的领域内存实 现:LTMemory和VTMemory。这两者的区别,是在他们分配存储空间的时间要求上,LTMemory随着分配空间的增加,分配的时间以固定的线 性增长。在rtsj规范中,没有说明VTMemory相对LTMemory的任何优点,但规范对LTMemory的分配时间有严格要求,而对 VTMemory没有。所以可以简单的这样理解:LTMemory分配时间很短,但内存使用效率可能不高;而VTMemory分配时间可能长一点,但内存 使用效率可能会高。个人认为,现阶段不用考虑他们的具体区别,全部使用LTMemory也不会有任何问题,我以我的房东的房子做保证。
下面提供一段代码,示范一下,使用领域内存的情景。
import javax.realtime.*;
public class Foo {
class Bar implements Runnable{
// run()在运行sm.enter(this)时被调用
public void run(){
//newObject 被分配在LTMemory sm中
Object newObject = new Object();
}//方法退出后,newObject对象的空间立即释放
}
void createSomeObjects() {
//分配1024字节空间
ScopeMemory sm = new LTMemory(1024, 1024);
// 1.直接分配
Bar b = (Bar)sm.newInstance(Bar.class);
// 2.进入领域
sm.enter(b);
}//在这里,垃圾收集器准备回收sm
//
//这样的情形下,sm.enter()方法中分配的对象,
//在运行期间即使引用悬空,也不会被垃圾收集,
//而在此方法退出后,其中所有的分配空间被立即释放,
//也不通过垃圾收集机制。
Public static void main(String[] args){
:Foo foo = new Foo();
Foo. createSomeObjects();
}
}
这个例子比较简陋,但他是完整的,并且说明了rtsj编写程序的几个两个基本要求:内存管理类的使用和程序编写风格要求。内存管理类在前面的例子 程序的注释中有所介绍。而所谓程序编写风格,其实就是sm.enter(b)的使用的方式,包括Bar需要实现run方法。这样的风格称做 closure,中文为闭包。闭包的意思是,一个程序段相对的独立和封闭,这样的程序段可以很容易的控制内存这样的资源的分配控制。比如,我在进入这个程 序段时准备好内存空间,例子中的分配空间是1024字节,在退出这个程序段时将这些内存释放掉,因为在闭包的环境下,相当于告诉jvm,过了这段程序,这 些内存空间中的对象对我已经没有用了。
我们来假想一下sm.enter(b)中的情形:
enter( b ){
prepareResource(b);
b.run();
releaseResource(b);
}
对于内存管理,浅尝则止,只说到概念。这些内容可能还不足以开始真正的工作,还有很多内存管理方面内容没有说明。更详尽的内容描述可能在其它的更深入的资料中找到。
在介绍RealtimeThread之前,我们先来看一个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!");
}
};
//准入控制,分析可行性。如果发现此线程某些资源不能得到,则退出。
//注意:分析可行性是一项可选功能,有的rtsj jvm可能没有实现,
//但是这个特性确实很吸引人。
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);
}
}
从上图中可以看出,RealtimeThread扩展了Thread,也就是说,他具有了普通Thread所有的特性和功能。
说到线程,就不得不说到线程的优先级了。普通java的线程有1-10,10个优先级,而rtsj的线程有32个优先级。而且只要是RealtimeThread的优先级,肯定比普通Thread优先级高。换句话说,RealtimeThread优先级肯定大于10。
RealtimeThread相对于Thread,增加了更多的细致的控制能力,这些从RealtimeThread的构造函数中,就能看得出来。
public RealtimeThread(SchedulingParameters scheduling,
ReleaseParameters release,
MemoryParameters memory, MemoryArea area,
ProcessingGroupParameters group,
java.lang.Runnable logic)
1.SchedulingParameters 调度参数:此参数中可能包含优先级信息,此线程可能就按照这个优先级运行;
2.ReleaseParameters 释放参数:释放参数在线程使用waitForNextPeriod方法和要求使用准入控制的时候起作用;
3.MemoryParameters 内存参数:此参数可以约束线程对领域内存的使用。
4.MemoryArea 内存区域:可选择的有,堆,不朽内存,领域内存;
5.ProcessingGroupParameters 处理组:用来控制非周期性的活动;
6.Runnable 逻辑:也就是Runnable对象。
这六个参数这样介绍,真是糊里糊涂。现在用一个例子,看看能不能帮助你。
//Note:此代码引自<实时Java平台编程>,page 117
import javax.realtime.*;
public class FullConstr{
public static void main(String[] args){
//1.将优先级信息封装在此参数中
SchedulingParameters scheduling =
new PriorityParameters( PriorityScheduler.MIN_PRIORITY + 20 );
//在线程执行完一个周期之后,也就是waitForNextPeriod时
//会使用此参数中的信息。这里全部用null构造,就是不起作用
ReleaseParameters release =
new AperiodicParameters( null,null,null,null );
//指定内存使用的类型
MemoryParameters memory =
new MemoryParameters( MemoryParameters.NO_MAX,//Heap
0);//Immortal memory
//内存区域。这里使用了堆内存,和我们使用new没有区别
MemoryArea area = HeapMemory.instance();
//这里不讨论此参数。虽然不能靠这个参数帮助你,
//但我保证,特不会妨碍你。
ProcessingGroupParameters group = null;
//这个参数熟悉吧,就是我们要运行的代码。
//这里使用MyThread,假设已经准备好了要运行的代码。
Runnable logic = new MyThread();
RealtimeThread rt = new RealtimeThread(scheduling,
release,
memory,
area,
group,
logic);
rt.start();
try{
//等待此线程,直到结束。
rt.join();
}catch(Exception e){};
}
}
实时线程中,还有一个重要的问题是,资源的抢占和优先级的关系。现在的rt jvm实现中,实现的都是优先级继承机制。Rtsj推荐的另一种抢占机制是优先级限高机制。这里不作详细介绍。这些内容会在本文的后续专题文章中出现。
在rtsj中,很多问题的解决方法都是衍生于实时线程的,例如事件触发机制。这里所说的事件,扩展了java awt & swing中的事件思想,rtsj把jvm外部的事件也映射到jvm中,例如i/o的网络操作。为了实现高效的事件操作,体现实时特性,就需要借助于实时 线程,线程池这样的技术。异步事件,异步传输的控制机制就是结合实时线程,加上各种资源的封装分配的结果。这样说来,实时线程是rtsj很基础的内容。
线程介绍了这么多,是不是觉得有点复杂?对,不但复杂,而且能完成更多的工作。如果你能够把这两段程序运行一下,并试着根据api修改一下,我认为,本文的介绍目的就达到了。
浅尝则止。
这里所说的高精度时间,是相对于普通java而言。普通java的时间精度是毫秒,而rtsj的高精度时间可表示的精度为纳秒。
高精度时间用一个64位表示的毫秒值和一个32位表示的纳秒值组成,时间长度就是:
10e64 (毫秒) + 10e32 * 1000 (纳秒) = 时间总和 (毫秒)
这个时间总和相当于大约2.92亿年。
这样,从时间精度上和时间表示长度上,都对以前的java时间做了很大的提升。
不过,现在的软件系统能够响应1个毫秒级也决非易事,更何况是纳秒了。这可能是rtsj规范的制定者对千年虫问题还是心有余悸的缘故吧。
HighResolutionTime是这些类中的抽象基类,提供了最基本的时间方法的实现。
AbsoluteTime表示绝对时间,例如2003年5月8日23:06:10 .177 就是一个绝对的时间,如果有需要,这个时间可以精确到纳秒级。可能在这个时间需要引爆一个原子弹,也可能这个时间我正写的头昏脑胀需要大喊一声,不管怎样,这个时间可是纳秒不差。
RelativeTime是一个相对时间,一个时间段,一个期限,一个deadline time。这个功能类的强项就是时间的前推后算。对于实时程序而言,很多的操作需要设置超时时间。比如,一辆汽车正在驶向悬崖边缘,要求必须在15个纳秒 内启动转向停车操作,如果在这个规定期限内此操作没有完成,那就让驾驶员跳车吧,否则人财两空。这个15纳秒就用RelativeTime来表示,也只有 这个工具类可以表示这样精度的时间段。但在我看来,汽车比我贵重多了,所以,如果是我,我就把转向停车操作的时限调到2.92亿年,誓与汽车共存亡。
RationalTime表示的是,在一个时间段内,某个操作发生的频率。从表现来说,他的行为类似于Java的Timer类,不同的时,RationalTime表示的时间精度更高。
关于高精度时间,这里没有给出例子。原因是我觉得他们和普通Java中提供的工具类太想象了,不同的就是时间精度。而Real-Time本身强调的很多问题就是时间的问题,所以在这里,才把时间部分的工具类单独拿出来作介绍。
本人能力有限,如果你觉得没有把这些东西说明白,也可以理解。不过这些东西也是不太容易理解的。光说不练肯定不行。所以现在你还是没有搞清楚,也不用多浪费脑细胞。亲手试试,也许会有豁然开朗的感觉。
在前面也说过,本文重在概念性方面。具体每一个技术细节问题,后续会有相应的专题文章介绍。你也可以参考相关的书籍和资料。
Huihoo Power!
企业计算研究中心
http://www.huihoo.com
开放企业基金会
http://www.huihoo.org
Real-Time Java Expert Group
http://www.rtj.org
Reference Implementation,RTSJ JVM
http://www.timesys.com
<<实时Java平台编程>>
Peter C. Dibble
腾启明 等译
机械工业出版社