您好、欢迎来到现金彩票网!
当前位置:红彩会 > 分派优先级 >

探索实时 Java 的独特功能

发布时间:2019-07-12 16:40 来源:未知 编辑:admin

  developerWorks 中国 正在向 IBM Developer 过渡。 我们将为您呈现一个全新的界面和更新的主题领域,并一如既往地提供您希望获得的精彩内容。

  实时 Java 是对 Java 语言的一组增强,为应用程序提供了一定程度的实时性能,这些实时性能是标准 Java 技术所不能提供的。传统的吞吐量性能通常是对可在固定时间量内完成的指令、任务或工作的总数的衡量。与传统的吞吐量性能不同,实时性能专注于应用程序(在不超出给定时间约束的情况下)响应外部刺激因素所需的时间。在硬实时系统中,决不能超出这类约束;软实时系统对违规具有更高的容忍度。实时性能要求应用程序本身控制处理器,以便它能够响应刺激因素,并且在响应刺激因素的同时,虚拟机内的竞争进程不会阻止应用程序代码的执行。实时 Java 在 Java 应用程序中交付了前所未有的响应能力。

  实时 JVM 可利用实时操作系统(real-time operating system,RTOS)服务来提供硬实时功能,或者可以为具有比较软的实时约束的应用程序运行一个或多个传统操作系统。在使用实时 JVM 时,可以免费使用实时 Java 中使用的一些技术。但是为了探索实时 Java 中的一些特性,需要对应用程序进行一些更改。这些特性是本文介绍的重点。

  JVM 服务是一个执行工作的给定应用程序,这些工作仅能被该应用程序松散地控制。一些运行时子进程在 JVM 内部运行,包括:

  :此任务用于收回应用程序不再使用的运行时内存块。垃圾收集可以使应用程序执行延迟一段时间。

  :此进程(之所以称为类加载,是因为 Java 应用程序是在类粒度级别加载的)涉及从文件系统或网络加载应用程序结构、指令和其他资源。在标准 Java 中,应用程序在第一次引用一个类时加载这个类(

  :许多虚拟机在应用程序运行时通过动态编译将方法由 Java 字节码解释为本地机器指令。尽管这可以提高性能,但编译活动本身可能导致临时延迟,阻止应用程序代码运行。

  :在标准 Java 中,应用程序只有极小的控制权限来调度自己的运行线程,以及调度与在同一操作系统上运行的其他应用程序相关的应用程序。

  所有这些子进程都可能限制应用程序响应外部刺激因素的能力,因为它们可能延迟应用程序代码的执行。例如,可以调度一个指令序列来响应来自网络、雷达系统、键盘或任何其他设备的信号。实时应用程序具有一段很短的可接受时期,在此期间,允许不相关的进程(比如垃圾收集)延迟响应指令序列的执行。

  实时 Java 提供了各种技术,旨在最小化底层子进程对应用程序的干扰。切换到实时 JVM 时可使用的 “免费” 技术包括:限制了收集操作的持续时间和干扰影响的专门垃圾收集,允许在启动时优化性能(而不是延迟优化)的专门的类加载,专门的锁定和同步,以及能够避免优先级反转的专门的优先线程调度。但是,可能需要对应用程序进行一些修改,要利用 Java 实时规范(Real-Time Specification for Java,RTSJ)引入的特性时更应如此。

  RTSJ 提供了一个支持 JVM 中大量实时特性的 API。一些特性在规范实现中是强制性的,另一些是可选的。规范包括以下一般区域:

  RTSJ 定义了javax.realtime.RealtimeThread 标准类的一个子类。从本质上讲,RealtimeThread支持规范中的一些高级特性。例如,实时线程受实时线程调度器控制。该调度器提供了一个独特的调度优先级范围,可以实现先入先出的实时调度策略(确保最高优先级的线程不会受到干扰),以及优先级继承(该算法可阻止较低优先级线程无限期地持有需要不受干扰地运行的较高优先级线程所需的锁,这种情形称为优先级反转)。

  可以在代码中明确构造RealtimeThread的实例。但是也可以轻松更改应用程序来启用实时线程,从而避免繁重的开发工作和相关成本。这里给出了干扰最小且最透明地启用实时线程的各种方式的示例。(可以下载本文所有示例的源代码)。这些技术使应用程序能够最轻松地利用实时线程,使应用程序能够保持与标准虚拟机兼容。

  在清单 1 中,提供RealtimeThread对象的方法被指定为自身的一个类。通过这种方式,只有在加载该类时才会验证该方法,这在第一次访问

  方法时完成。当加载类时,运行时虚拟机字节码验证器尝试验证RealtimeThread类是否为Thread类的子类,如果为找到实时类,验证将失败并抛出NoClassDefFoundError。使用反射分配线 演示了一种替代技术,它具有的效果与清单 1 相同。它首先设置一个优先级值来确定期望的线程类型,根据类名称实例化实时线程或常规线程。这段反射式代码要求类中存在一个构造函数,这个函数接受一个java.lang.Runnable实例作为最后的参数,为所有其他参数传递空值。

  ,而另一个不能。您的选择取决于底层的 JVM。只需在您的分发版中包含相关类文件,就可以启用对应版本。无论选择哪个版本,代码都相对较简单并且可避免任何异常处理,这与前面的例子不同。但是,当分发应用程序时,必须包含两个类中的一个,具体选择取决于将运行应用程序的相关虚拟机。

  RTSJ 为每个线程引入了分配上下文的概念。当将一个内存区域用作一个线程的分配上下文时,该线程实例化的所有对象都从该区域分配。RTSJ 指定以下附加的隔离内存区域:

  内存区域,其中的内存永远不会被使用。当运行静态初始化器时,初始化类的线程使用此区域作为分配上下文。尽管永远空闲的内存不需要垃圾收集器的注意,但对它的使用没有任何限制,因为其中的内存不会被回收。

  )。范围无需任何垃圾收集活动,它们的内存也可以一次性地完整回收,以供重用。当虚拟机决定不再将一个范围当作任何活动线程的分配上下文区域时,在该范围中分配的对象就会被终结和清除,从而释放分配给它们的内存以供重用。

  内存区域根据类型或地址来确定。可以指定将每个物理内存区域当作范围区域重用,或者当作永远空闲区域供一次性使用。这类内存区域可以访问具有特定特征的内存,或从特定设备(比如闪存或共享内存)进行访问。

  范围概念对对象引用引入了更强的限制。当释放了一个范围内存块时,其中的对象将被清除,绝不能存在具有指向已释放内存块内部的引用的对象,否则将导致悬摆指针。这在一定程度上是通过实施分配规则来完成的。分配规则指明从非范围内存区域分配的对象不能指向内存区域。这能够确保当释放范围对象时,其他内存区域的对象不会保留对不存在的对象的引用。

  分配规则不允许一个范围内的对象指向另一个范围。但是,这意味着每个对象必须有一个强制范围清除顺序,这个顺序由每个线程内部的一个堆栈维护。除范围以外,该堆栈还包括对进入其中的其他内存区域的引用。只要内存区域成为了线程的分配上下文,它就会被放在线程的范围堆栈的顶部。分配规则指明在堆栈较高位置的范围中的对象可以引用堆栈中较低位置的范围中的对象。因为在顶部的范围会首先被清除。较低位置的范围不允许引用较高位置的范围。

  堆栈中的范围顺序与其他线程的堆栈中的范围顺序保持一致。一旦将一个范围放在任何线程的堆栈上,该堆栈中离其最近的范围被当作是它的父范围(如果堆栈中没有其他范围,则将唯一的

  当作父范围)。尽管该范围仍然在堆栈上,但只有父范围保持一致时,才能将该范围放在任何其他线程的堆栈上,这意味着它是其他线程的堆栈中位置最高的范围。换句话说,被使用的范围只能有一个父对象。这可以确保当释放范围时,会按相同顺序进行清除,无论哪个线程执行每个范围的清除任务,并且分配规则会在所有线程中保持一致。

  可以通过两种方式来使用特定内存区域:将该区域指定为运行线程的初始内存区域(在构造线程对象时指定),或者显式地输入该区域,为其提供一个将执行的

  对象(将该区域指定为默认区域)。但使用不同的内存区域时,必须考虑一些特殊的因素,因为这些因素会带来复杂性和可能的风险。必须选择区域的大小和数量。如果范围正在被使用,必须谨慎设计线程的范围堆栈的顺序,还必须考虑分配规则。

  的一个子类,它实现的对象可以在不受到垃圾收集器干扰的情况下运行。它们可以在不受干扰的情况下运行,因为它们不能访问从堆分配的任何对象。任何违反此访问限制的尝试都会抛出一个。

  另一个调度选项是异步事件处理器,可以使用它来调度将执行的代码,以响应异步或定期事件。(如果事件是由定时器发起的,那么它们可能是定期的)。这使您无需为这类事件显式地调度线程。相反。虚拟机维护一个共享线程池,该线程池被分派用于在发生事件时运行异步事件处理器的代码。这可以简化实时应用程序,将您从对线程和内存区域的管理中解放出来。图 2 中的类图显示了可用于调度代码的选项:图 2. 此类图演示了调度代码的选项

  应用程序创建的每个内存区域会被分配所申请的内存大小。选择的大小太大会降低内存使用效率,但是选择的大小太小很容易使应用程序遇到

  。在开发期间,即使应用程序没有变化,底层库也可能改变。这可能导致意外的附加内存使用,导致内存使用超出内存区域限制。

  多个线程共享的范围内存区域的无需很大,因为在没有内存使用它时会将它清除。但是,对使用范围的线程计时稍作更改之后,范围就会始终被用作线程的分配上下文。这将导致该范围始终不会被清除,导致

  如果未足够重视代码设计,就可能发生这些异常。实际上,对程序行为和计时的细微更改可能导致这些异常的意外出现。一些示例包括:

  由于线程之间的计时和同步变化,堆中在正常情况下不可被 NHRT 使用的对象可能变得可用。

  当不知道从哪个内存区域分配对象或者特定范围位于范围堆栈上的何处时,可能发生

  由于分配规则的限制,通常使用静态字段或其他缓存数据方式的代码会使范围不安全,可能导致

  任何类型的常规或实时线程都可以初始化类,包括 NHRT(它可能导致意外的

  终结也可能导致死锁。在终结内存区域之前,终结线程可能获取了锁。其他线程可能会争用这些锁和在终结期间将获取的锁,进而导致死锁。

  尽管可以保证 NHRT 的运行不会受到垃圾收集的直接干扰,但 NHRT 可能与由垃圾收集抢占的其他线程类型共享相同的锁。如果 NHRT 在尝试获取这类锁时被延迟了,而且拥有该锁的线程被垃圾收集延迟了,那么垃圾收集也会间接地延迟 NHRT。

  下一个示例演示了到目前为止介绍的一些实时特性。首先,清单 5 展示了两个类,它们分别是事件数据的生成者和使用者。两个类都是

  在清单 6 中,数据生成代码被分配给一个异步事件处理器,以在最高的可用优先级上运行。该处理器进入一个范围内存区域来运行数据生成代码。相同的范围内存区域是 NHRT 类的一个参数,充当数据的使用者。线程类也很直观,允许异步访问

  在清单 7中,两个范围被用作非堆线程和处理器的范围堆栈,使用两个范围是因为这些

  本示例有一个有趣之处,那就是为什么没有报告事件 4 和 5。监听线程每次报告队列中的事件时,它会从队列前端开始,到队列末尾结束,这意味着所有 6 个事件至少被报告一次。但是,代码的设计可确保用于存储事件的内存在没有被任何线程使用时将自动被释放。当使用者线程停止读取队列时,它将退出范围内存区域,这时没有任何

  内存管理是自动执行的,它的运行不会受到垃圾收集器的干扰,无论收集器是否是活动的(因为处理器和线程都与堆没有关系)。各个事件在内存中存储为一个对象队列,如果一个监听线程可以使用它们,那么这个队列会继续增长。如果没有这样的监听线程,队列和相关的事件将自动被释放。

  一般使用场景借助调度和内存管理框架,可以设计一个具有各种优先级线程的应用程序,以在实时虚拟机中最佳地执行(并且也可能在其他虚拟机中很好地运行)。应用程序可以包含具有高优先级的事件处理线程,从外部输入收集数据并存储数据以供处理。由于这些事件处理线程的过渡性和异步特性,它们可能也适用于其他内存管理机制,它们可能具有极严格的实时约束。在中间优先级上,可能存在使用数据和执行计算或分发数据的处理线程。中间线程可能需要分配足够的 CPU 利用率来管理其工作负载。在最低优先级上,可能存在维护和日志记录线程。如果使用实时虚拟机来管理应用程序中各种任务的调度和内存使用,则可以让内存最高效地运行。

  RTSJ 的目的是使开发人员可以编写能够在必需的实时约束下运行的应用程序。使用实时调度器和线程就足以实现此目标了。如果还不能实现,可能需要执行更高级的开发,利用由虚拟机实现的一个或多个更高级的特性。结束语

  本文介绍了一些技巧,您可以利用它们将实时 Java 元素集成到 Java 应用程序中。本文介绍了一些调度和内存管理特性,您可能希望利用它们来实现实时性能。本文只是您利用 Java 语言的传统优势(比如互操作性和安全性)的开端,将这些优势与新的特性组合在一起,就可以满足应用程序所需的实时约束。

http://m3-ctech.com/fenpaiyouxianji/616.html
锟斤拷锟斤拷锟斤拷QQ微锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷微锟斤拷
关于我们|联系我们|版权声明|网站地图|
Copyright © 2002-2019 现金彩票 版权所有