111 votes

Raison de l'appel de shutdown() sur ExecutorService

J'ai lu beaucoup de choses à ce sujet ces dernières heures, et je ne vois tout simplement pas de raison ( valide raison) pour appeler shutdown() sur le ExecutorService Il n'y a pas de raison de s'inquiéter, à moins d'avoir une application gigantesque qui stocke des dizaines et des dizaines de services d'exécution différents qui ne sont pas utilisés pendant une longue période.

La seule chose que fait l'arrêt (d'après ce que j'ai compris), c'est faire ce que fait un fil normal une fois qu'il est terminé. Lorsque le thread normal termine la méthode d'exécution du Runnable (ou Callable), il est transmis au Garbage Collection pour être collecté. Avec le service Executor, les threads seront simplement mis en attente, ils ne seront pas cochés pour le ramassage des ordures. Pour cela, l'arrêt est nécessaire.

Ok, revenons à ma question. Y a-t-il une raison d'appeler shutdown sur ExecutorService très souvent, ou même juste après lui avoir soumis certaines tâches ? Je voudrais laisser derrière moi le cas où quelqu'un le fait et juste après appelle à awaitTermination() comme cela est validé. Une fois que nous faisons cela, nous devons recréer une nouvelle ExecutorService encore une fois, pour faire la même chose. N'est-ce pas l'idée même de la ExecutorService pour réutiliser les fils ? Alors pourquoi détruire le ExecutorService si tôt ?

N'est-ce pas une façon rationnelle de créer simplement ExecutorService (ou deux selon le nombre dont vous avez besoin), puis pendant l'exécution de l'application, leur transmettre les tâches lorsqu'elles se présentent, et enfin à la sortie de l'application ou à d'autres étapes importantes, arrêter ces exécuteurs ?

J'aimerais avoir la réponse de quelques codeurs expérimentés qui écrivent beaucoup de code asynchrone en utilisant les ExecutorServices.

La deuxième question secondaire, un peu plus petite, concerne la plateforme Android. Si certains d'entre vous disent que ce n'est pas la meilleure idée de fermer les exécuteurs à chaque fois, et que votre programme est sur Android, pourriez-vous me dire comment vous gérez ces fermetures (pour être précis - quand vous les exécutez) lorsque nous traitons différents événements du cycle de vie de l'application.

En raison du commentaire sur CommonsWare, j'ai rendu le message neutre. Je n'ai vraiment pas envie d'en débattre à mort et il semble que ça y mène. Je suis seulement intéressé à apprendre ce que j'ai demandé ici auprès de développeurs expérimentés s'ils sont prêts à partager leurs expériences. Merci.

68voto

Giovanni Botta Points 2674

En shutdown() ne fait qu'une chose : empêcher les clients d'envoyer plus de travail au service d'exécution. Cela signifie que toutes les tâches existantes continueront à s'exécuter jusqu'à leur achèvement, sauf si d'autres actions sont entreprises. Cela est vrai même pour les tâches planifiées, par exemple pour un ScheduledExecutorService : les nouvelles instances de la tâche planifiée ne seront pas exécutées. Cela libère également toutes les ressources des threads d'arrière-plan. Cela peut être utile dans divers scénarios.

Supposons que vous avez une application console qui possède un service exécuteur exécutant N tâches. Si l'utilisateur appuie sur CTRL-C, vous vous attendez à ce que l'application se termine, éventuellement de manière élégante. Qu'est-ce que cela signifie "gracieusement" ? Vous voulez peut-être que votre application ne puisse plus soumettre de tâches au service d'exécution et, en même temps, vous voulez attendre que les N tâches existantes soient terminées. Vous pouvez y parvenir en utilisant un crochet d'arrêt en dernier recours :

final ExecutorService service = ... // get it somewhere

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Performing some shutdown cleanup...");
        service.shutdown();
        while (true) {
            try {
                System.out.println("Waiting for the service to terminate...");
                if (service.awaitTermination(5, TimeUnit.SECONDS)) {
                    break;
                }
            } catch (InterruptedException e) {
            }
        }
        System.out.println("Done cleaning");
    }
}));

Ce hook arrêtera le service, ce qui empêchera votre application de soumettre de nouvelles tâches, et attendra que toutes les tâches existantes soient terminées avant d'arrêter la JVM. La terminaison await bloquera pendant 5 secondes et retournera vrai si le service est arrêté. Ceci est fait dans une boucle de sorte que vous êtes sûr que le service s'arrêtera finalement. L'InterruptedException est avalée à chaque fois. C'est la meilleure façon d'arrêter un service exécuteur qui est réutilisé dans toute votre application.

Ce code n'est pas parfait. À moins que vous ne soyez absolument certain que vos tâches finiront par se terminer, vous pourriez vouloir attendre un délai d'attente donné et ensuite simplement quitter, abandonnant les threads en cours d'exécution. Dans ce cas, il serait logique d'appeler également shutdownNow() après le délai d'attente dans une dernière tentative d'interrompre les threads en cours ( shutdownNow() vous donnera également une liste des tâches en attente d'exécution). Si vos tâches sont conçues pour répondre à une interruption, cela fonctionnera parfaitement.

Un autre scénario intéressant est celui où vous avez un ScheduledExecutorService qui exécute une tâche périodique. La seule façon d'arrêter la chaîne des tâches périodiques est d'appeler shutdown() .

EDIT : J'aimerais ajouter que je ne recommanderais pas l'utilisation d'un crochet d'arrêt comme indiqué ci-dessus dans le cas général : cela peut être source d'erreurs et ne devrait être qu'un dernier recours. De plus, si vous avez plusieurs crochets d'arrêt enregistrés, l'ordre dans lequel ils seront exécutés est indéfini, ce qui peut être indésirable. Je préférerais que l'application appelle explicitement shutdown() sur InterruptedException .

15voto

Ravindra babu Points 5571

Le but de l'ExecutorService n'est-il pas de réutiliser les threads ? Alors pourquoi détruire l'ExecutorService si tôt ?

Oui. Vous ne devriez pas détruire et recréer ExecutorService fréquemment. Initialiser ExecutorService lorsque vous en avez besoin (principalement au démarrage) et le garder actif jusqu'à ce que vous en ayez fini avec lui.

N'est-il pas plus rationnel de créer simplement un ExecutorService (ou deux, selon le nombre dont vous avez besoin), puis, pendant l'exécution de l'application, de leur passer les tâches lorsqu'elles se présentent, et ensuite, à la sortie de l'application ou à d'autres étapes importantes, de fermer ces exécuteurs ?

Oui. C'est rationnel de fermer ExecutorService sur les étapes importantes comme la sortie de la demande, etc.

La deuxième question secondaire, un peu plus petite, concerne la plateforme Android. Si certains d'entre vous disent que ce n'est pas la meilleure idée de fermer les exécuteurs à chaque fois, et que vous programmez sur Android, pourriez-vous me dire comment vous gérez ces fermetures (pour être précis, quand vous les exécutez) lorsque nous traitons les différents événements du cycle de vie de l'application.

Supposons que ExecutorService est partagé entre différentes activités de votre application. Chaque activité sera mise en pause ou reprise à des intervalles de temps différents et vous avez toujours besoin d'une ExecutorService selon votre demande.

Au lieu de gérer l'état de ExecutorService dans les méthodes du cycle de vie de l'activité, déplacez la gestion de l'ExecutorService (création/arrêt) vers vos propres méthodes de gestion de l'activité. Service .

Créer ExecutorService dans le service => onCreate() et l'arrêter correctement dans onDestroy()

Méthode d'arrêt recommandée ExecutorService :

Comment arrêter correctement le service Executor de java ?

7voto

emon Points 140

Un ExecutorService doit être arrêté dès qu'il n'est plus nécessaire pour libérer les ressources du système et permettre l'arrêt en douceur de l'application. Comme les threads d'un ExecutorService peuvent être des threads non démon, ils peuvent empêcher l'arrêt normal de l'application. En d'autres termes, votre application reste en cours d'exécution après avoir terminé sa méthode principale.

Livre de référence

Chaperon:14 Page:814

1voto

Raison de l'appel de shutdown() sur ExecutorService

Aujourd'hui, j'ai rencontré une situation où je dois attendre qu'une machine soit prête avant de lancer une série de tâches sur cette machine.

Je fais un appel REST à cette machine, si je ne reçois pas 503 (Server Unavailable) alors la machine est prête à traiter mes demandes. J'attends donc de recevoir 200 (Success) pour le premier appel REST.

Il y a plusieurs façons d'y parvenir, j'ai utilisé ExecutorService pour créer un thread et le programmer pour qu'il s'exécute toutes les X secondes. Donc, j'ai besoin d'arrêter ce thread sur une condition, regardez ça...

final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    Runnable task = () -> {
        try {
            int statusCode = restHelper.firstRESTCall();

            if (statusCode == 200) {
                executor.shutdown();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    };

    int retryAfter = 60;
    executor.scheduleAtFixedRate(task, 0, retryAfter, TimeUnit.SECONDS);

La deuxième question secondaire, un peu plus petite, concerne la plateforme Android.

Je peux peut-être répondre si vous me fournissez un peu plus de contexte ! D'après mon expérience du développement Android, il est rare que vous ayez besoin de Threads. Est-ce que vous développez un jeu ou une application qui a besoin de threads pour les performances ? Si ce n'est pas le cas, sous Android, vous avez d'autres moyens de résoudre des problèmes comme le scénario que j'ai expliqué ci-dessus. Vous pouvez plutôt utiliser TimerTask, AsyncTask ou Handlers ou Loaders en fonction du contexte. En effet, si UIThread attend longtemps, vous savez ce qui se passe :/.

0voto

Devlax Points 3

Ceci est vrai même pour les engagements planifiés, par exemple pour un ScheduledExecutorService : les nouveaux cas de la mission réservée ne seront pas exécutés.

Nous devrions nous attendre à ce que vous ayez une application de confort qui dispose d'un agent d'administration faisant des courses.

Je n'en saisis pas le sens sans effort ? Peut-être avez-vous besoin que votre application n'ait pas la possibilité de soumettre d'autres missions à l'administration des agents et que, dans l'intervalle, vous deviez attendre la fin de vos engagements actuels.

Sauf si vous êtes tout à fait sûr que vos courses se termineront, vous devez rester assis pendant une pause donnée et ensuite sortir tout simplement, en abandonnant les cordes à courir.

Dans le cas où vos activités sont destinées à réagir aux interférences, cela fonctionnera très bien.

Une autre situation intrigante est le moment où vous avez un ScheduledExecutorService qui exécute une activité.

La meilleure façon d'arrêter la chaîne d'activité est d'appeler shutdown()

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