Convertir un ExecutorService a daemon en Java


Estoy usando un ExecutoreService en Java 1.6, iniciado simplemente por

ExecutorService pool = Executors.newFixedThreadPool(THREADS). 

Cuando mi subproceso principal esté terminado (junto con todas las tareas procesadas por el grupo de subprocesos), este grupo evitará que mi programa se apague hasta que llame explícitamente a

pool.shutdown();

¿Puedo evitar tener que llamar a esto convirtiendo de alguna manera la gestión de subprocesos interna utilizada por este grupo en un subproceso deamon? O me estoy perdiendo algo aquí.

Author: isapir, 2012-12-14

7 answers

Probablemente la solución más simple y preferida está en La respuesta de Marco13 así que no se deje engañar por la diferencia de votos (la respuesta mía es unos años mayor) o la marca de aceptación (solo significa que la solución mía era apropiada para las circunstancias de OP, no que sea la mejor).


Puede usar ThreadFactory para establecer subprocesos dentro de Executor a demonios. Esto afectará al servicio ejecutor de una manera que también se convertirá en daemon thread, por lo que (y los hilos manejados por él) se detendrán si no hay otro hilo no daemon. Este es un ejemplo simple:

ExecutorService exec = Executors.newFixedThreadPool(4,
        new ThreadFactory() {
            public Thread newThread(Runnable r) {
                Thread t = Executors.defaultThreadFactory().newThread(r);
                t.setDaemon(true);
                return t;
            }
        });

exec.execute(YourTaskNowWillBeDaemon);

Pero si desea obtener ejecutor que permitirá que su tarea termine, y al mismo tiempo llamará automáticamente a su método shutdown() cuando la aplicación se complete, es posible que desee envolver su ejecutor con Guayaba MoreExecutors.getExitingExecutorService.

ExecutorService exec = MoreExecutors.getExitingExecutorService(
        (ThreadPoolExecutor) Executors.newFixedThreadPool(4), 
        100_000, TimeUnit.DAYS//period after which executor will be automatically closed
                             //I assume that 100_000 days is enough to simulate infinity
);
//exec.execute(YourTask);
exec.execute(() -> {
    for (int i = 0; i < 3; i++) {
        System.out.println("daemon");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});
 77
Author: Pshemo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:17

Ya hay una funcionalidad incorporada para crear un ExecutorService que termina todos los subprocesos después de un cierto período de inactividad: Puede crear un ThreadPoolExecutor, pasarle la información de tiempo deseada y luego llamar a allowCoreThreadTimeout(true) en este servicio ejecutor:

/**
 * Creates an executor service with a fixed pool size, that will time 
 * out after a certain period of inactivity.
 * 
 * @param poolSize The core- and maximum pool size
 * @param keepAliveTime The keep alive time
 * @param timeUnit The time unit
 * @return The executor service
 */
public static ExecutorService createFixedTimeoutExecutorService(
    int poolSize, long keepAliveTime, TimeUnit timeUnit)
{
    ThreadPoolExecutor e = 
        new ThreadPoolExecutor(poolSize, poolSize,
            keepAliveTime, timeUnit, new LinkedBlockingQueue<Runnable>());
    e.allowCoreThreadTimeOut(true);
    return e;
}

EDITAR Haciendo referencia a las observaciones en los comentarios: Tenga en cuenta que este ejecutor del grupo de subprocesos no se apagará automáticamente cuando se cierre la aplicación. El ejecutor continuará ejecutándose después de que la aplicación salga, pero no más de el keepAliveTime. Si, dependiendo de los requisitos precisos de la aplicación, el keepAliveTime tiene que ser más largo que unos pocos segundos, la solución en la respuesta de Pshemo puede ser más apropiada: Cuando los subprocesos se establecen como subprocesos daemon, entonces terminarán inmediatamente cuando la aplicación salga.

 32
Author: Marco13,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:03:05

Usaría la clase ThreadFactoryBuilder de Guava.

ExecutorService threadPool = Executors.newFixedThreadPool(THREADS, new ThreadFactoryBuilder().setDaemon(true).build());

Si aún no estás usando Guayaba, iría con una subclase ThreadFactory como se describe en la parte superior de La respuesta de Pshemo

 12
Author: Phil Hayward,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:17

Sí.

Simplemente necesita crear su propia clase ThreadFactory que cree subprocesos daemon en lugar de subprocesos regulares.

 5
Author: SLaks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-14 17:14:42

Esta solución es similar a la de @Marco13, pero en lugar de crear nuestra propia ThreadPoolExecutor, podemos modificar la que devuelve Executors#newFixedThreadPool(int nThreads). He aquí cómo:

ExecutorService ex = Executors.newFixedThreadPool(nThreads);
 if(ex instanceof ThreadPoolExecutor){
    ThreadPoolExecutor tp = (ThreadPoolExecutor) ex;
    tp.setKeepAliveTime(time, timeUnit);
    tp.allowCoreThreadTimeOut(true);
}
 1
Author: Steve,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-09 18:17:05

Normalmente quiero que mis subprocesos se ejecuten como subprocesos demonio, así que agregué una clase de utilidad de la siguiente manera:

import java.util.concurrent.ThreadFactory;

public class DaemonThreadFactory implements ThreadFactory {

    public final static ThreadFactory instance = 
                    new DaemonThreadFactory();

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }
}

Que me permite pasar fácilmente DaemonThreadFactory.instance al ExecutorService, por ejemplo,

ExecutorService exec = Executors.newFixedThreadPool(
    4, DaemonThreadFactory.instance
);

O úselo para iniciar fácilmente un hilo daemon desde un Runnable, por ejemplo,

DaemonThreadFactory.instance.newThread(
    () -> { doSomething(); }
).start();
 0
Author: isapir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-19 22:44:59

Si tiene una lista conocida de tareas, no necesita hilos de demonio en absoluto. Simplemente puede llamar a shutdown () en el ExecutorService después de enviar todas sus tareas.

Cuando se complete el subproceso principal, utilice el método awaitTermination() para dar tiempo a que se completen las tareas enviadas.Las tareas enviadas actualmente se ejecutarán, y el grupo de subprocesos terminará su subproceso de control una vez que se hayan completado.

for (Runnable task : tasks) {
  threadPool.submit(task);
}
threadPool.shutdown();
/*... do other stuff ...*/
//All done, ready to exit
while (!threadPool.isTerminated()) {
  //this can throw InterruptedException, you'll need to decide how to deal with that.
  threadPool.awaitTermination(1,TimeUnit.SECOND); 
}
 -1
Author: Phil Hayward,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-08 17:36:05