Почему переход с бесконечного цикла на TimerTask привел к снижению производительности процессора?

Я написал демона, который был структурирован следующим образом:

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

Я заметил, что он использует очень большой объем процессора - до 100%. У меня был аналогичный демон на моих производственных серверах в течение нескольких месяцев с той же проблемой ЦП.

Вчера я реорганизовал код для использования TimerTask. Сразу же я заметил, что использование ЦП уменьшилось в моем блоке dev. Поэтому я решил развернуть производство и дважды проверить с помощью Munin. Вот графики:

Load average

CPU usage

Несколько пунктов:

  • На рабочем сервере нет ничего, кроме JVM.
  • Других потоков приложений нет.
  • Он определенно выполнял код старого стиля с правильными периодическими интервалами - я всегда записываю журнал каждый раз, когда поток выполняется.

Итак: почему Thread.sleep настолько неэффективен по сравнению с TimerTask?

Ответ 1

Три возможности, о которых я могу думать:

  • У вас огромное количество потоков, которые делают это, и они постоянно переключаются в контекст. Использование таймера будет означать только один поток. С другой стороны, это означает, что вы получите только одну задачу, выполняемую за раз.
  • У вас есть оператор continue; где-то в вашем цикле перед сном, поэтому, даже если основная часть работы цикла не выполняется очень часто, что-то есть. Трудно сказать, не видя еще более конкретного кода.
  • У вас сломанная комбинация JVM/OS. По-видимому, это кажется маловероятным.

Простой цикл, выполняющий только Thread.sleep(1000), должен быть очень дешевым - и вам также будет легко проверить его.

Ответ 2

Сравните скорость вашего процессора, поток и таймер. Timertask - более медленный поток (намного медленнее).