Thread.sleep () завис? - PullRequest
       7

Thread.sleep () завис?

20 голосов
/ 19 июля 2011

Вот мой простой код, который нужно циклически повторять каждую секунду (не нужно быть точным) и запускать работу, если это необходимо:

while (true) {
  // check db for new jobs and 
  // kick off thread if necessary
  try {
    Thread.sleep(1000);
  } catch(Throwable t) {
    LOG.error("", t);
  }
}

Этот код работал отлично в течение нескольких месяцев. Буквально вчера у нас начались проблемы, когда один из наших серверов завис в методе Thread.sleep (1000) . IOW - прошло более суток, а Thread.sleep не вернулся. Я запустил jconsole и получил эту информацию о теме.

Name: Thread-3
State: TIMED_WAITING
Total blocked: 2  Total waited: 2,820

Stack trace: 
 java.lang.Thread.sleep(Native Method)
xc.mst.scheduling.Scheduler.run(Scheduler.java:400)
java.lang.Thread.run(Thread.java:662)

Scheduler.java: 400 - строка Thread.sleep выше. Выходные данные jconsole не увеличивают «Всего ожиданий» каждую секунду, как я ожидал. На самом деле это не меняется вообще. Я даже выключил jconsole и запустил его снова в надежде, что, возможно, это вызовет обновление, но снова получит те же цифры. Я не знаю, какое еще может быть объяснение, кроме того, что jvm неправильно повесил команду сна. Однако в мои годы у меня было так мало проблем с jvm, что я предполагаю, что это должно быть упущение с моей стороны.

примечание: еще одна вещь, на которую следует обратить внимание, это то, что никакой другой поток не активен. IOW - процессор почти простаивает. Я где-то читал, что Thread.sleep может быть законно истощен, если активен другой поток, но здесь это не так.

версия Solaris:

$ uname -a
SunOS xcmst 5.10 Generic_141415-08 i86pc i386 i86pc

Java-версия:

$ java -version
java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) Server VM (build 20.1-b02, mixed mode)

Ответы [ 6 ]

7 голосов
/ 19 июля 2011

В дополнение к тому, что упомянул bdonlan, вы можете посмотреть ScheduledThreadPoolExecutor . Я работаю над проектом очень похожего типа, и благодаря этому небольшому фрагменту этот объект облегчил мне жизнь.

ScheduleAtFixedRate

Если любое выполнение этой задачи занимает больше времени, чем ее, то последующие казни могут начаться поздно, но не будут одновременно выполнять.

Надеюсь, это поможет!

5 голосов
/ 19 июля 2011

Являетесь ли вы зависимым от числа системных тиков, чтобы монотонно увеличиваться?

Из того, что я слышал от кого-то, случилось ( иногда ), чтосистемный тик идет назад на один или два тика.Я сам еще не испытал это, но если вы зависите от этого, это может объяснить, что происходит?

Редактировать:

Когда я сказал System.currentTimeMillis(), я думаю, что я ошибся,Я думал, что System.currentTimeMillis() похожа на функцию GetTickCount() Windows (то есть измеряет время, которое не зависит от системного времени), но на самом деле это не так.Так что из курса это может измениться, но это было не моей точки зрения: очевидно, отсчеты тиков, измеренные системным таймером, могут также идти назад на два или три, даже игнорируя изменения системного времени.Не уверен, поможет ли это, но спасибо Raedwald за то, что он указал на возможность изменения системного времени, поскольку я не это имел в виду.

3 голосов
/ 26 июля 2011

Я знаю, что вы смотрели в jconsole, но может быть полезно отправить сигнал 3 процессу (то есть kill -3) и выложить здесь еще один дамп потока.Или, если вы действительно хотите вникнуть в детали, то вы можете рассмотреть возможность быстрого или быстрого выполнения одного или нескольких дампов pstack / jstack зависшего процесса, чтобы показать, где на самом деле находятся потоки.В Интернете доступна информация о том, как соотнести эту информацию с дампом потока Java.

Кроме того, «одним из наших серверов» вы говорите, что проблема воспроизводима на одном сервере, но никогда не возникает на другомсерверы?Это указывает на проблему с этим одним сервером.Убедитесь, что на ваших серверах все одинаково и что на этом оборудовании нет никаких проблем.

Наконец, это не может быть проблемой Java как таковой.Thread.sleep (long) является нативным методом (отображается непосредственно на управление потоками базовой операционной системы), поэтому убедитесь, что ваша ОС обновлена.

2 голосов
/ 27 июля 2011

Рассматривали ли вы использование Таймер & TimerTask .

Вот простой фрагмент, который может помочь.

import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;

public class Example {

    public static void main(String args[]) {
        Timer timer = new Timer();

        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                Calendar instance = Calendar.getInstance();
                System.out.println("time: " + instance.getTime() + " : " + instance.getTimeInMillis());

                // check db for new jobs and
                // kick off thread if necessary
            }
        };

        int startingDelay = 0; // timer task will be started after startingDelay
        int period = 1000; // you are using it as sleeping time in your code
        timer.scheduleAtFixedRate(task, startingDelay, period);
    }

}

РЕДАКТИРОВАТЬ

Согласно изученным мною обсуждениям, Thread.sleep() is the sign of poorly designed code. Причины

  • ... Поток не теряет права собственности ни на какие мониторы (из документации).
  • Блокирует поток от выполнения.
  • И, очевидно, это не дает никакой гарантии, что выполнение начнется после времени ожидания.
  • Для меня это настолько примитивно использовать Thread.спать().Существует целый пакет, посвященный параллелизму .

Какой из них лучше, чем Thread.sleep ()?Что поднимает другой вопрос.Я бы посоветовал вам взглянуть на Concurrency главу из книги Effective Java.

1 голос
/ 19 июля 2011

Thread.sleep () не является хорошей практикой в ​​программировании на Java. Просто Google "Thread.sleep () плохо?" и вы поймете мою точку зрения.

Во-первых, он делает текущий поток недоступным для других частей программы, особенно если он многопоточный. Возможно, именно поэтому вы испытываете зависание.

Во-вторых, было бы катастрофическим, если бы текущим потоком был EDT (Event Dispatch Thread), а приложение имело Swing GUI.

Лучшей альтернативой будет Object.wait () :

final Object LOCK = new Object();
final long SLEEP = 1000;

public void run() {
  while (true) {
    // check db for new jobs and 
    // kick off thread if necessary

    try {
      synchronize (LOCK) {
        LOCK.wait(SLEEP);
      }
    } catch (InterruptedException e) {
      // usually interrupted by other threads e.g. during program shutdown
      break;
    }

  }
}
0 голосов
/ 25 июля 2011

возможно, вы можете попробовать другой инструмент, кроме Jconsole, чтобы сначала подтвердить, что он является блоком в спящем API.

Например, попробуйте вручную с помощью jstack напечатать его в файл много раз и проверить результат.

Или используйте более качественный инструмент, такой как Youkit (commercail), если у вашей организации есть лицензия на подробное профилирование приложения, или удаленную отладку (возможно, не в производстве)

ИЛИ Вы можетепроверьте, выполняется ли код «// проверка БД для новых заданий» во время.проверка логов, или профиля, или любого другого метода зависит от вашего приложения ........ Если проверка БД очень быстрая, а затем спит 1 секунду, если очень вероятно, что вы всегда видите сон в трассировке стека простопотому что сравниваемая вероятность ....

...