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

Я читаю 7-ю главу "Параллелизм Java на практике".

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

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

Я не понял это полностью.

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

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

4 ответа

Решение

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

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

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

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

Прерванный статус потока - это в основном логический флаг, который устанавливается в "true" 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 = 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и просто завершить нашу задачу, а затем вернуться. Это не хорошая практика, упомянутая выше, однако, и может вызвать проблемы в некоторых сценариях.

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

//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")
}

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

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

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

Другие вопросы по тегам