Maison >Java >javaDidacticiel >Comment utiliser le pool de threads Java pour optimiser nos applications
Le pool de threads est un outil, mais il ne convient pas à tous les scénarios. Lorsque nous utilisons des pools de threads, nous devons les configurer de manière appropriée en fonction de la nature de l'application, de la disponibilité des ressources informatiques et des besoins de l'application. Si le pool de threads n'est pas correctement configuré, cela peut entraîner une dégradation des performances de l'application ou des problèmes tels qu'un blocage ou une famine. Par conséquent, nous devons choisir le pool de threads avec soin.
Utilisez un pool de threads pour optimiser les scénarios d'utilisation des applications
Un grand nombre de tâches à court terme : si l'application doit gérer un grand nombre de tâches à court terme, l'utilisation d'un pool de threads peut éviter des créations et des destructions fréquentes de threads, réduisant ainsi le contexte des threads. Réduisez les frais de commutation, améliorant ainsi les performances et l'évolutivité des applications.
Accès simultané à la base de données : si l'application doit accéder simultanément à la base de données, l'utilisation du pool de threads peut utiliser pleinement la puissance de calcul du processeur multicœur et améliorer les performances et le débit de l'accès simultané à la base de données. .
Tâches gourmandes en calcul : si l'application doit effectuer des tâches gourmandes en calcul, le pool de threads peut être utilisé pour exécuter les tâches simultanément, en utilisant pleinement la puissance de calcul du processeur multicœur et en améliorant les performances et la réponse. rapidité des tâches gourmandes en calcul.
Applications basées sur des événements : si l'application est pilotée par des événements, l'utilisation d'un pool de threads peut éviter le blocage du thread de traitement des événements et améliorer la vitesse de réponse et le débit du traitement des événements.
Tâches de longue durée : si l'application doit gérer des tâches de longue durée, l'utilisation d'un pool de threads peut éviter d'occuper les ressources de thread pendant une longue période et améliorer la disponibilité et l'évolutivité de l'application.
Différentes configurations de pools de threads et dans quelles circonstances sont-ils utilisés :
1.FixedThreadPool
FixedThreadPool est un pool de threads de taille fixe qui pré-crée un certain nombre de threads lors de sa création. Lorsqu'une tâche doit être exécutée, le pool de threads sélectionnera un thread disponible pour exécuter la tâche. Si tous les threads exécutent des tâches, de nouvelles tâches attendront dans la file d'attente des tâches.
Lors de l'utilisation de FixedThreadPool, la principale chose à considérer est la taille du pool de threads. Si la taille du pool de threads est trop petite, les tâches peuvent être mises en file d'attente dans la file d'attente, ce qui affecte le temps de réponse de l'application. Si la taille du pool de threads est trop grande, cela peut consommer trop de ressources informatiques et entraîner une dégradation des performances des applications. Par conséquent, lors du choix d'une taille de pool de threads, vous devez prendre en compte les besoins informatiques de votre application et la disponibilité des ressources informatiques.
2.CachedThreadPool
CachedThreadPool est un pool de threads de taille dynamique qui ajuste automatiquement la taille du pool de threads en fonction du nombre de tâches. Lorsqu'une tâche doit être exécutée, le pool de threads crée un nouveau thread pour exécuter la tâche. S'il y a plusieurs tâches à effectuer, le pool de threads créera plusieurs threads. Lorsque les threads sont inactifs, le pool de threads recycle ces threads.
CachedThreadPool convient aux scénarios dans lesquels un grand nombre de tâches doivent être effectuées sur une courte période de temps. Puisqu'il peut ajuster dynamiquement la taille du pool de threads en fonction du nombre de tâches, il peut mieux utiliser les ressources informatiques, améliorant ainsi les performances de l'application.
3.SingleThreadExecutor
SingleThreadExecutor est un pool de threads avec un seul thread. Lorsqu'une tâche doit être exécutée, le pool de threads utilisera un thread unique pour exécuter la tâche. Si plusieurs tâches doivent être exécutées, elles attendront dans la file d’attente des tâches. Puisqu'il n'y a qu'un seul thread, SingleThreadExecutor convient aux scénarios qui nécessitent l'exécution séquentielle de tâches, telles que les pools de connexions à la base de données ou les processeurs de journaux.
4.ScheduledThreadPool
ScheduledThreadPool est un pool de threads utilisé pour effectuer des tâches planifiées. Il peut exécuter des tâches à intervalles spécifiés ou après un délai fixe. Par exemple, vous pouvez utiliser ScheduledThreadPool pour sauvegarder régulièrement votre base de données ou nettoyer vos journaux.
Lorsque vous utilisez ScheduledThreadPool, vous devez faire attention au temps d'exécution et à la répétition des tâches. Si une tâche prend beaucoup de temps à s’exécuter, cela peut affecter le temps d’exécution d’autres tâches. Si la tâche n'est pas récurrente, vous devrez peut-être l'annuler manuellement pour éviter qu'elle ne se poursuive.
5.WorkStealingThreadPool
WorkStealingThreadPool est un pool de threads qui utilise un algorithme de vol de travail. Il utilise plusieurs pools de threads, chacun avec une file d'attente de tâches. Lorsqu'un thread d'un pool de threads est inactif, il vole des tâches dans les files d'attente de tâches d'autres pools de threads pour les exécuter.
WorkStealingThreadPool convient aux scénarios dans lesquels plusieurs tâches indépendantes doivent être exécutées. Puisqu'il alloue dynamiquement les tâches et les threads, il permet une meilleure utilisation des ressources informatiques, améliorant ainsi les performances des applications.
Ceux-ci-dessus sont plusieurs pools de threads couramment utilisés. Bien entendu, Java fournit également d'autres pools de threads, tels que ForkJoinPool, CachedThreadExecutor, etc. Lors du choix d'un pool de threads, nous devons faire un choix en fonction des besoins de l'application et de la disponibilité des ressources informatiques.
Création personnalisée d'un pool de threads
Utilisez la classe d'usine Executors pour créer un pool de threads. Bien que cette méthode soit simple et rapide, nous devons parfois contrôler plus précisément le comportement du pool de threads, puis créer un pool de threads personnalisé.
Le pool de threads en Java est implémenté via la classe ThreadPoolExecutor, nous pouvons donc personnaliser le pool de threads en créant un objet ThreadPoolExecutor. La méthode constructeur de la classe ThreadPoolExecutor a plusieurs paramètres. Nous introduisons ici uniquement certains paramètres couramment utilisés.
corePoolSize : Le nombre de threads principaux dans le pool de threads, c'est-à-dire le nombre minimum de threads qui restent actifs dans le pool de threads. Lorsqu'une tâche est soumise, si le nombre de threads actifs est inférieur au nombre de threads principaux, de nouveaux threads seront créés pour traiter la tâche.
maximumPoolSize : Le nombre maximum de threads autorisés dans le pool de threads. Lorsqu'une tâche est soumise, si le nombre de threads actifs a atteint le nombre de threads principaux et que la file d'attente des tâches est pleine, de nouveaux threads seront créés pour traiter la tâche jusqu'à ce que le nombre de threads actifs atteigne le nombre maximum de threads.
keepAliveTime : durée pendant laquelle les threads inactifs des threads non principaux restent actifs. Lorsque le nombre de threads actifs est supérieur au nombre de threads principaux et que la durée de survie du thread inactif dépasse keepAliveTime, il sera détruit jusqu'à ce que le nombre de threads actifs ne dépasse pas le nombre de threads principaux.
workQueue : File d'attente des tâches, utilisée pour enregistrer les tâches en attente d'exécution. Java fournit plusieurs types de files d'attente de tâches, telles que SynchronousQueue, LinkedBlockingQueue, ArrayBlockingQueue, etc.
threadFactory : utilisé pour créer de nouveaux fils de discussion. Vous pouvez personnaliser la méthode de création de thread en implémentant l'interface ThreadFactory, par exemple en définissant le nom du thread, en définissant la priorité du thread, etc.
Les pools de threads créés sur mesure peuvent contrôler de manière plus flexible le comportement du pool de threads, comme l'ajustement du nombre de threads principaux et du nombre maximum de threads en fonction de différents scénarios d'application, la sélection de différents types de files d'attente de tâches, etc. Dans le même temps, vous devez également prêter attention aux principes de conception du pool de threads pour éviter de créer trop de threads, ce qui entraînerait un gaspillage de ressources système ou une concurrence de threads entraînant une dégradation des performances.
Stratégies d'optimisation des pools de threads Lorsque vous utilisez des pools de threads pour optimiser les performances des applications, vous devez faire attention à certaines stratégies d'optimisation, notamment la taille du pool de threads, le type de file d'attente des tâches, la gestion des exceptions du pool de threads, la surveillance du pool de threads, etc.
Taille du pool de threads : la taille du pool de threads doit être déterminée en fonction des besoins spécifiques de l'application. Si l'application doit gérer un grand nombre de tâches à court terme, vous pouvez définir une taille de pool de threads plus petite ; si l'application doit gérer des tâches gourmandes en calcul, vous pouvez définir une taille de pool de threads plus grande.
Type de file d'attente des tâches : Le type de file d'attente des tâches doit également être déterminé en fonction des besoins spécifiques de l'application. Si le nombre de tâches est important, mais le temps d'exécution de chaque tâche est court, une file d'attente illimitée peut être utilisée ; si le nombre de tâches est petit, mais le temps d'exécution de chaque tâche est long, une file d'attente illimitée peut être utilisée ;
Gestion des exceptions du pool de threads : les tâches du pool de threads peuvent générer des exceptions, et une gestion appropriée des exceptions est requise pour éviter que d'autres tâches du pool de threads ne soient affectées. Vous pouvez utiliser des blocs try-catch pour intercepter les exceptions levées par les tâches et les gérer de manière appropriée, comme la journalisation, la resoumission des tâches, etc.
Surveillance du pool de threads : la surveillance du pool de threads peut nous aider à comprendre l'état et les performances du pool de threads pour un réglage approprié. Vous pouvez utiliser JMX (Java Management Extensions) ou des composants de surveillance personnalisés pour surveiller l'état d'exécution du pool de threads, tel que le nombre de threads actifs dans le pool de threads, le nombre de tâches dans la file d'attente des tâches, le nombre de tâches terminées, etc.
Ci-dessous, nous allons parcourir un exemple pour démontrer comment utiliser un pool de threads pour optimiser les performances de votre application .
Exemple : Calcul de la séquence de Fibonacci
Nous allons montrer comment utiliser un pool de threads pour calculer la séquence de Fibonacci à travers un exemple simple pour montrer comment les pools de threads peuvent améliorer les performances de votre application.
La séquence de Fibonacci est une séquence définie de manière récursive, définie comme suit :
F(0) = 0
F(1) = 1
F(n) = F(n- 1) + F(n-2), n > 1
Nous pouvons utiliser l'algorithme récursif pour calculer la séquence de Fibonacci, mais l'algorithme récursif est moins efficace car il calculera à plusieurs reprises certaines valeurs. Par exemple, le calcul de F(5) nécessite le calcul de F(4) et F(3), le calcul de F(4) nécessite le calcul de F(3) et F(2), et le calcul de F(3) nécessite le calcul de F(2) et F(2) F(1), on voit que F(3) et F(2) sont calculés deux fois.
Nous pouvons utiliser un pool de threads pour éviter les calculs répétés et ainsi améliorer les performances de l'application. Les étapes spécifiques de mise en œuvre sont les suivantes :
Divisez la tâche en plusieurs sous-tâches, et chaque sous-tâche calcule la valeur d'une séquence de Fibonacci.
Soumettez les sous-tâches au pool de threads pour une exécution simultanée.
Utilisez ConcurrentHashMap pour mettre en cache les valeurs déjà calculées afin d'éviter les calculs répétés.
Attendez que toutes les tâches soient terminées et renvoyez les résultats.
Voici le code d'implémentation :
import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveTask; public class FibonacciTask extends RecursiveTask<Integer> { private static final long serialVersionUID = 1L; private static final Map<Integer, Integer> cache = new ConcurrentHashMap<>(); private final int n; public FibonacciTask(int n) { this.n = n; } @Override protected Integer compute() { if (n == 0) { return 0; } if (n == 1) { return 1; } Integer result = cache.get(n); if (result != null) { return result; } FibonacciTask f1 = new FibonacciTask(n - 1); FibonacciTask f2 = new FibonacciTask(n - 2); f1.fork(); f2.fork(); result = f1.join() + f2.join(); cache.put(n, result); return result; } public static void main(String[] args) throws ExecutionException, InterruptedException { ForkJoinPool pool = new ForkJoinPool(); FibonacciTask task = new FibonacciTask(10); System.out.println(pool.invoke(task)); } }
Dans le code ci-dessus, nous utilisons ForkJoinPool comme pool de threads. Chaque sous-tâche calcule la valeur d'une séquence de Fibonacci et utilise ConcurrentHashMap pour mettre en cache la valeur calculée afin d'éviter le comptage répété. Enfin, attendez que toutes les tâches soient terminées et renvoyez les résultats.
Nous pouvons voir que dans l'exemple ci-dessus, nous utilisons ForkJoinPool comme pool de threads et héritons de la classe RecursiveTask pour implémenter le calcul simultané de la séquence de Fibonacci. Dans la méthode calculate(), on vérifie d'abord si la valeur de la séquence de Fibonacci a été calculée dans le cache. Si elle a été calculée, le résultat dans le cache est renvoyé directement. Sinon, nous créons deux sous-tâches f1 et f2, les soumettons au pool de threads pour une exécution simultanée, utilisons la méthode join() pour attendre leurs résultats d'exécution et ajoutons leurs résultats d'exécution comme résultat d'exécution de la tâche en cours, et au en même temps, ajoutez ceci Les valeurs de la séquence de Bonacci et ses résultats de calcul sont stockés dans le cache afin que les résultats puissent être obtenus directement à partir du cache la prochaine fois que le calcul est effectué.
Dans la méthode main(), nous créons un objet ForkJoinPool et créons un objet FibonacciTask, puis appelons la méthode Invoke() pour exécuter la tâche et imprimer les résultats de l'exécution sur la console.
A travers cet exemple simple, nous pouvons voir que l'utilisation d'un pool de threads peut grandement améliorer les performances d'une application, notamment dans les tâches gourmandes en calculs. Le pool de threads peut exécuter des tâches simultanément, exploitant ainsi pleinement la puissance de calcul des processeurs multicœurs, évitant la création et la destruction fréquentes de threads, réduisant ainsi le coût de changement de contexte de thread et améliorant les performances et l'évolutivité des applications.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!