1. Aperçu du fil de discussion
Les threads sont l’unité d’exécution de base pour l’exécution du programme. Lorsque le système d'exploitation (à l'exclusion des systèmes d'exploitation à thread unique, tels que le premier DOS de Microsoft) exécute un programme, un processus est établi dans le système et, dans ce processus, au moins un thread doit être établi (ce thread est appelé le thread principal). thread). thread) comme point d’entrée pour l’exécution de ce programme. Par conséquent, tout programme exécuté dans le système d’exploitation possède au moins un thread principal.
Les processus et les threads sont deux modèles d'exploitation essentiels dans les systèmes d'exploitation modernes. Il peut y avoir plusieurs processus dans le système d'exploitation, y compris des processus système (processus créés en interne par le système d'exploitation) et des processus utilisateur (processus créés par des programmes utilisateur) ; un processus peut avoir un ou plusieurs threads. Il n'y a pas de mémoire partagée entre les processus, ce qui signifie que les processus du système s'exécutent dans leurs propres espaces mémoire indépendants. Les threads d'un processus peuvent partager l'espace mémoire alloué par le système au processus.
Les threads peuvent non seulement partager la mémoire du processus, mais également disposer de leur propre espace mémoire, également appelé pile de threads. Il est alloué par le système lors de la création du thread. Il est principalement utilisé pour sauvegarder le thread. données utilisées à l'intérieur du thread, telles que la pile de threads. Variables définies dans la fonction d'exécution.
Remarque : tout thread exécutera une fonction lors de sa création. Cette fonction est appelée fonction d'exécution de thread. Cette fonction peut également être considérée comme le point d'entrée du thread (similaire à la fonction principale du programme). Quel que soit le langage ou la technologie utilisée pour créer un thread, cette fonction doit être exécutée (l'expression de cette fonction peut être différente, mais il y aura toujours une telle fonction). Par exemple, le troisième paramètre de la fonction API CreateThread utilisé pour créer un thread sous Windows est le pointeur de cette fonction d'exécution.
Une fois que le système d'exploitation a divisé le processus en plusieurs threads, ces threads peuvent être exécutés simultanément sous la gestion du système d'exploitation, améliorant ainsi considérablement l'efficacité d'exécution du programme. Bien que l'exécution des threads ressemble à plusieurs threads s'exécutant en même temps d'un point de vue macro, il ne s'agit en fait que d'une dissimulation du système d'exploitation. Puisqu’un processeur ne peut exécuter qu’une seule instruction à la fois, il est impossible d’exécuter deux tâches en même temps sur un ordinateur doté d’un seul processeur. Afin d'améliorer l'efficacité d'exécution du programme, le système d'exploitation supprimera un thread lorsqu'il est inactif et laissera d'autres threads l'exécuter. Cette méthode est appelée planification des threads. La raison pour laquelle nous voyons plusieurs threads s'exécuter en même temps sur la surface est que le temps de commutation entre différents threads est très court et que, dans des circonstances normales, la commutation est très fréquente. Supposons que nous ayons les threads A et B. Au moment de l'exécution, il se peut qu'après que A s'exécute pendant 1 milliseconde, après être passé à B, B s'exécute pendant encore 1 milliseconde, puis passe à A, et A s'exécute pendant encore 1 milliseconde. Étant donné qu'une milliseconde est difficile à percevoir pour les gens ordinaires, à première vue, il semble que A et B soient exécutés en même temps, mais en fait A et B sont exécutés alternativement.
2. Les avantages que nous apportent les threads
Une utilisation appropriée des threads peut réduire les coûts de développement et de maintenance et même améliorer les performances d'applications complexes. Par exemple, dans les applications GUI, les événements peuvent être mieux traités grâce à la nature asynchrone des threads ; dans les programmes de serveur d'applications, plusieurs threads peuvent être établis pour gérer les demandes des clients. Les threads peuvent même simplifier la mise en œuvre de machines virtuelles. Par exemple, le garbage collector de la machine virtuelle Java (JVM) s'exécute généralement dans un ou plusieurs threads. Par conséquent, l’utilisation de threads améliorera nos applications dans les cinq aspects suivants :
1. Utiliser pleinement les ressources du processeur
La plupart des ordinateurs dans le monde ne disposent désormais que d’un seul processeur. Il est donc particulièrement important d’utiliser pleinement les ressources du processeur. Lors de l'exécution d'un programme monothread, le processeur peut être inactif alors que le programme est bloqué. Cela entraînera un gaspillage important de ressources informatiques. L'utilisation du multithreading dans un programme peut exécuter d'autres threads lorsqu'un thread est en veille ou bloqué et que le processeur est inactif. De cette façon, il est difficile pour le processeur de rester inactif. Les ressources du processeur sont donc pleinement utilisées.
2. Simplifiez le modèle de programmation
Si le programme n'effectue qu'une seule tâche, écrivez simplement un programme monothread et écrivez le code selon les étapes pour effectuer la tâche. Mais pour effectuer plusieurs tâches, si vous utilisez toujours un seul thread, vous devez déterminer dans le programme si et quand chaque tâche doit être exécutée. Par exemple, il affiche les heures, les minutes et les secondes d'une horloge. Si vous utilisez un seul thread, vous devez juger le temps de rotation et l'angle de ces trois pointeurs un à un dans la boucle. Si trois threads sont utilisés pour gérer l’affichage de ces trois pointeurs, cela signifie alors effectuer une tâche distincte pour chaque thread. Cela aide les développeurs à comprendre et à maintenir le programme.
3. Simplifiez le traitement des événements asynchrones
Lorsqu'une application serveur reçoit différentes connexions client, le moyen le plus simple de la gérer consiste à créer un thread pour chaque connexion client. Le thread d'écoute est alors toujours chargé d'écouter les requêtes du client. Si ce type d'application est traité par un seul thread, lorsque le thread d'écoute reçoit une requête client, il commence à lire les données envoyées par le client. Après avoir lu les données, la méthode de lecture est bloquée, c'est-à-dire ce thread. Will ne pourra plus écouter les demandes des clients. Si vous souhaitez gérer plusieurs requêtes client dans un seul thread, vous devez utiliser des connexions Socket non bloquantes et des E/S asynchrones. Cependant, l'utilisation d'E/S asynchrones est plus difficile à contrôler et plus sujette aux erreurs que l'utilisation d'E/S synchrones. Par conséquent, les événements asynchrones tels que les requêtes multiples peuvent être gérés plus facilement à l’aide du multithreading et des E/S synchrones.
4. Rendre l'interface graphique plus efficace
Lorsque vous utilisez un seul thread pour traiter les événements GUI, une boucle doit être utilisée pour rechercher les événements GUI pouvant survenir à tout moment. Au sein de la boucle, en plus de rechercher les événements GUI, d'autres codes de programme doivent être exécutés. Si le code est trop long, les événements de l'interface graphique seront "gelés" jusqu'à ce que le code soit exécuté.
Dans les frameworks GUI modernes (tels que SWING, AWT et SWT), un thread de répartition d'événements (EDT) distinct est utilisé pour analyser les événements GUI. Lorsque nous appuyons sur un bouton, la fonction d'événement de clic du bouton sera appelée dans ce fil de répartition d'événements. Étant donné que la tâche d'EDT est uniquement d'analyser les événements de l'interface graphique, la réponse aux événements de cette manière est très rapide.
5. Économies de coûts
Il existe généralement trois méthodes pour améliorer l’efficacité de l’exécution d’un programme :
(1) Augmentez le nombre de processeurs dans l'ordinateur.
(2) Démarrer plusieurs processus pour un programme
(3) Utilisez plusieurs processus dans le programme.
La première méthode est la plus simple à mettre en œuvre, mais elle est aussi la plus coûteuse. Cette méthode ne nécessite aucune modification du programme. En théorie, n'importe quel programme peut utiliser cette méthode pour améliorer l'efficacité d'exécution. Bien que la deuxième méthode ne nécessite pas l'achat de nouveau matériel, il n'est pas facile de partager des données. Si la tâche à accomplir par ce programme nécessite le partage de données, cette méthode n'est pas pratique et le démarrage de plusieurs threads consommera beaucoup de système. ressources. La troisième méthode comble juste les défauts de la première méthode, tout en héritant de ses avantages. En d'autres termes, il n'est pas nécessaire d'acheter un CPU, et il n'occupera pas non plus beaucoup de ressources système en démarrant trop de threads (par défaut, l'espace mémoire occupé par un thread est bien plus petit que l'espace mémoire occupé par un processus) . Multiple), et le multithreading peut simuler le mode d'exécution de plusieurs processeurs. Par conséquent, l'utilisation du multithreading est le moyen le moins coûteux d'améliorer l'efficacité de l'exécution du programme.
3. Modèle de thread Java
Puisque Java est un langage purement orienté objet, le modèle de thread de Java est également orienté objet. Java encapsule toutes les fonctions nécessaires des threads via la classe Thread. Pour créer un thread, il doit y avoir une fonction d'exécution de thread. Cette fonction d'exécution de thread correspond à la méthode run de la classe Thread. La classe Thread possède également une méthode start, qui est chargée d'établir un thread, ce qui équivaut à appeler la fonction de création de thread Windows CreateThread. Lorsque la méthode start est appelée, si le thread est établi avec succès, la méthode run de la classe Thread est automatiquement appelée. Par conséquent, toute classe Java qui hérite de Thread peut créer un thread via la méthode start de la classe Thread. Si vous souhaitez exécuter votre propre fonction d'exécution de thread, vous devez remplacer la méthode run de la classe Thread.
En plus de la classe Thread dans le modèle de thread Java, il existe également une interface Runnable qui identifie si une classe Java peut être utilisée comme classe de thread. Cette interface n'a qu'une seule exécution de méthode abstraite, qui est la fonction d'exécution de thread de Java. modèle de fil. Par conséquent, le seul critère pour une classe de thread est de savoir si la classe implémente la méthode run de l'interface Runnable. En d'autres termes, une classe avec une fonction d'exécution de thread est une classe de thread.
Comme le montre ce qui précède, il existe deux façons de créer des threads en Java. L'une consiste à hériter de la classe Thread et l'autre à implémenter l'interface Runnable et à créer des threads via Thread et la classe qui implémente Runnable. ces deux méthodes sont essentiellement considérées comme une méthode, c'est-à-dire que le thread est créé via la classe Thread et que la méthode run est exécutée. Mais leur grande différence est que les threads sont créés en héritant de la classe Thread. Bien que cela soit plus facile à implémenter, puisque Java ne prend pas en charge l'héritage multiple, si cette classe de thread hérite de Thread, elle ne peut pas hériter d'autres classes. méthodes pour établir des threads en implémentant l'interface Runnable, afin que les classes de thread puissent hériter des classes liées à l'entreprise au lieu de la classe Thread si nécessaire.