Java: вызов прерываемых методов из кода

Я читаю 7-ю главу Java Concurrency на практике.

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

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

Я не понял этого до конца.

Означает ли это, что если я вызову Thread.sleep в своем методе, мне придется вызывать его в цикле или что-то в этом роде?

Кто-нибудь может объяснить, почему это должно быть сделано именно так?


person Vinoth Kumar C M    schedule 25.10.2011    source источник
comment
Здесь дается разумное объяснение: stackoverflow .com/questions/7751690/   -  person Cole    schedule 14.02.2012


Ответы (4)


Я вызываю Thread.sleep в своем методе, мне придется вызывать его в цикле или что-то в этом роде?

Thread.sleep() вызовет InterruptedException, когда текущий поток будет прерван (другим потоком). Это ваш выбор, как на это реагировать. Если вы хотите спать, независимо от того, пытается ли кто-то вас прервать, тогда да, вам нужно построить какой-то цикл вокруг блока try-catch. Вероятно, вам следует использовать часы (например, System.nanoTime()), чтобы проверить, как долго вы спали до того, как было выбрано исключение, а затем продолжить спать в течение оставшегося времени и т. д.

Обратите внимание, что InterruptedException вызывается только, если другой поток прервал текущий поток, вызвав (current)Thread.interrupt(). Это не происходит само по себе, поэтому в целом вам не нужно создавать циклы вокруг снов или чего-то подобного. Обычно потоки прерываются только по уважительной причине (например, закрытие приложения), поэтому вы, вероятно, захотите поддерживать отмену/прерывание, если нет особых причин не делать этого. «Особой причиной» может быть, например, запись на устройство ввода-вывода и попытка гарантировать, что все данные будут записаны, независимо от попыток отмены.

person Joonas Pulakka    schedule 25.10.2011

Сначала некоторое объяснение:

Прерванный статус потока — это, по сути, логический флаг, который устанавливается в значение «истина» с помощью interrupt(). Текущее состояние этого флага можно прочитать с помощью Thread.currentThread().isInterrupted().

Если прерываемая операция (например, Object.wait() или Thread.sleep()) находит установленный флаг прерывания, она выдает InterruptedException и в то же время очищает (устанавливает значение "false") флаг, который может выглядеть следующим образом:

if ( Thread.interrupted() ) { throw new InterruptedException(); }

Обратите внимание и запомните, что Thread.interrupted() неявно очищает флаг прерывания! Это означает, что к моменту выполнения вашего catch( InterruptedException ie) {...} сам поток уже не знает, что он был прерван.

Тем не менее, давайте посмотрим на два примера:

Сначала пример задачи, которая поддерживает отмену. Здесь нам все равно, как далеко продвинется задача до того, как будет прервана:

  public void run() {

    int x = 0;

    try {

      while (x < 10) {
        Thread.sleep(1000); // Some interruptible operation
        x++;
      }

      System.out.println("x = " + x);

    } catch (InterruptedException ie) {

      System.out.println("Interrupted: x = " + x);

      // We know we've been interrupted. 
      // Let the caller know it, too:
      Thread.currentThread().interrupt();
    }

  }

Этот код пытается посчитать x от 0 до 10. Если его не прервать, он завершится и выведет «x = 10». Однако, если поток прерывается между ними, будет выброшено InterruptedException, прервав текущую задачу увеличения x. В этом случае вывод может быть любым, от «Прервано: x = 0» до «Прервано: x = 9», в зависимости от того, когда поток был прерван.

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

Теперь, если важно, чтобы наша задача выполнялась полностью, чтобы вывод всегда был «x = 10», что означает, что задача не поддерживает отмену, нам нужен другой подход:

  public void run() {
    int x = 0;

    boolean wasInterrupted = false; // <- This is the local variable to store the interruption status

    while (x < 10) {

      wasInterrupted = wasInterrupted || Thread.interrupted(); // not really needed in this case, but for the sake of completeness...

      try {

        Thread.sleep(1000); // <- Some interruptible operation

      } catch (InterruptedException e) {
        wasInterrupted = true;
      }

      x++;
    }

    System.out.println("x = " + x);

    if ( wasInterrupted ) {
      Thread.currentThread().interrupt();
    }

  }

В этом случае мы продолжаем обработку даже после InterruptedException, пока задача не будет завершена. Чтобы оставаться вежливым, если мы обнаруживаем прерывание, мы сохраняем это условие в wasInterrupted, чтобы мы могли правильно установить флаг прерывания перед возвратом из метода.

Вот что имеется в виду

должен сохранять статус прерывания локально и восстанавливать его непосредственно перед возвратом.

Он говорит «должен», потому что мы строго не обязаны обрабатывать прерывания таким образом — мы можем просто игнорировать любые InterruptedException и просто завершить нашу задачу, а затем вернуться. Однако это не рекомендуемая практика, упомянутая выше, и в некоторых сценариях может вызвать проблемы.

person JimmyB    schedule 27.10.2011

Насколько я понимаю это: долго работающая служба, которая сама не может или не должна быть прервана, вызывает другие методы, которые можно прервать. Таким образом, эта долго работающая служба должна быть в состоянии обнаружить это и сообщить об этом с помощью метода или флага. Но он должен иметь возможность повторить операцию, а не просто вызывать InterruptedException.

Вызов метода, который блокирует, означает, что текущее выполнение заблокировано и ожидает, пока метод блокировки не вернет значение. Это можно сделать в цикле. Затем вы знаете, был ли вызов метода успешным или вызванный метод прерван.

person hellectronic    schedule 25.10.2011

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

//Let's say you have integer data named "a"...
a = 45646;

//Normally after interruption, and terminating the activity data a is currently
//referencing @memory will be released...

//If you want to continue using data in a you have to save it somewhere
// persistant(Let's say a file)
void onInterruptionDetected()
{
    saveToSomeFile(a, "temp.txt");
}

//After re-execution of activity(Assuming we need a's previous data in order to 
// continue...), we can recover a's data(which is 45646) in previous interruption...
void onResumeDetected()
{
    a = loadFromFile("temp.txt")
}

Надеюсь, это поможет, я все еще сплю, может быть ошибка :)

person Gökhan Barış Aker    schedule 25.10.2011