web-dev-qa-db-fra.com

Transformer un ExecutorService en démon en Java

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

ExecutorService pool = Executors.newFixedThreadPool(THREADS). 

Lorsque mon thread principal est terminé (avec toutes les tâches traitées par le pool de threads), ce pool empêchera mon programme de s’arrêter tant que je n’ai pas appelé explicitement

pool.shutdown();

Puis-je éviter d'avoir à appeler cela en transformant en quelque sorte le processus de gestion de threads interne utilisé par ce pool en un thread de démon? Ou suis-je manque quelque chose ici.

58
Antiz

La solution probablement la plus simple et la mieux adaptée est en réponse de Marco13 alors ne vous laissez pas berner par la différence de vote (la réponse est vieille de quelques années) ou par le repère d'acceptation (cela signifie simplement que la solution de la mine était appropriée pour les circonstances du PO, mais pas est le meilleur).


Vous pouvez utiliser ThreadFactory pour définir les threads dans Executor en démons. Cela affectera le service exécuteur de sorte qu'il deviendra également un thread démon, ainsi il (et les threads gérés par celui-ci) s'arrêtera s'il n'y aura 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 laissera sa tâche se terminer et en même temps appellera automatiquement sa méthode shutdown() lorsque l'application sera terminée, vous voudrez peut-être envelopper votre exécuteur avec GuavaMoreExecutors.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();
        }
    }
});
85
Pshemo

Il existe déjà une fonctionnalité intégrée pour créer une ExecutorService qui termine tous les threads après une certaine période d'inactivité: vous pouvez créer une ThreadPoolExecutor, lui transmettre les informations de minutage souhaitées, puis appeler allowCoreThreadTimeout(true) sur ce service de l'exécuteur

/**
 * 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 En faisant référence aux remarques dans les commentaires: Notez que cet exécuteur de pool de threads ne sera pas automatiquement arrêté lorsque l'application se ferme. L'exécuteur continuera à s'exécuter après la fermeture de l'application, mais pas plus longtemps que keepAliveTime. Si, en fonction des exigences précises de l'application, la keepAliveTime doit être supérieure à quelques secondes, la solution contenue dans la réponse de Pshemo peut être plus appropriée: lorsque les threads sont configurés pour être des démons, ils se terminent immédiatement lorsque l'application se ferme.

37
Marco13

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'utiliserais une sous-classe ThreadFactory comme décrit en haut de Réponse de Pshemo

16
Phil Hayward

Oui.

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

4
SLaks

Si vous ne souhaitez l'utiliser que dans un seul endroit, vous pouvez alors intégrer l'implémentation Java.util.concurrent.ThreadFactory (par exemple, lambda en supposant que Java 1.8 ou une version plus récente), par exemple. pour une piscine avec 4 fils, vous écririez:

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 usines de threads produisent des threads de démon, alors j'ajoute une classe d'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 utilisez-le pour démarrer facilement un daemon Thread à partir d'une Runnable, par ex.

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

Vous pouvez utiliser ThreadFactoryBuilder de Guava. Je ne voulais pas ajouter la dépendance et je voulais la fonctionnalité de Executors.DefaultThreadFactory, alors j'ai utilisé la composition:

class DaemonThreadFactory implements ThreadFactory {
    final ThreadFactory delegate;

    DaemonThreadFactory() {
        this(Executors.defaultThreadFactory());
    }

    DaemonThreadFactory(ThreadFactory delegate) {
        this.delegate = delegate;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread thread = delegate.newThread(r);
        thread.setDaemon(true);
        return thread;
    }
}
0
etherous

Cette solution est similaire à celle de @ Marco13 mais au lieu de créer notre propre ThreadPoolExecutor, nous pouvons modifier celle renvoyée par Executors#newFixedThreadPool(int nThreads). Voici comment:

ExecutorService ex = Executors.newFixedThreadPool(nThreads);
 if(ex instanceof ThreadPoolExecutor){
    ThreadPoolExecutor tp = (ThreadPoolExecutor) ex;
    tp.setKeepAliveTime(time, timeUnit);
    tp.allowCoreThreadTimeOut(true);
}
0
Steve