Остановка цикличного выполнения распараллеливает в Java

Я использую поток, который непрерывно читает из очереди.

Что-то как:

public void run() {
    Object obj;
    while(true) {
        synchronized(objectsQueue) {
            if(objectesQueue.isEmpty()) {
                try {
                    objectesQueue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                obj = objectesQueue.poll();
            }
        }

        // Do something with the Object obj
    }
}

Что лучший способ состоит в том, чтобы остановить этот поток?

Я вижу две опции:

1 - С тех пор Thread.stop() удерживается от использования, я могу реализовать a stopThisThread() метод, который использует n атомарную условную переменную проверки.

2 - Отправьте Смертельный Объект-событие или что-то как этот очереди. Когда поток выбирает смертельное событие, он выходит.

Я предпочитаю 1-й путь, однако, я не знаю, когда звонить stopThisThread() метод, поскольку что-то могло бы идти, это - путь к очереди, и стоповый сигнал может прибыть сначала (не желательный).

Какие-либо предложения?

7
задан AplusKminus 28 June 2016 в 17:38
поделиться

6 ответов

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

Если вы хотите остановиться как можно скорее, я предлагаю вам сделать это

BlockingQueue<O> queue = ...

...

public void run() {
   try {
       // The following test is necessary to get fast interrupts.  If
       // it is replaced with 'true', the queue will be drained before
       // the interrupt is noticed.  (Thanks Tim)
       while (!Thread.interrupted()) {
           O obj = queue.take();
           doSomething(obj);
       }
   } catch (InterruptedException ex) {
       // We are done.
   }
}

Чтобы остановить поток t , созданный с помощью этого метода run , просто вызовите t.interrupt (); .

Если вы сравните приведенный выше код с другими ответами, вы заметите, как использование BlockingQueue и Thread.interrupt () упрощает решение.

Я бы также сказал, что дополнительный флаг stop не нужен, а в целом потенциально вреден. Хорошо работающий рабочий поток должен учитывать прерывания. Неожиданное прерывание просто означает, что рабочий запускается в контексте, который исходный программист не ожидал. Лучше всего, если рабочий будет делать то, что ему говорят ... т.е. он должен остановиться ... соответствует это или нет концепции исходного программиста.

6
ответ дан 7 December 2019 в 05:19
поделиться

Я думаю, что ваши два случая на самом деле демонстрируют одинаковое потенциальное поведение. Во втором случае рассмотрим, как поток A добавляет событие DeathEvent, после которого поток B добавляет FooEvent. Когда ваш поток задания получает событие DeathEvent, за ним по-прежнему существует FooEvent, что является тем же сценарием, который вы описываете в Варианте 1, если вы не пытаетесь очистить очередь перед возвратом, но тогда вы по существу сохраняете поток живым, когда вы пытаются сделать, это остановить.

Я согласен с вами, что первый вариант более желателен. Возможное решение будет зависеть от того, как заполнена ваша очередь. Если он является частью вашего класса рабочего потока, вы можете настроить свой метод stopThisThread () на установку флага, который будет возвращать соответствующее значение (или генерировать исключение) из вызова постановки в очередь, то есть:

MyThread extends Thread{
  boolean running = true;

  public void run(){
    while(running){
      try{
        //process queue...
      }catch(InterruptedExcpetion e){
        ...
      }
    }
  }

  public void stopThisThread(){
    running = false;
    interrupt();
  }

  public boolean enqueue(Object o){
    if(!running){
       return false;
         OR
       throw new ThreadNotRunningException();
    }
    queue.add(o);
    return true;
  }
}

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

0
ответ дан 7 December 2019 в 05:19
поделиться

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

ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
service.scheduleWithFixedDelay(myThread, 1, 10, TimeUnit.SECONDS);

этот пример будет запускать ваш поток с задержкой в ​​10 секунд, это означает, что по завершении одного запуска он перезапускается через 10 секунд. И вместо того, чтобы изобретать велосипед, вы получаете

service.shutdown()

, в while (true) больше нет необходимости.

ScheduledExecutorService Javadoc

1
ответ дан 7 December 2019 в 05:19
поделиться

Я обычно помещаю флаг в класс, в котором есть поток, и в моем коде потока я бы сделал это. (ПРИМЕЧАНИЕ: вместо while (true) я делаю while (flag))

Затем создайте метод в классе, чтобы установить флаг в false;

private volatile bool flag = true;

public void stopThread()
{
   flag = false;
}

    public void run() {
        Object obj;
        while(flag) {
            synchronized(objectsQueue) {
                if(objectesQueue.isEmpty()) {
                    try {
                        objectesQueue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    obj = objectesQueue.poll();
                }
            }

            // Do something with the Object obj
        }
    }
0
ответ дан 7 December 2019 в 05:19
поделиться

В потоке чтения есть логическая остановка переменной. Если вы хотите, чтобы этот поток остановился, установите для thius значение true и прервите поток. В потоке чтения, когда это безопасно (когда у вас нет необработанного объекта), проверьте состояние переменной остановки и вернитесь из цикла, если она установлена. как показано ниже.

public class readerThread extends Thread{
    private volitile boolean stop = false;
    public void stopSoon(){
        stop = true;
        this.interrupt();
    }
    public void run() {
        Object obj;
        while(true) {
            if(stop){
                return;
            }
            synchronized(objectsQueue) {
            if(objectesQueue.isEmpty()) {
                try {
                    objectesQueue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(stop){
                    return;
                }    
                obj = objectesQueue.poll();
                // Do something with the Object obj
            }
        }
    }


}
public class OtherClass{
     ThreadReader reader;
     private void start(){
          reader = ...;
          reader.start();
     }

     private void stop(){
          reader.stopSoon();
          reader.join();     // Wait for thread to stop if nessasery.
     }
}
1
ответ дан 7 December 2019 в 05:19
поделиться

Подход 1 является предпочтительным.

Просто установите для поля volatile stop значение true и вызовите interrupt () в работающем потоке. Это заставит любые методы ввода-вывода, которые ожидают возврата, с InterruptedException (и, если ваша библиотека написана правильно, это будет обработано корректно).

0
ответ дан 7 December 2019 в 05:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: