Quel est l'avantage d'utiliser ExecutorService
sur les threads en cours d'exécution en passant un Runnable
dans le constructeur Thread
?
ExecutorService
résume un grand nombre des complexités associées aux abstractions de niveau inférieur comme le brut Thread
. Il fournit des mécanismes pour démarrer, fermer, soumettre, exécuter et bloquer en toute sécurité la fin réussie ou brusque des tâches (exprimée en Runnable
ou Callable
).
De JCiP , section 6.2, directement de la bouche du cheval:
Executor
peut être une interface simple, mais elle constitue la base d'un cadre flexible et puissant pour l'exécution de tâches asynchrones qui prend en charge une grande variété de politiques d'exécution de tâches. Il fournit un moyen standard de découplage soumission de tâche de exécution de tâche, décrivant les tâches commeRunnable
. Les implémentationsExecutor
fournissent également une prise en charge du cycle de vie et des crochets pour ajouter la collecte de statistiques, la gestion des applications et la surveillance. ... L'utilisation d'unExecutor
est généralement le chemin le plus simple pour implémenter une conception producteur-consommateur dans votre application.
Plutôt que de passer votre temps à mettre en œuvre (souvent de manière incorrecte et avec beaucoup d'efforts) l'infrastructure sous-jacente du parallélisme, le j.u.concurrent
framework vous permet de vous concentrer sur les tâches de structuration, les dépendances, le parallélisme potentiel. Pour une large gamme d'applications simultanées, il est simple d'identifier et d'exploiter les limites des tâches et d'utiliser j.u.c
, vous permettant de vous concentrer sur le sous-ensemble beaucoup plus petit des vrais défis de concurrence qui peut nécessiter des solutions plus spécialisées.
En outre, malgré l'apparence générale, la page API Oracle résumant les utilitaires de concurrence inclut des arguments vraiment solides pour les utiliser, notamment:
Les développeurs sont susceptibles de déjà comprendre 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 simultanées sont beaucoup plus simples à déboguer lorsqu'elles sont basées sur des composants fiables et bien testés.
Cette question sur SO demande un bon livre, auquel la réponse immédiate est JCiP. Si ce n'est pas déjà fait, procurez-vous-en un exemplaire. L'approche globale de la concurrence présentée ici va bien au-delà de cette question, et vous fera économiser beaucoup de chagrin à long terme.
Un avantage que je vois est dans la gestion/planification de plusieurs threads. Avec ExecutorService, vous n'avez pas à écrire votre propre gestionnaire de threads qui peut être en proie à des bugs. Cela 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 trivial, 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 existe encore de nombreux 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 clair à utiliser ExecutorService.
Les limitations suivantes du Thread traditionnel surmontées par le framework Executor (framework Thread Pool intégré).
StackOverflowException
, par conséquent notre JVM plantera.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 demandes ou des tâches.
L'utilisation de Thread Pool vous permet de modifier votre politique d'exécution selon vos besoins. vous pouvez passer d'un seul thread à plusieurs threads en remplaçant simplement l'implémentation ExecutorService.
Thread Pool in Java augmente la stabilité du système en créant un nombre configuré de threads décidé en fonction de la charge du système et des ressources disponibles.
Thread Pool libère le développeur d'applications de la gestion des threads et permet de se concentrer sur la logique métier.
Voici quelques avantages:
Est-ce vraiment si cher de créer un nouveau fil?
Comme référence, je viens de créer 60 000 threads avec des méthodes Runnable
s avec des méthodes run()
vides. Après avoir créé chaque thread, j'ai appelé immédiatement sa méthode start(..)
. Cela a pris environ 30 secondes d'activité intense du processeur. Des expériences similaires ont été faites 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 pile, donc vous manquerez de mémoire , (2) il pourrait y avoir une limite sur le nombre de threads par processus imposé par le système d'exploitation, mais pas nécessairement, il semble .
Donc, pour autant que je puisse voir, si nous parlons de lancer disons 10 threads par seconde, et ils se terminent tous plus vite que les nouveaux démarrent, et 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 puisse toujours être plus pratique ou lisible d'exprimer certaines idées de concurrence dans le code.) D'un autre côté, si vous planifiez des centaines ou des milliers de tâches par seconde, ce qui prend du temps à s'exécuter, vous pouvez 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 demandes adressées à un serveur, et que l'intensité des demandes reçues par votre serveur augmente. Mais par exemple un thread en réponse à chaque événement d'entrée utilisateur (appui sur une touche, mouvement de la souris) semble parfaitement bien, tant que les tâches sont brèves.
ExecutorService donne également accès à FutureTask qui retournera à la classe appelante les résultats d'une tâche en arrière-plan une fois terminée. Dans le cas de la mise en œuvre de Callable
public class TaskOne implements Callable<String> {
@Override
public String call() throws Exception {
String message = "Task One here. . .";
return message;
}
}
public class TaskTwo implements Callable<String> {
@Override
public String call() throws Exception {
String message = "Task Two here . . . ";
return message;
}
}
// from the calling class
ExecutorService service = Executors.newFixedThreadPool(2);
// set of Callable types
Set<Callable<String>>callables = new HashSet<Callable<String>>();
// add tasks to Set
callables.add(new TaskOne());
callables.add(new TaskTwo());
// list of Future<String> types stores the result of invokeAll()
List<Future<String>>futures = service.invokeAll(callables);
// iterate through the list and print results from get();
for(Future<String>future : futures) {
System.out.println(future.get());
}
La création d'un grand nombre de threads sans restriction au seuil maximal peut entraîner le manque d'application de la mémoire du tas. À cause de cela, la création d'un ThreadPool est une bien meilleure solution. 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 en Java. La classe Executors fournit une implémentation simple d'ExecutorService à l'aide de ThreadPoolExecutor.
Source:
Avant Java 1.5 version, Thread/Runnable était conçu pour deux services distincts
ExecutorService dissocie ces deux services en désignant Runnable/Callable comme unité de travail et Executor comme mécanisme pour exécuter (avec cycle de vie) l'unité de travail