27

目标:每隔一段时间执行某些代码。

问题:在性能方面,是否存在显着差异:

while(true) {
    execute();
    Thread.sleep(10 * 1000);
}

executor.scheduleWithFixedDelay(runnableWithoutSleep, 0, 10, TimeUnit.SECONDS);

?

当然,后一种选择更洁净。然而,我想知道我是否应该开始一次名为“花几天时间重构遗留代码来告别 Thread.sleep()”的冒险。

更新:此代码在超级/超级/超高负载环境中运行。

4

4 回答 4

12

您正在处理以数十秒为单位的睡眠时间。通过在此处更改睡眠选项可能会节省纳秒或微秒。

我每次都更喜欢后一种风格,但如果你有前一种,而且要花很多钱来改变它,“提高性能”就不是一个特别好的理由。

编辑回复:8000 个线程

8000 个线程非常多;我可能会转移到预定的执行程序,以便您可以控制系统上的负载量。您关于改变唤醒时间的观点是需要注意的,尽管我认为更大的风险是所有线程都在睡觉然后连续醒来并竞争所有系统资源的踩踏事件。

我会花时间把这些都扔到一个固定的线程池调度执行器中。只有尽可能多的同时运行,因为您可以使用最有限的资源(例如,# cores 或 # IO 路径)加上一些来处理任何问题。这将以延迟为代价为您提供良好的吞吐量。

使用该Thread.sleep()方法将很难控制正在发生的事情,并且您可能会损失吞吐量延迟。

如果您需要更详细的建议,您可能必须更详细地描述您正在尝试做的事情。

于 2012-11-16T18:16:14.673 回答
1

由于您没有提到 Java 版本,所以情况可能会发生变化。

正如我从 Java 的源代码中回忆的那样,主要区别在于内部编写的方式。

对于Sun Java 1.6,如果您使用第二种方法,本机代码还会将等待和通知调用引入系统。因此,在某种程度上,线程效率更高且 CPU 友好。

但是你又一次失去了控制,你的代码变得更加不可预测 - 考虑你想睡 10 秒钟。

所以,如果你想要更多的可预测性——当然你可以选择选项 1。

另外,附带说明一下,在遗留系统中,当你遇到这样的事情时 - 80% 的机会现在有更好的方法来做到这一点 - 但神奇的数字是有原因的(剩下的 20%)所以,改变它个人风险 :)

于 2012-11-16T18:52:51.620 回答
1

有不同的场景,

  1. Timer 创建一个不断更新的任务队列。当 Timer 完成后,它可能不会立即被垃圾回收。所以创建更多的定时器只会将更多的对象添加到堆上。Thread.sleep() 只暂停线程,因此内存开销会非常低
  2. Timer/TimerTask 也会考虑到你的任务的执行时间,所以会更准确一点。它更好地处理多线程问题(例如避免死锁等)。
  3. 如果你线程得到异常并被杀死,那就是一个问题。但是 TimerTask 会处理它。无论之前运行是否失败,它都会运行
  4. TimerTask 的优势在于它更好地表达了您的意图(即代码可读性),并且它已经实现了 cancel() 功能。

参考取自这里

于 2017-05-23T06:22:49.800 回答
0

您说您在“大型......高负载环境”中运行,所以如果我理解正确,您有许多这样的线程同时睡眠,就像您的代码示例一样。重用一个线程比杀死和创建一个新线程花费更少的 CPU 时间,并且重构可能允许您重用线程。

您可以使用大于 1的ScheduledThreadPoolExecutor创建线程池。然后,当您在该线程池上调用scheduleWithFixedDelay时,如果线程可用,它将被重用。corePoolSize

此更改可能会降低 CPU 利用率,因为线程正在被重用而不是销毁和创建,但减少的程度将取决于它们正在执行的任务、池中的线程数等。如果某些情况下,内存使用量也会下降的任务重叠,因为一次空闲的线程会更少。

于 2018-12-28T15:41:08.737 回答