2010-07-26 9 views
32

El siguiente fragmento de código intenta completar esto.Agraciado cierre de hilos y ejecutor

El código se repite para siempre y comprueba si hay solicitudes pendientes para procesar. Si hay alguno, crea un nuevo hilo para procesar la solicitud y lo envía al ejecutor. Una vez que todos los hilos están listos, duerme durante 60 segundos y nuevamente verifica las solicitudes pendientes.

public static void main(String a[]){ 
    //variables init code omitted 
    ExecutorService service = Executors.newFixedThreadPool(15); 
    ExecutorCompletionService<Long> comp = new ExecutorCompletionService<Long>(service); 
    while(true){ 
     List<AppRequest> pending = service.findPendingRequests(); 
     int noPending = pending.size(); 
     if (noPending > 0) { 
      for (AppRequest req : pending) { 
       Callable<Long> worker = new RequestThread(something, req); 
       comp.submit(worker); 
      } 
     } 
     for (int i = 0; i < noPending; i++) { 
      try { 
       Future<Long> f = comp.take(); 
       long name; 
       try { 
        name = f.get(); 
        LOGGER.debug(name + " got completed"); 
       } catch (ExecutionException e) { 
        LOGGER.error(e.toString()); 
       } 
      } catch (InterruptedException e) { 
       LOGGER.error(e.toString()); 
      } 
     } 
     TimeUnit.SECONDS.sleep(60); 
    } 

    } 

Mi pregunta es que la mayor parte del procesamiento realizado por estos hilos se relaciona con la base de datos. Y este programa se ejecutará en una máquina de Windows. ¿Qué sucede con estos hilos cuando alguien intenta cerrar o cerrar sesión en la máquina? Cómo cerrar con gracia los hilos en ejecución y también el ejecutor.

Respuesta

55

Un cierre ordenado típico de un ExecutorService podría ser algo como esto:

final ExecutorService executor; 

Runtime.getRuntime().addShutdownHook(new Thread() { 
    public void run() { 
     executor.shutdown(); 
     if (!executor.awaitTermination(SHUTDOWN_TIME)) { //optional * 
      Logger.log("Executor did not terminate in the specified time."); //optional * 
      List<Runnable> droppedTasks = executor.shutdownNow(); //optional ** 
      Logger.log("Executor was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed."); //optional ** 
     } 
    } 
}); 

* Usted puede iniciar sesión que el ejecutor aún tenía tareas que procesar después de esperar el tiempo que estaba dispuesto a esperar.
** Puede intentar forzar a los subprocesos de trabajo del ejecutor a abandonar sus tareas actuales y asegurarse de que no inicien ninguno de los restantes.

Tenga en cuenta que la solución anterior funcionará cuando un usuario emita una interrupción en su proceso java o cuando su ExecutorService solo contenga hilos daemon. Si, en cambio, el ExecutorService contiene hilos no daemon que no se han completado, la JVM no intentará apagarse, y por lo tanto no se invocarán los ganchos de cierre.

Si intenta cerrar un proceso como parte de un ciclo de vida de aplicación discreto (no un servicio), entonces el código de apagado no se debe colocar dentro de un gancho de cierre sino en la ubicación adecuada donde el programa está diseñado para terminar.

+0

Esto está al revés. Suponiendo que el 'ExecutorService' es uno de los valores típicos devueltos por los métodos de fábrica' Executors', los hilos de respaldo son hilos que no son daemon. El enganche de cierre no se invocará hasta que esos hilos salgan, y eso no sucederá hasta que se cierre el 'ExecutorService'. Su solución solo se producirá si los hilos del ejecutor no son daemon o si su programa recibe una interrupción de usuario. –

+0

El OP pregunta específicamente sobre el caso en el que se le ha pedido a la JVM que realice un cierre ordenado. De todos modos, el contenido de la respuesta es preciso, uno no necesita hacer esto en un cierre para terminar el servicio del ejecutor antes. –

+0

Gracias por responder tan rápido.Creo que me perdí esa parte de su pregunta. ¿Puedes agregar un tercer '***' para aclarar ese bit? –

7

El libro "Java Concurrency in Practice" afirma:

7,4. JVM Shutdown

La JVM puede cerrarse de manera ordenada o abrupta . Un cierre ordenado se inicia cuando los últimos (nondaemon) hilo "normal" termina, alguien llama System.exit, o por otros medios específicos de la plataforma (tales como el envío de un SIGINT o golpear Ctrl-C). [...]

7.4.1. Ganchos de cierre

En un apagado ordenado, la primera JVM inicia todos los ganchos de cierre registrados. Los ganchos de cierre son hilos no iniciados que están registrados con Runtime.addShutdownHook. La JVM no garantiza en el orden en que se inician los ganchos de cierre . Si alguno de los subprocesos de la aplicación (daemon o nondaemon) aún se ejecutan en tiempo de apagado, continúan ejecutándose al mismo tiempo que el proceso de cierre . Cuando todos los ganchos de cierre tienen completado, la JVM puede elegir ejecutar los finalizadores si runFinalizersOnExit es true, y luego se detiene. La JVM no intenta intentar detener o interrumpir cualquier subprocesos de la aplicación que todavía están ejecutándose en el momento del apagado; son terminados abruptamente cuando la JVM finalmente se detiene. Si los ganchos de cierre no se completan, , entonces el proceso de apagado ordenado "se cuelga" y la JVM debe cerrarse bruscamente . [...]

Los bits son importantes, "La JVM hace ningún intento de detener o interrumpir cualquier hilo de aplicaciones que todavía se están ejecutando en tiempo de apagado;. Que se terminan abruptamente cuando la JVM con el tiempo se detiene" así que supongo que la conexión a la base de datos terminará abruptamente, si no hay ganchos de cierre para realizar una limpieza ordenada (si está utilizando marcos, generalmente proporcionan tales ganchos de apagado). En mi experiencia, la sesión en la base de datos puede permanecer hasta que el DB la agote, cuando la aplicación. se termina sin tales ganchos.

2

Usted puede llamar shutdown() en el ExecutorService:

inicia un cierre ordenado en el que tareas presentadas anteriormente se ejecutado, pero no hay nuevas tareas serán aceptadas .

o puede llamar a shutdownNow():

intentos de detener todas las tareas que ejecutan activamente , detiene el procesamiento de tareas en espera , y devuelve una lista de las tareas que estaban a la espera ejecución.

No hay garantías más allá de mejor esfuerzo intenta detener el proceso activamente la ejecución de tareas. Por ejemplo, las implementaciones típicas se cancelarán a través de Thread.interrupt(), por lo que cualquier tarea que no responda a las interrupciones puede que nunca termine.

Cuál te las llamadas depende de lo mal que desea que se detenga ....

+0

Me enfrenta el siguiente problema en mi aplicación web.Cómo detener las fugas de memoria .http: //stackoverflow.com/questions/39166512/memory-leak-error-in-tomcat-server-even-after-removed-quartz -relacionado con el código – Vicky

5

Como agregar un gancho de apagado para llamar explícitamente shutdown() no funcionó para mí, encontré una solución fácil en Google's Guava: com.google.common.util.concurrent.MoreExecutors.getExitingExecutorService.

+1

Aún así, es un poco problemático ya que 'MoreExecutors.getExitingExecutorService' solo acepta instancias' ThreadPoolExecutor'. Como resultado, tiene que definir manualmente esos ejecutores en lugar de simplemente llamar, por ejemplo, 'Executors.newFixedThreadPool' que devuelve' ExecutorService'. – voo