¿Cómo cerrar un ExecutorService?

Cada vez que llamo shutdownNow() o shutdown() no se apaga. Leí algunos hilos en los que decía que el cierre no está garantizado. ¿Alguien puede proporcionarme una buena forma de hacerlo?

El patrón típico es:

 executorService.shutdownNow(); executorService.awaitTermination(); 

Al llamar a shutdownNow , el ejecutor (generalmente) intentará interrumpir los hilos que administra. Para que el cierre sea más ordenado, debe detectar la excepción interrumpida en los hilos o verificar el estado interrumpido. Si no lo haces, tus hilos se ejecutarán para siempre y tu ejecutor nunca podrá apagarse. Esto se debe a que la interrupción de subprocesos en Java es un proceso colaborativo (es decir, el código interrumpido debe hacer algo cuando se le pide que se detenga, no el código de interrupción ).

Por ejemplo, el siguiente código imprime Exiting normally... Pero si comenta la línea if (Thread.currentThread().isInterrupted()) break; , se imprimirá Still waiting... porque los hilos dentro del ejecutor aún se están ejecutando.

 public static void main(String args[]) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(1); executor.submit(new Runnable() { @Override public void run() { while (true) { if (Thread.currentThread().isInterrupted()) break; } } }); executor.shutdownNow(); if (!executor.awaitTermination(100, TimeUnit.MICROSECONDS)) { System.out.println("Still waiting..."); System.exit(0); } System.out.println("Exiting normally..."); } 

Alternativamente, podría escribirse con una InterruptedException como esta:

 public static void main(String args[]) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(1); executor.submit(new Runnable() { @Override public void run() { try { while (true) {Thread.sleep(10);} } catch (InterruptedException e) { //ok let's get out of here } } }); executor.shutdownNow(); if (!executor.awaitTermination(100, TimeUnit.MICROSECONDS)) { System.out.println("Still waiting..."); System.exit(0); } System.out.println("Exiting normally..."); } 

La mejor manera es la que tenemos en realidad en el javadoc que es:

El siguiente método cierra un ExecutorService en dos fases, primero llamando a shutdown para rechazar tareas entrantes, y luego llamando a shutdownNow , si es necesario, para cancelar cualquier tarea persistente:

 void shutdownAndAwaitTermination(ExecutorService pool) { pool.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being cancelled if (!pool.awaitTermination(60, TimeUnit.SECONDS)) System.err.println("Pool did not terminate"); } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted pool.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); } }