2009-09-13 14 views
9

He enviado una tarea utilizando ejecutores y necesito que se detenga después de un tiempo (por ejemplo, 5 minutos). He intentado hacer así:Ejecutores de Java: ¿cómo puedo detener tareas enviadas?

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 me sale un error: Tengo un vector común que necesita ser modificado por las tareas y luego leer de un hilo, e incluso si dejo toda la tarea, si se produce el tiempo de espera, obtengo:

Exception in thread "Thread-1" java.util.ConcurrentModificationException 

¿Hay algún problema? ¿Cómo puedo detener las tareas enviadas que siguen funcionando después de 5 minutos?

+0

@Raffaele Di Fazio: He formateado el código - y añadió una llave de cierre, por favor verificar su exactitud. – akf

+0

Gracias, lo siento por el formato incorrecto. – Raffo

Respuesta

20

El hecho de que llame al cancel() en Future no significa que la tarea se detendrá automáticamente. Usted tiene que hacer algún trabajo dentro de la tarea para asegurarse de que va a dejar:

  • Uso cancel(true) de manera que una interrupción se envía a la tarea.
  • Manija InterruptedException. Si una función en su tarea arroja un InterruptedException, asegúrese de salir con gracia tan pronto como sea posible al detectar la excepción.
  • Compruebe 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; 
    } 
} 

También, como otros mensajes han mencionado: ConcurrentModificationException puede ser lanzado, incluso si se utiliza la clase thread-safe Vector, porque iteradores que obtenga de Vector no apta para subprocesos, y por lo tanto necesita ser sincronizado. La avanzada de bucle 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); 
    } 
} 
+0

Excelente, de alguna manera, nunca encontré el hilo. interrumpido() - ¡Puedo usar eso mañana! –

+7

Primero, llamar a future.cancel (true) no hace absolutamente nada. El contrato de invocación indica que cancelará las tareas antes de regresar, y la implementación utiliza un bloque final para garantizarlo. En segundo lugar, nunca llame a Thread.interrupted(), borrando el estado interrumpido del hilo. La mayoría de las implementaciones querrían usar Thread.isInterrupted(). La limpieza de la bandera debe ser examinada. En tercer lugar, no tiene que manejar la excepción InterruptedException a menos que esté utilizando métodos de bloqueo como la adquisición de bloqueo, y luego el compilador asegura que sí lo está. The FutureTask detectará excepciones. –

+1

@Tim Bender: tienes razón: future.cancel (verdadero) no hace nada, probado por mí mismo. Pero no he entendido lo que piensas que debo hacer ... – Raffo

0

El caso más común para ConcurrentModificationException es cuando el vector se modifica al mismo tiempo que se itera. A menudo esto se hará en un solo hilo. Debe mantener un bloqueo en el Vector durante toda la iteración (y tenga cuidado de no estancarse).

+0

Sí, sé por qué se lanza la excepción, pero no debería. La iteración es posterior a la parte del código que publiqué y, si el código funciona bien, no debería obtener una excepción ... – Raffo

1

El ConcurrentModificationException proviene de su llamada a tasks.clear() mientras que sus Exceutors está interactuando sobre su tasksVector. Lo que puede hacer es llamar al shutdownNow() en su ExecutorService

+0

Esto no parece funcionar ... – Raffo

0

fut.get() es una llamada de bloqueo, incluso después de que el tiempo de espera, se le bloqueará hasta que se realiza la tarea. Si desea detenerse lo más cerca posible de la marca de 5 minutos, debe verificar el indicador de interrupción, solo lo recomiendo utilizando el método Thread.isInterrupted() que preserva el estado de interrupción. Si desea detenerse inmediatamente y no necesita limpiar ningún estado, ejecute una excepción que será capturada 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 la colección "tasks" 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 hace con la Lista cuando usted llama a clear(). Pero, si necesita comenzar a formar una lista de tareas nuevas para ejecutar, le sugiero que forme una nueva Lista de tareas, no utilice una Lista anterior 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 de pila completo, en lugar del error de una línea.

+0

Utilicé la colección en otro lugar, y está en un ciclo while, por lo que debe borrarse para que esté vacía cuando se repita el ciclo. Por supuesto, puedo hacer clear() después del código que se muestra en la publicación, y esto debería estar bien. La parte importante de mi pregunta no es la excepción: lo que necesito saber es cómo detener el futuro después de 5 minutos y, por supuesto, intentaré hacer una excepción como sugirió. Incluso puedo cambiar la forma en que presento mis tareas. Aprendí de esa manera aquí: http://stackoverflow.com/questions/1322147/help-with-java-executors-wait-for-task-termination – Raffo

-1

Coloque el fut.cancel(true); en el bloque finally

+0

¿Quiere decir en un bloque 'finalmente' agregarse, ¿verdad? –

Cuestiones relacionadas