Ejecutores de Java: ¿cómo puedo detener las tareas enviadas?
-
07-07-2019 - |
Pregunta
He enviado una tarea usando ejecutores y necesito que se detenga después de un tiempo (por ejemplo, 5 minutos). He intentado hacer así:
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
}
}
Pero siempre recibo un error: tengo un Vector compartido que debe ser modificado por las tareas y luego leído por un hilo, e incluso si detengo toda la tarea, si ocurre el tiempo de espera obtengo:
Exception in thread "Thread-1" java.util.ConcurrentModificationException
¿Hay algo mal? ¿Cómo puedo detener las tareas enviadas que siguen funcionando después de 5 minutos?
Solución
El hecho de que llame a cancel ()
en Future
no significa que la tarea se detendrá automáticamente. Debe hacer un trabajo dentro de la tarea para asegurarse de que se detendrá:
- Use
cancel (true)
para que se envíe una interrupción a la tarea. - Manejar
InterruptedException
. Si una función en su tarea arroja unaInterruptedException
, asegúrese de salir con gracia lo antes posible al detectar la excepción. - Verifique periódicamente
Thread.currentThread (). isInterrupted ()
si la tarea realiza un cálculo continuo.
Por ejemplo:
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;
}
}
Además, como han mencionado otras publicaciones: ConcurrentModificationException
se puede lanzar incluso si se usa la clase Vector
segura para subprocesos, porque los iteradores que obtienes de Vector
no es seguro para subprocesos y, por lo tanto, debe sincronizarse. El bucle avanzado utiliza iteradores, así que ten cuidado:
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);
}
}
Otros consejos
La ConcurrentModificationException
proviene de su llamada a task.clear ()
mientras su Exceutors itera sobre sus tareas
Vector . Lo que puede intentar hacer es llamar a
shutdownNow ()
en su ExecutorService
El caso más común para ConcurrentModificationException
es cuando el vector
se está modificando al mismo tiempo que se está iterando. A menudo esto se hará en un solo hilo. Debe mantener bloqueado el Vector
durante toda la iteración (y tener cuidado de no llegar a un punto muerto).
fut.get () es una llamada de bloqueo, incluso después del tiempo de espera, bloqueará hasta que se complete la tarea. Si desea detenerse lo más cerca posible de la marca de 5 minutos, debe verificar el indicador de interrupción, solo le recomiendo que lo haga utilizando el método Thread.isInterrupted () que conserva el estado de interrupción. Si desea detenerse de inmediato y no necesita limpiar ningún estado, inicie una excepción que será detectada por el futuro y se le indicará como una ExecutionException.
fut.cancel (true) no hace nada ya que el método invokeAll () ya lo ha hecho por usted.
A menos que use las " tareas " Colección en otro lugar, probablemente no necesite llamar a clear () en ella. Este no será el origen de su problema ya que el método invokeAll () se realiza con la Lista para cuando llame a clear (). Pero, si necesita comenzar a formar una lista de nuevas tareas para ejecutar, le sugiero que forme una nueva Lista de tareas, no use una Lista de Tareas nuevas.
Desafortunadamente, no tengo una respuesta para su problema. No veo suficiente información aquí para diagnosticarlo. Nada en el fragmento de código que proporcionó indica un uso incorrecto (solo innecesario) de las clases / métodos de la biblioteca. Quizás si incluyó un seguimiento completo de la pila, en lugar del error de una línea.
Pon el fut.cancel (true);
en el bloque finalmente