2012-02-02 189 views
2

我创建了一个多线程应用程序,它使用相同的ScheduledExecutorService(方法scheduleAtFixedRate和一个固定的10个线程的线程池)执行不同的任务。然而,除了一个主要任务之外的所有线程(不要与主线程混淆!)大部分时间都在睡觉,直到有用户输入。主任务和其他(用户输入)线程之间共享的数据受同步块中的锁对象保护。Java ScheduledExecutorService - 需要关于实时性能问题的建议

主要任务以相当大的频率执行重复任务,例如25赫兹(即40毫秒的周期),重要的是及时执行此任务。通常情况也是如此,但不幸的是并非全部。其他“很好”的应用程序也运行在同一台计算机上(Linux OP),但是CPU 100%运行在100%以上。在60分钟的测量期间(即90000个样品),在约50个样品中,两个连续样品之间的实际周期>≥60ms,并且其中约30个周期超过100ms,在几个非常差的病例(时间相当接近,以秒为单位),时间在1000到2300毫秒之间。在测量过程中没有用户输入。看看数据日志,看起来很明显,有些东西阻止执行程序在这些时间间隔内执行其工作,因为它们通常跟着执行程序的“追赶”,即在2或3毫秒内从应用程序中获取多个日志。

我已经尝试过在每次执行任务时定期进行垃圾回收,但是(至少从短期来看)它似乎只会让事情变得更糟。我也测量了任务的执行时间。它大部分是大约1毫秒,它不应该导致执行器崩溃(或者应该这样做?)。在这里也有的偏差,偶尔也会在100ms左右,但他们可以解释不到50%的延迟。我试过寻找TaskRejectedExceptions,什么都没发现。

所以我现在的问题基本上是:我可以期待从一个ScheduledExecutorService随着时间的推移?这可能是一个线程问题,尽管在这些情况下只有主要任务应该运行吗?什么可能会导致ScheduledExecutor暂时停止执行,只能用它的“追赶”数据来洪泛日志,并且是否有任何方法来控制这种恼人的行为?这可能与我的JVM仅仅是没有实时优先级功能的普通JVM有关吗?任何关于从哪里开始挖掘的帮助,想法或理论,都会得到真正的赞赏!

+0

我相信你没有通过任务的记录时间来判断任务的执行时间,而是明确地测量并打印出调用之间的时间? – Perception 2012-02-02 22:35:32

+0

这有点令人困惑。所以你所说的测量是两次执行主任务的动作之间的时间,应该每40ms执行一次。 – Tudor 2012-02-02 22:36:19

+0

很难说,但日志和垃圾回收可以大量影响'及时'的性能。要专注于日志记录,您可能不应该在执行程序的某个线程中运行任何日志记录代码。控制GC出现并不在我的联盟中,并且高度依赖于您正在使用的JVM。最好的一般技巧是尽量减少对象的创建,但当GC中断你的应用程序时(除非你精确地定制你的应用程序和JVM设置),*会*得到一个点。 – 2012-02-02 22:39:37

回答

0

根据计时器documentation,你的情况似乎完全正常:

在固定速率执行,每个执行相对于初始执行的安排执行时间安排。如果由于任何原因(例如垃圾收集或其他后台活动)延迟执行,两个或更多执行将会快速连续发生以“赶上”。从长远来看,执行的频率恰好是指定周期的倒数(假设系统时钟基础Object.wait(long)是准确的)。

事实是,scheduleAtFixedRate()不能保证执行将在它应该开始的时候准确地开始,而只是在长期运行中的频率。 其实,我不确定是否有办法来完成使用Java。

+0

实际上没有办法在大多数情况下实现你需要一个实时的操作系统,即使在linux下的实时调度程序下运行一个C程序,我们发现它不能保证一个固定的速率(尽管我们测量的速度远远小于并且几乎没有java中的OP测量器) – nos 2012-02-02 23:36:43

0

的几个注意事项:

  • 调用垃圾回收器并不保证收集很可能会思考的方式。从Java文档:

调用该方法表明Java虚拟机 努力朝着回收未使用的对象,以使他们当前占用快速重用内存 。

这里的关键是建议。永远不要期望它完全按照您的预期运行。你也不能期望它能够阻止实际的系统垃圾收集在另一次运行。

  • 线程已安排,但仍然可以按计划实际执行它们。您的处理器使用率不足100%并不意味着它必须能够执行另一个线程。一般来说,期望在没有从头开始完全编写的环境中高度准确地实时执行线程可能不会导致期望的结果。