Исполнители Java:как я могу остановить отправленные задачи?
-
07-07-2019 - |
Вопрос
Я отправил задачу с использованием исполнителей, и мне нужно, чтобы она остановилась через некоторое время (например,5 минут).Я пробовал сделать так:
for (Future<?> fut : e.invokeAll(tasks, 300, TimeUnit.SECONDS)) {
try {
fut.get();
} catch (CancellationException ex) {
fut.cancel(true);
tasks.clear();
} catch(ExecutionException ex){
ex.printStackTrace(); //FIXME: gestita con printstack
}
}
Но я всегда получаю сообщение об ошибке:У меня есть общий вектор, который необходимо изменить задачами, а затем прочитать потоком, и даже если я остановлю всю задачу, если произойдет тайм-аут, я получу:
Exception in thread "Thread-1" java.util.ConcurrentModificationException
Здесь что-то не так?Как я могу остановить отправленные задачи, которые все еще работают через 5 минут?
Решение
Просто потому, что ты звонишь cancel()
на Future
не означает, что задача остановится автоматически.Вам нужно проделать некоторую работу внутри задачи, чтобы убедиться, что она остановится:
- Использовать
cancel(true)
чтобы на задачу было отправлено прерывание. - Ручка
InterruptedException
.Если функция в вашей задаче выдаетInterruptedException
, убедитесь, что вы завершили работу корректно как можно скорее после обнаружения исключения. - Периодически проверяйте
Thread.currentThread().isInterrupted()
если задача выполняет непрерывные вычисления.
Например:
class LongTask implements Callable<Double> {
public Double call() {
// Sleep for a while; handle InterruptedException appropriately
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
System.out.println("Exiting gracefully!");
return null;
}
// Compute for a while; check Thread.isInterrupted() periodically
double sum = 0.0;
for (long i = 0; i < 10000000; i++) {
sum += 10.0
if (Thread.currentThread().isInterrupted()) {
System.out.println("Exiting gracefully");
return null;
}
}
return sum;
}
}
Кроме того, как упоминалось в других сообщениях: ConcurrentModificationException
может быть выброшено, даже если используется потокобезопасный Vector
класс, потому что итераторы, которые вы получаете от Vector
не являются потокобезопасными и поэтому должны быть синхронизированы.Расширенный цикл for использует итераторы, поэтому будьте осторожны:
final Vector<Double> vector = new Vector<Double>();
vector.add(1.0);
vector.add(2.0);
// Not thread safe! If another thread modifies "vector" during the loop, then
// a ConcurrentModificationException will be thrown.
for (Double num : vector) {
System.out.println(num);
}
// You can try this as a quick fix, but it might not be what you want:
synchronized (vector) { // "vector" must be final
for (Double num : vector) {
System.out.println(num);
}
}
Другие советы
ConcurrentModificationException
поступает из вашего вызова tasks.clear ()
, пока ваши эксперты работают с вашими tasks
Vector код>. То, что вы можете попытаться сделать, это вызвать
shutdownNow ()
в вашем ExecutorService
Наиболее распространенный случай для ConcurrentModificationException
- это когда vector
изменяется одновременно с итерацией. Часто это будет сделано в один поток. Вам нужно удерживать блокировку на Vector
на протяжении всей итерации (и избегать взаимоблокировки).
fut.get () является блокирующим вызовом, даже после истечения времени ожидания вы будете блокировать, пока задача не будет выполнена. Если вы хотите остановиться как можно ближе к 5-минутной отметке, вам нужно проверить флаг прерывания, я просто рекомендую вам сделать это с помощью метода Thread.isInterrupted (), который сохраняет состояние прерывания. Если вы хотите просто немедленно прекратить работу и не нужно очищать какое-либо состояние, генерируйте исключение, которое будет перехвачено Future и указано вам как ExecutionException.
fut.cancel (true) ничего не делает, так как метод invokeAll () уже сделал это для вас.
Если вы не используете " задачи " Сбор в другом месте, вам, вероятно, не нужно вызывать clear () для него. Это не будет источником вашей проблемы, так как метод invokeAll () выполняется со списком к тому времени, когда вы вызываете clear (). Но если вам нужно начать формировать список новых задач для выполнения, я предлагаю вам сформировать новый список задач, а не использовать старый список новых задач.
К сожалению, у меня нет ответа на вашу проблему. Я не вижу достаточно информации здесь, чтобы диагностировать это. Ничто в предоставленном вами фрагменте кода не указывает на неправильное (только ненужное) использование библиотечных классов / методов. Возможно, если вы включили полную трассировку стека вместо ошибки в одну строку.
Поместите fut.cancel (true);
в блок finally