Как изящно деградировать программу, когда есть ожидающие потоки? - PullRequest
2 голосов
/ 21 декабря 2011

Мне известна изящная конструкция завершения потока:

public class Foo implements Runnable {
   private volatile boolean stop = false;

   public void stop() {
      stop = true;
   }

   public void run() {
      while (!stop) {
         ...
      }
   }
}

Но если какой-то поток ожидает чего-то внутри какого-то объекта (используя wait(), без ограничения по времени), то эта конструкция победит 'не полезно останавливать этот поток, потому что он уже прошел условие в цикле while, поэтому он будет работать вечно.

Итак, как правильно остановить ожидающие потоки?

Ответы [ 4 ]

2 голосов
/ 21 декабря 2011

Если поток на самом деле ожидает чего-то, вы должны вызвать метод Thread.interrupt () для прерывания потока.Вместо проверки вашей пользовательской переменной в цикле while используйте Thread.isInterrupted () или Thread.interrupted () - один сбрасывает флаг прерывания, другой - нет.

Если вы чего-то ждете,Я предположил, что вам нужно было поймать InterruptedException , не так ли?

2 голосов
/ 21 декабря 2011

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

1 голос
/ 22 декабря 2011

Каждый корректный метод блокировки объявляет проверенное исключение InterruptedException, которое служит именно этой цели: уведомить, что поток был прерван во время его блокировки.

Вы должны перехватить это исключение,и на самом деле это может заменить ваше поле stop.

Например, давайте рассмотрим систему ведения журналов, которая будет записывать сообщения в файл в выделенном потоке (чтобы время, потраченное на ввод-вывод, не влияло на ваше приложение.- при условии, что он не слишком тяжелый IO).

Каждый поток имеет флаг initerrupted , который можно прочитать через Thread.currentThread().isInterrupted().Вы пытаетесь сделать что-то вроде этого:

class Logger {
  private final File file = ...;
  private final BlockingQueue<String> logLines = new LinkedBlockingQueue<String>();
  private final Thread loggingThread = new Thread(new Runnable(){
    @Override public void run() {
      PrintWriter pw;
      try {
        pw = new PrintWriter(new BufferedOutputStream(new FileOutputStream(file)));
        while (!Thread.currentThread().isInterrupted()) {
          try {
            pw.println(logLines.take());
          } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // good habit: make sure the interrupt status is set
          }
        }
        pw.flush();
        pw.close();
      } catch (IOException e) { ... flush and close pw if not null and open ... }
    }
  });
  { loggingThread.start(); }
  public void log(final String line) { logLines.offer(line); } // will always work, because logLines is a LinkedBQ.
  public void stop() { loggingThread.interrupt(); }
}

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

class Logger {
  ...
  {
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
      @Override public void run() { close(); }
    }));
  }
}

Это заставит JVM вызвать close () (поэтому прерывает поток, очищает и закрывает файл) перед завершением.

0 голосов
/ 21 декабря 2011

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

В противном случае все зависит от того, как вы ожидаете в потоке,Вы можете использовать wait(1000), затем проверить флаг и подождать еще.Вы можете ждать сообщения из очереди блокировки, вы можете использовать блокировки / условия, даже wait/notify может сработать, в этом случае вам нужно будет правильно обрабатывать прерывания.

...