Программа продолжает работать, несмотря на InterruptedException - PullRequest
6 голосов
/ 12 августа 2011

Я начал изучать Java, и теперь я нахожусь в главе параллелизма.Прочитав кое-что о параллелизме, я попробовал собственный пример.

public class Task implements Runnable{

public void run() {
    while(!Thread.interrupted()) {
        try {
            System.out.println("task");
            TimeUnit.SECONDS.sleep(2);
        }catch (InterruptedException e) {
            System.out.println("interrupted");
        }
    }
}

}

public static void main(String[] args) throws Exception {
    ExecutorService exec = Executors.newCachedThreadPool();
    exec.execute(new Task());
    TimeUnit.SECONDS.sleep(10);
    exec.shutdownNow();
}

Проблема в том, что я ожидал увидеть следующий вывод:

task
task
task
task
task
interrupted

, но после того, как я получил это, программа продолжает печатать, пока я не закрою ее.
Итак, мой вопрос: что я делаю не так?почему программа продолжает печатать?

Ответы [ 4 ]

7 голосов
/ 12 августа 2011

Когда вы выключаете исполнителя, он пытается остановить выполняемые задачи, прерывая их.Это вызывает исключение InterruptedException, но вы просто проглатываете его и продолжаете.Вы должны вернуться в свое предложение catch и / или сбросить статус прерывания потока, вызвав Thread.currentThread.interrupt(), что сбросит статус прерывания и выйдет из цикла.

4 голосов
/ 12 августа 2011

Раздел о прерываниях в Учебниках Java по параллелизму достаточно хорошо объясняет проблему:

Флаг состояния прерывания

Механизм прерывания реализован с использованием внутренний флаг, известный как статус прерывания. Вызов Thread.interrupt устанавливает этот флаг. Когда поток проверяет наличие прерывания вызывая статический метод Thread.interrupted, статус прерывания очищено. Нестатический метод isInterrupted, который используется одним поток для запроса статуса прерывания другого, не изменяет флаг состояния прерывания.

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

Таким образом, когда вы перехватываете InterruptedException внутри цикла, статус прерывания уже сбрасывается, и, таким образом, следующий вызов Thread.interrupted () вернет false, что, в свою очередь, продолжит выполнение цикла while. Чтобы остановить цикл, у вас есть следующие опции:

  • Используйте break для выхода из цикла
  • Используйте return для выхода из всего метода
  • Переместить блок try-catch за пределы цикла while (как предложено Натаном Хьюзом)
  • Вызовите interrupt () для текущего потока, чтобы снова установить флаг прерывания
  • Используйте отдельное логическое значение для управления циклом и соответственно установите этот флаг в блоке catch
  • Сделайте задачу повторяющейся, используя ScheduledExecutorService и отбрасывая цикл из метода run вашего Runnable
4 голосов
/ 12 августа 2011

Вы все еще находитесь в цикле while, добавьте разрыв или какой-либо другой выход из цикла.

    catch (InterruptedException e) {
        System.out.println("interrupted");
        break;
    }

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

Как подробно объясняют Саймон и Амир, условие завершения вашего цикла неожиданно недостаточно.

1 голос
/ 12 августа 2011

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

источник: http://download.oracle.com/javase/1,5.0/docs/api/java/util/concurrent/ExecutorService.html#shutdownNow()

...