Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java EE (J2EE) и Spring > Выполнение методов класса в отдельном потоке


Автор: sergakrem 29.8.2006, 17:24
Всем, доброе время суток.

Подскажите пожалуйста ответ на следующий вопрос:

Есть некоторый класс, один из методов которого выполняет достаточно ресурсоемкую и длительную опреацию (вызвая толпу других методов и т.д.). Нужно следить за статусом выполнения этого метода на веб-странице. Для этого создана сама страница и сервлет, который будет заниматься опросом. 
Но так же стоит задача управлением выполнения этого кода (реализовывающего метод), я имею ввиду - старт, пауза, resume и стоп.

Т.е. необходимо, выполнить данный код в потоке, который можно запустить, остановить, и т.д.
Сам код (содержимое метода) был написан таким образом, что его достаточно сложно модифицировать для грамотной реализации управления состояниями, т.е. хотелось бы просто выполнять его в потоке в том виде, в котором он есть.

Я решал это приблизительно таким образом:

Код

  private void processSearch(final boolean forward) {

    Thread thread = new Thread(new Runnable() {
      Log procLog = LogFactory.getLog(this.getClass());
      public void run() {
    if (forward) { // Processing to forward ==================================
              ...
    } else { // Processing to backward ====================================
              ...
    }
      }
    });
  }



(Здесь все, что находится внутри public void run() { ... } собственно и есть само тело метода)


У Thread методы suspend(), stop() и resume() объявленны, как Deprecated - соответственно, не  хотелось бы ими пользоваться, хотя они по-видимому решили бы задачу.

Собственно, вопрос заключается в том: есть ли у кого какие идеи решения такой задачи каким-нибудь более корректным способом, нежели использования Deprecated методами.


Заранее, большое спасибо.

Автор: Stampede 29.8.2006, 19:29
Увы, единственный безопасный способ управлять состоянием процедуры, выполняющейся в отдельном потоке (приостановить, продолжить, завершить) - это выставлять ей извне всякие разные флажки.

Код

public class Search implements Runnable {
  public static int PAUSE = 1;
  public static int RESUME = 2;
  public static int STOP = 3;

  private int flag;

  public void startProcessing() {
    new Thread(this).start();
  }

  public synchronized void setFlag(int flag)
  {
    this.flag = flag;
    notifyAll();
  }

  public void run() {
  // здесь выполняется собственно процедура
  }
}


Что касается реагирования на флажки, то тут придется выделять фрагменты кода, представляющие более или менее законченную операцию, и по окончании каждой из них проверять значение флага и соостветственно предпринимать какие-то действия. Хорошо, если какая-то продолжительная операция выполняется в циклле - тогда проверку флага можно вставить внутрь тела цикла.

Тут может возникнуть вопрос: а зачем, собственно, весь этот геморрой? Чем были плохи ныне упраздненные методы pause(), resume() и  stop()?

Плохи они тем, что позволяют любому внешнему процессу весьма авторитарным образом (например, по инициативе юзера) вмешиваться в ход выполнения процедуры, будучи при этом абсолютно ни уха ни рыла в том, что именно в нем происходит. А происходить может все что угодно, в том числе достаточно деликатные операции, которые блокируют всякие другие ресурсы.

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

Таким образом, на смену авторитарному стилю управления потоками пришел более мягкий, демократический. Я бы назвал его методом делегирования полномочий. В этом, по-видимому, нашел отражение тот факт, что менеджеры среднего звена в IT-индустрии таки действительно очень мало смыслят в технологических тонкостях создаваемых продуктов, и если командный стиль годится в армии или там где-нибудь на буровой, то в таком тонком деле как создание ПО на девелоперов лучше все-таки не давить, а предоставить им относительную свободу реализации smile

Автор: sergakrem 31.8.2006, 19:09
Ok. Спасибо за ответ.
Хочу тогда посоветоваться по этому поводу

Цитата

...
Плохи они тем, что позволяют любому внешнему процессу весьма авторитарным образом (например, по инициативе юзера) вмешиваться в ход выполнения процедуры, будучи при этом абсолютно ни уха ни рыла в том, что именно в нем происходит. А происходить может все что угодно, в том числе достаточно деликатные операции, которые блокируют всякие другие ресурсы.


Т.е. я правильно понимаю - если я (как разработчик) знаю, что в моем коде (в данном случае в этом методе, который я хотел бы выполнять в потоке) не происходит никаких блокировок и других "деликатных" smile вещей, и в принципе - я могу ограничить логику использования этих методов - то при их использовании никакого вреда не будет (за исключением, простите, матюков компилятора об использовании deprecated методов)?


Хотя я вобщем-то подумал еще об одном способе - блокировать синхронизированный объект (если мне надо остановить выполнение кода), который повсеместно там используется в коде, и соответственно - отпускать его (если я хочу продолжить выполнение). Ну а остановка выполнения (функция stop) - просто прибивать ссылку на объект потока (хотя на сколько я помню - это не поможет), ну или какой-нибудь другой способ.

Автор: Stampede 31.8.2006, 20:08
Цитата(sergakrem @  31.8.2006,  10:09 Найти цитируемый пост)
Хотя я вобщем-то подумал еще об одном способе - блокировать синхронизированный объект (если мне надо остановить выполнение кода), который повсеместно там используется в коде, и соответственно - отпускать его (если я хочу продолжить выполнение).


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

Что я имею в виду? Ну прежде всего то, что логику, использующую блокировки, очень непросто писать и еще труднее отлаживать. Как минимум нужно очень хорошо представлять себе, как работают мониторы объектов и все, что с ними связано (например, поведение при повторном вхождении в синхронизированный участок кода). Так вот, даже если ты запрограммируешь все это дело и оно вроде как заработает, не факт что под нагрузкой не начнут вылазить всякие разные странности. Потому что, повторяю, смоделировать все возможные сценарии переходов состояний на этапе тестирования - задача в общем случае очень нетривиальная.

Поэтому если сравнивать варианты, я бы наверное лучше тупо в лоб задействовал "устаренные" методы - если, как ты говоришь, ты уверен, что ничего страшного при этом не произойдет. Хотя, конечно, трудно советовать что-то конкретное, не зная подробностей. Что хоть за задача-то? Если не военная тайна, конечно smile

Цитата(sergakrem @  31.8.2006,  10:09 Найти цитируемый пост)
Ну а остановка выполнения (функция stop) - просто прибивать ссылку на объект потока (хотя на сколько я помню - это не поможет)


Дак именно что не поможет! Если извне ничего не делать, то поток будет мослать до тех пор пока не дойдет до конца метода run(). Коль скоро ты не хочешь влезать внутрь готового кода, можно опять же насильно вызвать stop(), но в этом случае по крайней мере нужно оформить действия по освобождению ресурсов в виде конструкции try-finally:

Код

public void run() {
  try {
    // собственно код процедуры
  }
  finally {
    // действия по завершению
  }
}


Ди, и вот еще что я подумал. Какие-то смутные подозрение вызывает твое упоминание о том, что код процедуры содержит кучу синчронизованных участков. С чего бы это они там были? Ты точно уверен, что ничего не подвесишь прямыми вызовами suspend()?

Автор: sergakrem 31.8.2006, 21:09
Да задача-то конечно - не военная тайна. 
Просто она вряд ли будет сама по себе интересна. Эта веб-прога просто является фронт-эндом к одной системе, к которой у меня есть интерфейс через JNI.

Идея такая - есть объект (методы которого я хочу выполнять в потоке), который просто дергает за native функции, получает оттуда некоторые данные, приводит их в нужный вид а затем передает их на формирование вида в JSP.

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

Просто дело в том, что перед выполнением этого метода объект некоторое время действиями пользователя на странице инициализируется начальными данными, а только затем уже, их используя, обрабатывает результаты native функций, которые он потом дергает.

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

Автор: COVD2 2.9.2006, 18:14
Цитата

Тут может возникнуть вопрос: а зачем, собственно, весь этот геморрой? Чем были плохи ныне упраздненные методы pause(), resume() и  stop()?

Плохи они тем, что позволяют любому внешнему процессу весьма авторитарным образом (например, по инициативе юзера) вмешиваться в ход выполнения процедуры, будучи при этом абсолютно ни уха ни рыла в том, что именно в нем происходит. А происходить может все что угодно, в том числе достаточно деликатные операции, которые блокируют всякие другие ресурсы.

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


  Наверное, дело в том, что сам поток как раз и не знает. Если бы знал, то при поступлении команды, скажем, stop(), он бы мог продолжить выполнение до ближайшей безопасной точки и остановиться (или откатиться назад и снять, например, поставленную им блокировку smile ).  Безопасные точки знает программист. И в этих точках он может вставить код проверки состояния флага и описания действий потока.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)