98 votes

Transformer un ExecutorService en démon en Java

J'utilise un ExecutoreService en Java 1.6, démarré simplement par

ExecutorService pool = Executors.newFixedThreadPool(THREADS). 

Lorsque mon thread principal est terminé (ainsi que toutes les tâches traitées par le pool de threads), ce pool empêchera mon programme de s'arrêter jusqu'à ce que j'appelle explicitement

pool.shutdown();

Puis-je éviter d'avoir à appeler cette fonction en transformant d'une manière ou d'une autre le thread interne de gestion utilisé par ce pool en un thread deamon ? Ou est-ce que quelque chose m'échappe ici ?

128voto

Pshemo Points 34648

La solution la plus simple et la plus appréciée est sans doute la suivante La réponse de Marco13 Ne vous laissez donc pas tromper par la différence de vote (cette réponse est plus ancienne de quelques années) ou la marque d'acceptation (cela signifie simplement que cette solution était appropriée pour les circonstances de l'OP, pas qu'elle est la meilleure en général).


Vous pouvez utiliser ThreadFactory pour définir les threads de l'Executor comme des démons. Cela affectera le service de l'exécuteur d'une manière telle qu'il deviendra également un thread démon, de sorte qu'il (et les threads gérés par lui) s'arrêtera s'il n'y a pas d'autre thread non démon. Voici un exemple 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);

Mais si vous voulez obtenir un exécuteur qui laisse sa tâche se terminer, et en même temps appelle automatiquement son shutdown() lorsque l'application est terminée, vous pouvez envelopper votre exécuteur avec la méthode Goyave 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();
        }
    }
});

61voto

Marco13 Points 14743

Il existe déjà une fonctionnalité intégrée permettant de créer un ExecutorService qui met fin à tous les threads après une certaine période d'inactivité : Vous pouvez créer un ThreadPoolExecutor en lui transmettant les informations de synchronisation souhaitées, puis en appelant allowCoreThreadTimeout(true) sur ce service d'exécution :

/**
 * 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;
}

EDIT Se référant aux remarques dans les commentaires : Notez que cet exécuteur de pool de threads ne s'arrête pas automatiquement lorsque l'application se termine. L'exécuteur continuera à fonctionner après la sortie de l'application, mais pas plus longtemps que la durée de vie de l'application. keepAliveTime . Si, en fonction des exigences précises de l'application, les keepAliveTime doit être plus longue que quelques secondes, la solution dans la réponse de Pshemo peut être plus approprié : Lorsque les threads sont définis comme des threads de démon, ils se terminent immédiatement lorsque l'application se termine.

30voto

Phil Hayward Points 413

J'utiliserais la classe ThreadFactoryBuilder de Guava.

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

Si vous n'utilisez pas déjà Guava, j'opterais pour une sous-classe de ThreadFactory comme celle décrite en haut de la page d'accueil. La réponse de Pshemo

11voto

Igal Points 836

Si vous ne voulez l'utiliser qu'à un seul endroit, vous pouvez mettre en ligne l'option java.util.concurrent.ThreadFactory Par exemple, pour un pool avec 4 threads, vous écrirez (exemple montré comme un lambda supposant Java 1.8 ou plus récent) :

ExecutorService pool = Executors.newFixedThreadPool(4,
        (Runnable r) -> {
            Thread t = new Thread(r);
            t.setDaemon(true);
            return t;
        }
);

Mais je veux généralement que toutes mes fabriques de threads produisent des threads démons, alors j'ajoute une classe utilitaire comme suit :

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;
    }
}

Cela me permet de passer facilement DaemonThreadFactory.instance à la ExecutorService par exemple

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

ou l'utiliser pour lancer facilement une démon Fil conducteur d'un Runnable par exemple

DaemonThreadFactory.instance.newThread(
    () -> { doSomething(); }
).start();

4voto

SLaks Points 391154

Sí.

Vous devez simplement créer votre propre ThreadFactory qui crée des threads démons plutôt que des threads normaux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X