¿Por qué usar ExecutorService para subprocesos de larga ejecución?

8

Quiero un objeto que genere un hilo de daemon que continuará ejecutándose durante toda la vida del proceso. Digamos, solo por el argumento, que es un hilo en un sistema embebido, y espera recibir y manejar comandos en algún puerto de diagnóstico. Pero, podría ser cualquier cosa realmente. La idea principal es que está viendo algo durante un largo período de tiempo; No está realizando una secuencia de tareas .

La sabiduría común de Java dice: nunca instanciar Thread, use un ExecutorServiceen su lugar. (Por ejemplo, vea esta respuesta ) ¿Pero cuál es el beneficio? Usar un grupo de subprocesos como un medio para crear un solo subproceso de larga ejecución parece inútil. ¿Cómo sería mejor que si escribiera esto?

class Foobar {
    public Foobar() {
        this.threadFactory = Executors.defaultThreadFactory();
        ...
    }

    public Foobar(ThreadFactory threadFactory) {
        this.threadFactory = threadFactory;
        ...
    }

    public void start() {
        fooThread = threadFactory.newThread(new Runnable() { ... });
        fooThread.setDaemon(true);
        fooThread.start();
    }
    ...
}

Nota: esta pregunta parece similar a la mía, pero las respuestas solo dicen cómo usar un grupo de subprocesos, no por qué .

Salomón lento
fuente
No estoy seguro acerca de Java, pero otros lenguajes generalmente recomiendan explícitamente evitar un grupo de subprocesos para cualquier subproceso de larga ejecución. Como dijiste, no tiene sentido usar uno de un número limitado de recursos durante un largo período de tiempo.
Frank Hileman

Respuestas:

11

Creo que "Nunca" es una palabra demasiado fuerte. Es más como la segunda regla de optimización: "No lo hagas (todavía)".

En mi opinión, la razón principal para usar un servicio ejecutor es administrar la cantidad de subprocesos en ejecución. Si permite que las clases arbitrarias creen sus propios subprocesos, puede encontrarse rápidamente con 1,000 subprocesos vinculados a la CPU (o que cambian constantemente de contexto). Un servicio ejecutor resuelve ese problema al proporcionar restricciones en la creación de subprocesos.

Una razón secundaria es que iniciar un hilo correctamente requiere algo de reflexión:

  • ¿Demonio o no demonio? Si se equivoca, debe llamar System.exit()para cerrar su programa.
  • Que prioridad Muchos programadores de Swing pensaron que estaban siendo inteligentes al girar subprocesos en segundo plano para manejar tareas intensivas en CPU, pero no se dieron cuenta de que el subproceso de envío de eventos se ejecuta con la máxima prioridad y los subprocesos secundarios heredan la prioridad de sus padres.
  • ¿Cómo lidiar con excepciones no capturadas?
  • ¿Cuál es un nombre apropiado? Parece una tontería obsesionarse con un nombre, pero los hilos con un propósito determinado le brindan mucha ayuda en la depuración.

Dicho esto, ciertamente hay casos en los que es apropiado girar hilos en lugar de confiar en un grupo de hilos.

  • Usaría el grupo de subprocesos en el caso en que mi aplicación genera tareas y quiero controlar cómo se ejecutan esas tareas.
  • Crearía hilos dedicados donde uno o unos pocos hilos están dedicados al procesamiento de eventos generados externamente .

"Generado externamente" depende del contexto. El caso clásico es el hilo al final de una cola de mensajes o socket, que necesita sondear esa cola y realizar alguna acción (que puede implicar el envío de una tarea a un grupo).

Sin embargo, también podría referirse a eventos generados fuera de un módulo en particular: por ejemplo, considero perfectamente razonable que Log4J haga AsyncAppendergirar su propio hilo en lugar de esperar que la aplicación proporcione un grupo.

kdgregory
fuente