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 ?

0voto

Steve Points 347

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

0voto

etherous Points 679

Vous pouvez utiliser le ThreadFactoryBuilder de Guava. Je ne voulais pas ajouter cette dépendance et je voulais la fonctionnalité de Executors.DefaultThreadFactory, donc 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;
    }
}

0voto

Dave The Dane Points 18

Ce n'est pas exactement répondre à la question qui nous occupe, mais cela peut s'avérer utile :

Comme d'autres l'ont fait remarquer, vous pouvez créer une DaemonThreadFactory, soit en ligne, soit en tant que classe utilitaire.

En sous-classant Runnable, vous pouvez obtenir le DaemonThread résultant de la Factory ci-dessus pour exécuter l'unité runnable entière dans un Thread non-daemon spawné séparément. Dans des circonstances normales, ce Thread non-daemon se terminera même si le Daemon Thread utilisé dans l'Executor est destiné à être fermé.

Voici un petit exemple :

import static java.util.concurrent.TimeUnit.*;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.*;

public class ScheduleStackOverflow {

    private static final DateTimeFormatter DTF_HH_MM_SS = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");

    private static final class ThreadRunnable implements Runnable {

        private final Runnable runnable;

        public ThreadRunnable(final Runnable runnable) {
            this.runnable = runnable;
        }
        @Override
        public void run() {
            final Thread delegateThread = new Thread(this.runnable);
            /**/         delegateThread.setDaemon(false); // TODO Try changing this to "true"
            /**/         delegateThread.start();
        }
    }

    public static void main(final String[] args) throws InterruptedException {

        final     ThreadFactory daemonThreadFactory = (daemonRunnable) -> {
            final Thread        daemon = new Thread   (daemonRunnable);
            /**/                daemon.setDaemon(true);
            return              daemon;
        };

        final Runnable runnable = new ThreadRunnable(() -> {
            System.out.println(DTF_HH_MM_SS.format(LocalTime.now()) + " daemon=" + Thread.currentThread().isDaemon() + " Scheduling...");

            sleep(5, SECONDS);

            System.out.println(DTF_HH_MM_SS.format(LocalTime.now()) + " daemon=" + Thread.currentThread().isDaemon() + " Schedule done.");
        });

        Executors
        .newSingleThreadScheduledExecutor(daemonThreadFactory)
        .scheduleAtFixedRate(runnable, 0, Duration.ofSeconds(10).toNanos(), NANOSECONDS);

        sleep(12, SECONDS);

        System.out.println(DTF_HH_MM_SS.format(LocalTime.now()) + " Main CLOSED!");
    }

    private static void sleep(final long timeout, final TimeUnit timeunit) {
        try {timeunit.sleep(timeout);} catch (InterruptedException e) {}
    }
}

-1voto

Phil Hayward Points 413

Si vous avez une liste connue de tâches, vous n'avez pas besoin de threads de démon du tout. Vous pouvez simplement appeler shutdown() sur l'ExecutorService après avoir soumis toutes vos tâches.

Lorsque votre thread principal est terminé, utilisez la méthode awaitTermination() pour laisser le temps aux tâches soumises de se terminer. Les tâches actuellement soumises seront exécutées et le pool de threads terminera son thread de contrôle une fois qu'elles seront terminées.

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

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