Razón para llamar a shutdown () en ExecutorService


84

Estuve leyendo bastante sobre esto en las últimas horas, y simplemente no veo ninguna razón (razón válida ) para llamar shutdown()a ExecutorService, a menos que tengamos una aplicación enorme que almacene, docenas y docenas de diferentes servicios ejecutores que no se utilizan para mucho tiempo.

Lo único que hace (por lo que he recopilado) que hace el apagado es hacer lo que hace un hilo normal una vez que está hecho. Cuando el hilo normal termine el método de ejecución de Runnable (o invocable), se pasará a Garbage Collection para su recolección. Con Executor Service, los subprocesos simplemente se pondrán en espera, no se marcarán para la recolección de basura. Para eso se necesita el apagado.

Ok, volvamos a mi pregunta. ¿Hay alguna razón para llamar al apagado con ExecutorServicemucha frecuencia, o incluso justo después de enviarle algunas tareas? Me gustaría dejar atrás el caso de que alguien lo está haciendo y, inmediatamente después, llama a awaitTermination()cuando se valida. Una vez que hacemos eso, tenemos que volver a crear uno nuevo ExecutorService, para hacer lo mismo. ¿No es la idea general ExecutorServicereutilizar los hilos? Entonces, ¿por qué destruir el ExecutorServicetan pronto?

¿No es una forma racional de simplemente crear ExecutorService(o acoplar dependiendo de cuántos necesite), luego, durante la ejecución de la aplicación, pasarles las tareas una vez que aparezcan, y luego, en la salida de la aplicación o en algunas otras etapas importantes, apague esos ejecutores? ?

Me gustaría recibir la respuesta de algunos codificadores experimentados que escriben mucho código asincrónico usando ExecutorServices.

Segunda pregunta lateral, acuerdos un poco más pequeños con la plataforma Android. SI algunos de ustedes dirán que no es la mejor idea apagar los ejecutores cada vez, y programan en Android, ¿podrían decirme cómo manejan esos cierres (para ser específicos, cuando los ejecutan) cuando nos ocupamos de diferentes eventos de ciclo de vida de la aplicación.

Debido al comentario de CommonsWare, hice la publicación neutral. Realmente no estoy interesado en discutir sobre eso a muerte y parece que está conduciendo allí. Solo me interesa conocer lo que pregunté aquí a desarrolladores experimentados, si están dispuestos a compartir sus experiencias. Gracias.


3
"Veo muchas veces códigos de muestra, en los que todo el tiempo, hay una invocación de shutdown () justo después de enviar o ejecutar tareas". Siéntase libre de usar hipervínculos para proporcionar evidencia de sus afirmaciones. Personalmente, nunca he visto ningún "código de muestra" que haga lo que usted dice. Es posible que esté malinterpretando algo, y solo podemos señalarlo si sabemos qué "códigos de muestra" está examinando.
CommonsWare

4
Hola CommonsWare. En primer lugar, veo un tono agresivo tuyo (o eso parece) hacia mí, que creo que no se valida aquí. No estaba tratando de retratar a las personas de manera negativa. En cuanto a su cita, me refería principalmente a la edición Thinking In Java IV, parte multitarea. Puede encontrar muchos ejemplos de eso en los ejemplos de Bruce Eckel. En su mayoría son simples, pero sin embargo, la impresión que Bruce me dio fue de usar el apagado con mucha frecuencia. De cualquier manera, te enfocaste en algo que no era la parte principal de mi publicación. Quité esas partes porque realmente no deseo discutir sobre eso.
Lucas

1
hay @CommonsWare in Thinking in java book por Bruce Eckel..in concurrencia / Executor página 804 Cuarta edición, siempre usa el método shutdown () justo después de enviar o ejecutar tareas en aplicaciones simples para ilustrar cómo funciona Executor como dijo Lucas
Error

2
Sé que esta es una publicación antigua, pero creo que la pregunta del OP sigue en pie y es válida. También me he encontrado con muchos códigos de muestra donde "hay una invocación de shutdown () justo después de ejecutar ()". tutorials.jenkov.com/java-util-concurrent/executorservice.html (primer tutorial que aparece cuando buscas en Google "ejemplo de Java
Executorservice

Gracias, tenía la misma pregunta que surgió con estos "códigos de muestra". journaldev.com/2340/…
Gregordy

Respuestas:


57

El shutdown()método hace una cosa: evita que los clientes envíen más trabajo al servicio ejecutor. Esto significa que todas las tareas existentes aún se ejecutarán hasta su finalización a menos que se tomen otras acciones. Esto es cierto incluso para las tareas programadas, por ejemplo, para un ScheduledExecutorService: las nuevas instancias de la tarea programada no se ejecutarán. Esto puede resultar útil en varios escenarios.

Supongamos que tiene una aplicación de consola que tiene un servicio ejecutor que ejecuta N tareas. Si el usuario presiona CTRL-C, espera que la aplicación finalice, posiblemente sin problemas. ¿Qué significa con gracia? Tal vez desee que su aplicación no pueda enviar más tareas al servicio ejecutor y, al mismo tiempo, desee esperar a que se completen sus N tareas existentes. Puede lograr esto usando un gancho de apagado como último recurso:

final ExecutorService service = ... // get it somewhere

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Performing some shutdown cleanup...");
        service.shutdown();
        while (true) {
            try {
                System.out.println("Waiting for the service to terminate...");
                if (service.awaitTermination(5, TimeUnit.SECONDS)) {
                    break;
                }
            } catch (InterruptedException e) {
            }
        }
        System.out.println("Done cleaning");
    }
}));

Este enlace cerrará el servicio, lo que evitará que su aplicación envíe nuevas tareas y esperará a que se completen todas las tareas existentes antes de cerrar la JVM. La terminación en espera se bloqueará durante 5 segundos y volverá a ser verdadera si se cierra el servicio. Esto se hace en un bucle para que esté seguro de que el servicio se cerrará eventualmente. La InterruptedException se traga cada vez. Esta es la mejor manera de cerrar un servicio ejecutor que se reutiliza en toda su aplicación.

Este código no es perfecto. A menos que esté absolutamente seguro de que sus tareas terminarán eventualmente, es posible que desee esperar un tiempo de espera determinado y luego simplemente salir, abandonando los subprocesos en ejecución. En este caso, tendría sentido llamar también shutdownNow()después del tiempo de espera en un intento final de interrumpir los subprocesos en ejecución ( shutdownNow()también le dará una lista de tareas esperando para ejecutarse). Si sus tareas están diseñadas para responder a una interrupción, esto funcionará bien.

Otro escenario interesante es cuando tienes un ScheduledExecutorService que realiza una tarea periódica. La única forma de detener la cadena de tareas periódicas es llamar shutdown().

EDITAR: Me gustaría agregar que no recomendaría usar un gancho de apagado como se muestra arriba en el caso general: puede ser propenso a errores y debería ser solo un último recurso. Además, si tiene muchos enlaces de cierre registrados, el orden en el que se ejecutarán no está definido, lo que podría no ser deseable. Yo prefiero tener la aplicación explícita llamar shutdown()a InterruptedException.


Lo siento Giovanni por la respuesta tardía, y gracias por eso por cierto. Sí, soy consciente de cómo funciona el Ejecutor, que intenté explicar en mi pregunta. El cierre hace lo que dijo y también permite que el recolector de basura recopile esos hilos muertos y, de hecho, recopile la instancia de ExecutorService. Mi pregunta fue específica. ¿Hay alguna razón para llamar a "shutdown ()" todo el tiempo, justo después de enviar / ejecutar algo en ExecutorService? La segunda parte de la pregunta está estrictamente relacionada con la arquitectura de Android. Si la respuesta a la anterior es no, entonces cuándo llamar al apagado durante y. ciclo vital.
Lucas

3
No hay razón para llamar a shutdown () todo el tiempo. De hecho, eso podría ser absolutamente incorrecto, ya que evitará que vuelva a utilizar el servicio ejecutor nuevamente. La razón para llamarlo al final del ciclo de vida del servicio es para que los subprocesos puedan finalmente ser recolectados como basura como notó. Si no lo hace, esos subprocesos mantendrán viva la JVM aunque estén inactivas.
Giovanni Botta

"No hay razón para llamar a shutdown () todo el tiempo. De hecho, eso podría ser absolutamente incorrecto, ya que evitará que vuelva a utilizar el servicio ejecutor". Ese es exactamente mi razonamiento y mi dillema de la pregunta original. Entonces, para reiterar, la pregunta es: ¿Cuándo debo apagar mi ExecutiveService en el ciclo de vida de Android?
Lucas

2
No tengo experiencia con Android, pero supongo que debería cerrarlo cuando se cierre la aplicación, para permitir que la JVM finalmente se cierre.
Giovanni Botta

3
Veo. Le sugiero que use un grupo de subprocesos en caché y nunca lo llame shutdown()para no desperdiciar recursos cuando sea innecesario. Si la aplicación se cierra, los subprocesos en el grupo eventualmente serán recolectados como basura (después de que estén inactivos durante 60 segundos de manera predeterminada). Tenga en cuenta que si desea que el grupo esté delimitado o desea una vida útil diferente del hilo, puede crear ThreadPoolExecutordirectamente un archivo.
Giovanni Botta

13

¿No es toda la idea que ExecutorService reutilice los hilos? Entonces, ¿por qué destruir ExecutorService tan pronto?

Si. No debe destruir y volver a crear con ExecutorServicefrecuencia. Inicialice ExecutorServicecuando lo necesite (principalmente al inicio) y manténgalo activo hasta que haya terminado.

¿No es una forma racional de simplemente crear ExecutorService (o un par según la cantidad que necesite), luego, durante la ejecución de la aplicación, pasarles las tareas una vez que aparezcan, y luego, en la salida de la aplicación o en algunas otras etapas importantes, apagarlas? ejecutores?

Si. Es racional cerrar ExecutorServiceen etapas importantes como la salida de la aplicación, etc.

Segunda pregunta lateral, acuerdos un poco más pequeños con la plataforma Android. SI algunos de ustedes dirán que no es la mejor idea apagar los ejecutores cada vez, y programan en Android, ¿podrían decirme cómo manejan esos apagados (para ser específicos, cuando los ejecutan) cuando tratamos con diferentes eventos de aplicación? ciclo vital.

Suponga que ExecutorServicese comparte entre diferentes actividades en su aplicación. Cada actividad se pausará / reanudará en diferentes intervalos de tiempo y aún necesitará una ExecutorServicepara su aplicación.

En lugar de administrar el estado de los ExecutorServicemétodos del ciclo de vida de la actividad, mueva la administración de ExecutorService (Creación / Apagado) a su Servicio personalizado .

Crear ExecutorServiceen servicio => onCreate()y apagarlo correctamente enonDestroy()

Forma recomendada de apagar ExecutorService:

Cómo apagar correctamente Java ExecutorService


3

Un ExecutorService debe cerrarse una vez que ya no sea necesario para liberar recursos del sistema y permitir el cierre correcto de la aplicación. Debido a que los subprocesos en un ExecutorService pueden ser subprocesos que no son demonio, pueden impedir la terminación normal de la aplicación. En otras palabras, su aplicación permanece ejecutándose después de completar su método principal.

Libro de referencia

Chaper: 14 Página: 814


0

Razón para llamar a shutdown () en ExecutorService

Hoy me encontré con una situación en la que tengo que esperar hasta que una máquina esté lista, antes de comenzar una serie de tareas en esa máquina.

Hago una llamada REST a esta máquina, si no recibo 503 (Servidor no disponible), la máquina está lista para procesar mis solicitudes. Entonces, espero hasta obtener 200 (éxito) para la primera llamada de REST.

Hay varias formas de lograrlo, utilicé ExecutorService para crear un hilo y lo programé para que se ejecute después de cada X segundos. Entonces, necesito detener este hilo con una condición, mira esto ...

final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    Runnable task = () -> {
        try {
            int statusCode = restHelper.firstRESTCall();

            if (statusCode == 200) {
                executor.shutdown();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    };

    int retryAfter = 60;
    executor.scheduleAtFixedRate(task, 0, retryAfter, TimeUnit.SECONDS);

Segunda pregunta lateral, acuerdos un poco más pequeños con la plataforma Android.

¡Quizás pueda responder si me proporciona un poco más de contexto! Además, según mi experiencia con el desarrollo de Android, rara vez necesitas Threads. ¿Estás desarrollando un juego o una aplicación que necesita hilos para funcionar? Si no es así, en Android tienes otras formas de abordar problemas como el escenario que expliqué anteriormente. Puede utilizar TimerTask, AsyncTask o Handlers o Loaders según el contexto. Esto se debe a que si UIThread espera mucho tiempo, sabes lo que sucede: /


0

Esto es genuino a pesar de las empresas planificadas, por ejemplo, para un ScheduledExecutorService: los nuevos casos de la asignación reservada no se ejecutarán.

Deberíamos esperar que tenga una aplicación cómoda que tenga una administración de agentes que ejecute N recados.

¿No estoy captando su significado sin esfuerzo? Tal vez necesite que su solicitud no tenga la opción de enviar más asignaciones a la administración del agente y, mientras tanto, debe esperar a que finalicen sus N compromisos actuales.

Excepto si estás totalmente seguro de que tus recados terminarán al final, deberías estar sentado durante un descanso determinado y luego simplemente salir, abandonando los hilos de ejecución.

En el caso de que sus actividades estén destinadas a reaccionar a interferencias, esto funcionará bien.

Otra situación intrigante es el punto en el que tiene un ScheduledExecutorService que realiza una actividad.

La mejor forma de detener la cadena de actividad es llamar a shutdown ()

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.