从无限循环切换到TimerTask为什么会导致CPU使用率下降?

11

我编写了一个结构如下的守护进程:

while( true ) {
  // do some stuff
  Thread.sleep( 1000 );
}

我注意到它使用了非常大量的CPU - 高达100%。在我的生产服务器上,我有一个类似的守护进程已经存在好几个月,也有相同的CPU问题。

昨天我重构了代码,使用TimerTask。立即我发现CPU使用率在我的开发环境中已经降低。因此,我决定部署到生产环境并使用Munin进行双重检查。以下是图表:

Load average

CPU usage

几点说明:

  • 除JVM外,生产服务器上绝对没有其他内容在运行。
  • 没有其他应用程序线程在运行
  • 它肯定以正确的周期间隔执行旧式代码 - 我每次线程执行时都写入日志。

所以:为什么Thread.sleep与TimerTask相比如此低效?


3
能否提供一个简短但完整的程序演示同样的效果?你有多少个线程在执行这个程序? - Jon Skeet
好主意。我稍后会尝试整理一些东西。 - netflux
.sleep() 会保持同步对象的锁定。所以,也许答案是 TimerTask 使用 .wait() 替代?我不确定,但必须尝试。 - Dmitry Zaytsev
@biovamp 我也是走这条路线,但不太确定Java。现在我需要再仔细检查一下自己的应用逻辑,然后再继续前进。 - netflux
TimerTask确实使用wait()。请查看mainLoop中的queue.wait(executionTime - currentTime) - Matt
2个回答

11

我能想到三种可能性:

  • 你有大量的线程在执行此操作,它们一直在进行上下文切换。使用计时器将意味着只有一个线程。然而,这意味着一次只能执行一个任务。
  • 在睡眠之前,循环中的主体工作即使没有频繁执行也会执行continue;语句。但是,如果没有看到更具体的代码,很难说。
  • 你的JVM/OS组合存在问题。尽管这似乎不太可能。

一个简单的循环仅反复执行Thread.sleep(1000)应该非常便宜 - 这也很容易验证。


我一直在想,像这种情况下中断异常的退化情况(也被吞掉)是否可能存在...不知道它是否真的存在,或者只是我多年来一直心存的无根据的恐惧。 - user166390
@pst:这再次回到线程的启动实际上被执行的频率 - 当然,是否吞咽了InterruptedException也是如此。当OP有一个可重现的案例时,这将非常方便。 - Jon Skeet
@Jon Skeet:两件事。首先,我检查了我的旧版本,并添加了一些更多的日志来确保没有continue语句或其他奇怪的事情发生。明确地Thread.sleep()正确,CPU使用率仍然像以前一样高。我还编写了一个6行的Java类,与我的主应用程序隔离开来,其中包含一个while(true)循环,它可以正常运行,几乎没有CPU使用率。因此,它指向JVM/代码中的其他问题。至少在我的while()循环中没有锁定/同步。还有其他可能影响线程性能的因素吗? - netflux
@robw:只有其他线程……可能是有其他东西在你不知情的情况下启动了一个线程吗?我建议你先复制一份代码,然后逐步删除更多的代码,直到问题消失。例如,如果你保留循环之前的所有代码,但将循环改为仅包含睡眠,问题是否仍然存在? - Jon Skeet
好的 - 所以在 while(true) 版本中,我从 while() 循环中删除了除 Thread.sleep(1000) 和 System.out.println() 之外的所有内容。仍然有相同的超高 CPU 使用率。将其切换到 TimerTask 后,CPU 使用率几乎降为零。奇怪。我并不急于弄清楚这个问题,因为对于我的目的,使用 TimerTask 就可以很好地工作,但这确实是一个有趣的问题。感谢您的建议。 - netflux

0

比较您的处理器、线程和计时器任务的速度。计时器任务是一个较慢的线程(慢得多)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接