web-dev-qa-db-fra.com

Arrêt gracieux des threads et de l'exécuteur

Le code suivant tente d’accompagner ceci.

Le code boucle en boucle et vérifie s'il y a des demandes en attente à traiter. S'il y en a, il crée un nouveau thread pour traiter la demande et la soumet à l'exécuteur. Une fois que tous les threads sont terminés, il dort pendant 60 secondes et vérifie à nouveau les demandes en attente.

public static void main(String a[]){
    //variables init code omitted
    ExecutorService service = Executors.newFixedThreadPool(15);
    ExecutorCompletionService<Long> comp = new ExecutorCompletionService<Long>(service);
    while(true){
        List<AppRequest> pending = service.findPendingRequests();
        int noPending = pending.size();
        if (noPending > 0) {
            for (AppRequest req : pending) {
                Callable<Long> worker = new RequestThread(something, req);
                comp.submit(worker);
            }
        }
        for (int i = 0; i < noPending; i++) {
            try {
                Future<Long> f = comp.take();
                long name;
                try {
                    name = f.get();
                    LOGGER.debug(name + " got completed");
                } catch (ExecutionException e) {
                    LOGGER.error(e.toString());
                }
            } catch (InterruptedException e) {
                LOGGER.error(e.toString());
            }
        }
        TimeUnit.SECONDS.sleep(60);
    }

  }

Ma question est la plupart des traitements effectués par ces threads traitent de base de données. Et ce programme sera exécuté sur une machine Windows. Qu'advient-il de ces threads lorsque quelqu'un tente d'arrêter ou de déconnecter la machine? Comment arrêter gracieusement les threads en cours d'exécution et également l'exécuteur.?

33
user378101

Un arrêt ordonné typique d'un ExecutorService pourrait ressembler à ceci:

final ExecutorService executor;

Runtime.getRuntime().addShutdownHook(new Thread() {
    public void run() {
        executor.shutdown();
        if (!executor.awaitTermination(SHUTDOWN_TIME)) { //optional *
            Logger.log("Executor did not terminate in the specified time."); //optional *
            List<Runnable> droppedTasks = executor.shutdownNow(); //optional **
            Logger.log("Executor was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed."); //optional **
        }
    }
});

* Vous pouvez vous connecter que l'exécuteur avait encore des tâches à traiter après avoir attendu le temps que vous étiez prêt à attendre.
** Vous pouvez essayer de forcer les threads de travail de l'exécuteur à abandonner leurs tâches actuelles et à vous assurer qu'ils ne démarrent pas les tâches restantes.

Notez que la solution ci-dessus fonctionnera lorsqu'un utilisateur envoie une interruption à votre processus Java ou lorsque votre ExecutorService ne contient que des threads de démon. Si, à la place, la variable ExecutorService contient des threads non démon qui ne se sont pas terminés, la machine virtuelle Java ne tentera pas de s'arrêter et, par conséquent, les points d'ancrage ne seront pas appelés.

Si vous essayez d'arrêter un processus dans le cadre du cycle de vie d'une application discrète (pas un service), le code d'arrêt ne doit pas être placé dans un crochet d'arrêt mais à l'emplacement approprié où le programme est conçu pour se terminer.

58
Tim Bender

Depuis que l'ajout d'un point d'arrêt pour appeler explicitement shutdown() n'a pas fonctionné pour moi, j'ai trouvé une solution simple dans la guava de Google: com.google.common.util.concurrent.MoreExecutors.getExitingExecutorService .

5
Georgi Peev

Vous pouvez appeler shutdown() sur la ExecutorService:

Lance un arrêt méthodique dans lequel Les tâches précédemment soumises sont exécuté, mais aucune nouvelle tâche ne sera accepté.

ou vous pouvez appeler shutdownNow():

Essaie de tout arrêter activement l'exécution des tâches, arrête le traitement des tâches en attente et renvoie une liste des tâches en attente exécution.

Il n'y a aucune garantie au-delà de meilleur effort tente d'arrêter traitement des tâches en cours d'exécution . Par exemple, des implémentations typiques annulera via Thread.interrupt (), donc toute tâche qui ne répond pas à les interruptions peuvent ne jamais se terminer.

Lequel vous appelez dépend de votre volonté d'arrêter ....

2
skaffman

J'ai eu le même problème, j'ai l'habitude d'obtenir l'erreur comme

o.a.c.loader.WebappClassLoaderBase :: L'application Web [ROOT] semble avoir démarré un thread nommé [pool-2-thread-1] mais ne l'a pas arrêté. Il y a beaucoup de chances que cela génère des fuites mémoires. Trace de fil de pile: Sun.misc.Unsafe.park (Méthode native) Java.util.concurrent.locks.LockSupport.park (LockSupport.Java:175) Java.util.concurrent.locks.AbstractQueuedSynchronizer $ ConditionObject.await (AbstractQueuedSynchronizer.Java:2039)

Le code ci-dessous a corrigé

private ThreadPoolExecutor executorPool;

@PostConstruct
public void init() {
    log.debug("Initializing ThreadPoolExecutor");
    executorPool = new ThreadPoolExecutor(1, 3, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1));
}

@PreDestroy
public void destroy() {
    log.debug("Shuting down ThreadPoolExecutor");
    executorPool.shutdown();
}
0
Chinmay Samant