Quels sont les avantages d’utiliser un ExecutorService?

Quel est l’avantage d’utiliser ExecutorService sur les threads en cours d’exécution en passant un Runnable dans le constructeur Thread ?

ExecutorService élimine un grand nombre des complexités associées aux abstractions de niveau inférieur telles que les Thread bruts. Il fournit des mécanismes permettant de démarrer, de fermer, de soumettre, d’exécuter et de bloquer en toute sécurité l’arrêt réussi ou brutal des tâches (exprimé en Runnable ou Callable ).

De JCiP , Section 6.2, directement de la bouche du cheval:

Executor peut être une interface simple, mais il constitue la base d’une infrastructure flexible et puissante pour l’exécution de tâches asynchrones qui prend en charge une grande variété de stratégies d’exécution de tâches. Il fournit un moyen standard de découpler la soumission des tâches de l’exécution des tâches , en décrivant les tâches comme étant Runnable . Les implémentations d’ Executor fournissent également une prise en charge du cycle de vie et des points d’ancrage pour l’ajout de la collecte de statistiques, la gestion des applications et la surveillance. … L’ utilisation d’un Executor est généralement la voie la plus facile pour implémenter une conception producteur-consommateur dans votre application.

Plutôt que de passer votre temps à mettre en œuvre (souvent à tort et avec effort) l’infrastructure sous-jacente du parallélisme, l’ juconcurrent vous permet de vous concentrer sur la structuration des tâches, les dépendances et le parallélisme potentiel. Pour un large éventail d’applications concurrentes, il est facile d’identifier et d’exploiter les limites des tâches et d’utiliser juc , ce qui vous permet de vous concentrer sur le sous-ensemble de problèmes de concomitance plus petits qui nécessitent des solutions plus spécialisées.

En outre, malgré l’aspect et la convivialité, la page API Oracle récapitulant les utilitaires de simultanéité inclut des arguments très solides pour les utiliser, notamment:

Les développeurs sont probablement déjà familiarisés avec les classes de bibliothèque standard. Il n’est donc pas nécessaire d’apprendre l’API et le comportement des composants simultanés ad hoc. De plus, les applications concurrentes sont beaucoup plus simples à déboguer lorsqu’elles reposent sur des composants fiables et testés.

Cette question sur SO concerne un bon livre, auquel la réponse immédiate est JCiP. Si vous ne l’avez pas déjà fait, procurez-vous une copie. L’approche globale de la concurrence présentée ici va bien au-delà de cette question et vous évitera beaucoup de chagrin à long terme.

Un avantage que je vois est la gestion / planification de plusieurs threads. Avec ExecutorService, vous n’avez pas besoin d’écrire votre propre gestionnaire de threads qui peut être affecté par des bogues. Ceci est particulièrement utile si votre programme doit exécuter plusieurs threads à la fois. Par exemple, vous voulez exécuter deux threads à la fois, vous pouvez facilement le faire comme ceci:

 ExecutorService exec = Executors.newFixedThreadPool(2); exec.execute(new Runnable() { public void run() { System.out.println("Hello world"); } }); exec.shutdown(); 

L’exemple peut être sortingvial, mais essayez de penser que la ligne “hello world” consiste en une opération lourde et que vous souhaitez que cette opération s’exécute dans plusieurs threads à la fois afin d’améliorer les performances de votre programme. Ceci n’est qu’un exemple, il y a encore beaucoup de cas où vous souhaitez planifier ou exécuter plusieurs threads et utiliser ExecutorService comme gestionnaire de threads.

Pour exécuter un seul thread, je ne vois aucun avantage évident à utiliser ExecutorService.

Voici quelques avantages:

  1. Le service exécuteur gère le thread de manière asynchrone
  2. Utilisez callable pour obtenir le résultat de retour après la fin du thread.
  3. Gérer l’allocation du travail au thread libre et à la revente du travail terminé à partir du thread pour atsortingbuer automatiquement un nouveau travail
  4. fork – joint le framework pour le parallel processing
  5. Meilleure communication entre les threads
  6. invokeAll et invokeAny donnent plus de contrôle pour exécuter tout ou partie du thread à la fois
  7. shutdown offre la possibilité de terminer tout le travail assigné au thread
  8. Les services exécuteurs planifiés fournissent des méthodes pour produire des invocations répétables d’exécutables et d’appelants. Espérons que cela vous aidera

Les limitations suivantes du thread traditionnel sont surmontées par le framework Executor (framework Thread Pool intégré).

  • Une mauvaise gestion des ressources, c’est-à-dire qu’elle continue à créer de nouvelles ressources pour chaque requête. Aucune limite à la création de ressource. En utilisant le cadre Executor, nous pouvons réutiliser les ressources existantes et limiter la création de ressources.
  • Not Robust : Si nous continuons à créer un nouveau thread, nous obtiendrons StackOverflowException exception StackOverflowException conséquent, notre JVM se bloquera.
  • Overhead Creation of time : Pour chaque demande, nous devons créer une nouvelle ressource. Créer une nouvelle ressource prend du temps. c’est à dire Thread Creating> task. En utilisant le framework Executor, nous pouvons intégrer le Thread Pool.

Avantages du pool de threads

  • L’utilisation du pool de threads réduit le temps de réponse en évitant la création de threads lors du traitement des requêtes ou des tâches.

  • L’utilisation du pool de threads vous permet de modifier votre stratégie d’exécution selon vos besoins. Vous pouvez passer d’un seul thread à plusieurs threads en remplaçant simplement l’implémentation d’ExecutorService.

  • Le pool de threads dans l’application Java augmente la stabilité du système en créant un nombre configuré de threads en fonction de la charge du système et des ressources disponibles.

  • Le pool de threads libère les développeurs d’applications des fonctionnalités de gestion des threads et permet de se concentrer sur la logique métier.

La source

Est-ce vraiment si cher de créer un nouveau sujet?

En tant que référence, je viens de créer 60 000 threads avec Runnable s avec les méthodes run() vides. Après avoir créé chaque thread, j’ai appelé sa méthode de start(..) immédiatement. Cela a pris environ 30 secondes d’activité intense du processeur. Des expériences similaires ont été réalisées en réponse à cette question . Le résumé de ceux-ci est que si les threads ne se terminent pas immédiatement et qu’un grand nombre de threads actifs s’accumulent (quelques milliers), alors il y aura des problèmes: (1) chaque thread a une stack, donc vous manquerez de mémoire , (2) il pourrait y avoir une limite au nombre de threads par processus imposée par le système d’exploitation, mais pas nécessairement, semble-t-il .

Donc, pour autant que je sache, si nous parlons de lancer par exemple 10 threads par seconde, et qu’ils finissent tous plus vite que les nouveaux, nous pouvons garantir que ce taux ne sera pas trop dépassé, alors l’ExecutorService n’offre aucun avantage concret en termes de performances ou de stabilité visibles. (Bien qu’il soit toujours plus pratique ou plus lisible d’exprimer certaines idées de concurrence dans le code.) Par contre, si vous planifiez des centaines ou des milliers de tâches par seconde, ce qui prend du temps, vous risquez de rencontrer de gros problèmes. tout de suite. Cela peut se produire de manière inattendue, par exemple si vous créez des threads en réponse à des requêtes adressées à un serveur, et que l’intensité des requêtes reçues par votre serveur augmente. Mais, par exemple, un thread en réponse à chaque événement de saisie utilisateur (pression sur une touche, mouvement de la souris) semble parfaitement correct, à condition que les tâches soient brèves.

ExecutorService donne également access à FutureTask qui renverra à la classe appelante les résultats d’une tâche en arrière-plan une fois celle-ci terminée. Dans le cas de l’implémentation de Callable

 public class TaskOne implements Callable { @Override public Ssortingng call() throws Exception { Ssortingng message = "Task One here. . ."; return message; } } public class TaskTwo implements Callable { @Override public Ssortingng call() throws Exception { Ssortingng message = "Task Two here . . . "; return message; } } // from the calling class ExecutorService service = Executors.newFixedThreadPool(2); // set of Callable types Set>callables = new HashSet>(); // add tasks to Set callables.add(new TaskOne()); callables.add(new TaskTwo()); // list of Future types stores the result of invokeAll() List>futures = service.invokeAll(callables); // iterate through the list and print results from get(); for(Futurefuture : futures) { System.out.println(future.get()); } 

Avant la version Java 1.5, Thread / Runnable était conçu pour deux services distincts

  1. Unité de travail
  2. Exécution de cette unité de travail

ExecutorService dissocie ces deux services en désignant Runnable / Callable comme unité de travail et Executor comme mécanisme d’exécution (avec cycle de vie) de l’unité de travail

La création d’un grand nombre de threads sans ressortingction sur le seuil maximal peut entraîner un manque de mémoire du tas sur l’application. À cause de cela, créer un ThreadPool est une solution bien meilleure. En utilisant ThreadPool, nous pouvons limiter le nombre de threads pouvant être regroupés et réutilisés.

La structure des exécuteurs facilite le processus de création de pools de threads dans Java. La classe des exécuteurs fournit une implémentation simple de ExecutorService à l’aide de ThreadPoolExecutor.

La source:

Qu’est-ce que le cadre des exécuteurs?