Как остановить поток без метода stop ()? - PullRequest
5 голосов
/ 13 июля 2010

У меня есть вопрос о потоках Java.Вот мой сценарий:

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

Ответы [ 6 ]

5 голосов
/ 13 июля 2010

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

public class MyThread extends Thread {
   private volatile boolean running = true;

   public void stop() {
      running = false;
   }

   public void run() {
      while (running) {
         // do your things
      }    
   }
}

Таким образом, вы можете безболезненно прервать поток, то есть, не выбрасывая InterruptedException.

4 голосов
/ 13 июля 2010

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

class Scratchpad {
    public static void main(String[] a) {
        Thread t = new Thread(new Runnable() {
            public void run() {doWork();}
        });
        t.start();

        try {
            Thread.sleep(50);
        } catch (InterruptedException ie) {}

        t.interrupt();
    }

    private static void doWork() {
        for ( long i = 1; i != 0; i *=5 );
    }
}

В приведенном выше случае единственное жизнеспособное решение - это переменная-флаг, которая выходит из цикла в начале отмены, ala @ inflagranti.

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

try {
   while(true) {
      SomeType next = queue.take();
      if ( next == POISON_PILL ) {
          return;
      }
      consume(next);
   }
} catch //...

РЕДАКТИРОВАТЬ :

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

2 голосов
/ 13 июля 2010

Вы можете прервать поток, его цепочка выполнения будет генерировать исключение InterruptedException большую часть времени (см. Особые случаи в документации ).

0 голосов
/ 13 июля 2010

Согласно API java.lang.Thread, вы должны использовать метод interrupt() и проверять флаг isInterrupted(), пока выполняете некоторую трудоемкую отменяемую операцию.Такой подход позволяет иметь дело с различными видами «ситуаций ожидания»:1. методы wait (), join () и sleep () выдают InterruptedExcetion после того, как вы вызовете метод interrupt()2. Если поток заблокирован java.nio.channels.Selector, он завершит работу селектора3. Если вы ожидаете, что поток ввода-вывода получит ClosedByInterruptException, но в этом случае ваше средство ввода-вывода должно реализовать интерфейс InterruptibleChannel.

Если невозможно вообще прервать это действие, вы можете просто отказаться от предыдущего потока и получить результаты из нового.Вы можете сделать это с помощью java.util.concurrent.Future и java.util.concurrent.ExecutorService.

Следующий фрагмент кода Cosider:

public class RequestService<Result> {

private ExecutorService executor = Executors.newFixedThreadPool(3);

private Future<Result> result;

  public Future<Result> doRequest(){
    if(result !=null){
        result.cancel(true);
    }
    result = executor.submit(new Callable<Result>() {
        public Result call() throws Exception {
            // do your long-running service call here
        }
    });
    return result;
  }

}

Future Объект здесь представляет результаты вызова службы.Если вы вызываете метод doRequest еще раз, он пытается отменить предыдущее задание, а затем пытается отправить новый запрос.Поскольку пул потоков содержит более одного потока, вам не придется ждать, пока предыдущий запрос не будет отменен.Новый запрос отправляется немедленно, и метод возвращает вам новый результат запроса.

0 голосов
/ 13 июля 2010

Существует метод setPriority(int) для Thread. Вы можете установить первому потоку его приоритет следующим образом:

Thread t = new Thread(yourRunnable);
t.start();
t.setPriority(Thread.MIN_PRIORITY); // The range goes from 1 to 10, I think

Но это не убьет вашу нить. Если у вас есть только два потока, использующих ваш runnable, то это хорошее решение. Но если вы создаете потоки в цикле и всегда устанавливаете приоритет последнего потока на минимум, вы получите много потоков.
Если это то, что собирается делать приложение, взгляните на ThreadPool. Это не существующий класс в Java API. Вам придется создать его самостоятельно.
ThreadPool - это еще один Thread, который управляет всеми другими Threads так, как вы хотите. Вы можете установить максимальное количество запущенных потоков. И в этом ThreadPool вы можете реализовать систему, которая автоматически управляет приоритетом Thread. Например: вы можете сделать так, чтобы старые потоки получали больший приоритет, чтобы вы могли правильно завершить их.

Итак, если вы знаете, как работать с ThreadPool, это может быть очень интересно.

0 голосов
/ 13 июля 2010

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

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

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

Таким образом, вы можете реализовать стек таких объектов, и ваш поток просто выполнит stack.peek ().процесс();каждая итерация, поэтому добавление новой, более важной задачи в стек автоматически останавливает выполнение любой предыдущей задачи.

Это приводит к гораздо более гибкому планированию - например, вы можете сделать, чтобы process () возвращал false, если тамему нечего делать, и в этот момент ваш планировщик может перейти к следующему элементу в стеке и попробовать его метод 'process (), предоставляющий вам серьезные возможности многозадачности в одном потоке без перегрузки ваших ресурсов (сеть, I').м угадал)

...