1. Processus et threads
1. Quel est le processus?
Définition étroite: un processus est une instance d'un programme informatique exécuté.
Définition générale: un processus est une activité en cours d'exécution d'un programme avec certaines fonctions indépendantes concernant un certain ensemble de données. Il s'agit de l'unité de base de l'exécution dynamique du système d'exploitation. Dans les systèmes d'exploitation traditionnels, les processus sont à la fois des unités d'allocation de base et des unités d'exécution de base.
2. Qu'est-ce qu'un fil?
Les threads, parfois appelés processus légers (LWP), sont les plus petites unités de flux d'exécution du programme. Un thread standard se compose d'un ID de thread, d'un pointeur d'instructions actuel (PC), d'un ensemble de registres et d'une pile. De plus, un thread est une entité dans le processus et est l'unité de base qui est planifiée et envoyée indépendamment par le système. Le thread lui-même ne possède pas les ressources système, mais n'a que quelques ressources essentielles pendant le fonctionnement, mais elle peut partager toutes les ressources appartenant au processus avec d'autres threads appartenant au même processus.
3. Quelle est la différence entre un processus et un fil?
La principale différence entre les processus et les threads est qu'ils sont des méthodes de gestion des ressources du système d'exploitation différentes.
Un processus a un espace d'adressage indépendant. Après un processus, il n'affectera pas d'autres processus en mode protégé, et un thread est juste un chemin d'exécution différent dans un processus.
Les threads ont leur propre pile et leurs variables locales, mais il n'y a pas d'espace d'adressage séparé entre les threads. Si un fil meurt, cela signifie que l'ensemble du processus meurt. Par conséquent, les programmes multi-processus sont plus robustes que les programmes multi-thread, mais lors du changement de processus, ils consomment plus de ressources et sont moins efficaces. Cependant, pour certaines opérations simultanées qui nécessitent des opérations simultanées qui nécessitent du partage de certaines variables, elles ne peuvent utiliser que des threads, pas des processus.
En bref, la différence entre un thread et un processus est:
(1) Un programme a au moins un processus et un processus a au moins un thread;
(2) L'échelle de division des threads est plus petite que celle du processus, ce qui rend la concurrence des programmes multi-thread.
(3) Le processus a des unités de mémoire indépendantes pendant l'exécution, et plusieurs threads partagent la mémoire, ce qui améliore considérablement l'efficacité de fonctionnement du programme.
(4) Il existe une différence entre les threads et les processus pendant l'exécution. Chaque thread indépendant a une entrée pour l'exécution du programme, une séquence d'exécution et une sortie pour le programme. Cependant, les threads ne peuvent pas être exécutés indépendamment et doivent exister dans l'application, et plusieurs contrôles d'exécution de threads sont fournis par l'application.
(5) D'un point de vue logique, la signification du multi-threading réside dans la mesure dans une application, plusieurs pièces d'exécution peuvent être exécutées en même temps. Cependant, le système d'exploitation ne considère pas plusieurs threads comme plusieurs applications indépendantes pour réaliser la planification des processus, la gestion et l'allocation des ressources.
Il s'agit de la différence importante entre un processus et un fil.
2. Le cycle de vie d'un fil et les cinq États de base
Les fils Java ont cinq États de base:
(1) nouvel état (nouveau): Lorsque la paire d'objets de thread est créée, elle entre dans le nouvel état, tel que: Thread t = new Mythread ();
(2) État prêt (Runnable): Lorsque la méthode start () de l'objet thread (t.start ();), le thread entre dans l'état prêt. Un thread dans l'état prêt signifie simplement que le thread est prêt et attend que le CPU planifie l'exécution à tout moment, et non pas que le thread s'exécute immédiatement après l'exécution de T.Start ();
(3) État d'exécution: Lorsque le CPU commence à planifier des threads à l'état prêt, le thread peut être vraiment exécuté, c'est-à-dire qu'il entre dans l'état en cours d'exécution. Remarque: L'état prêt est la seule entrée à l'état en cours d'exécution, c'est-à-dire que si un thread veut entrer l'état en cours d'exécution, il doit d'abord être dans l'état prêt;
(4) État bloqué: pour une raison quelconque, un fil à l'état en cours d'exécution abandonne temporairement son utilisation du CPU et arrête l'exécution. À l'heure actuelle, il entre dans l'état de blocage. Il n'aura plus la chance d'être appelé par le processeur pour entrer dans l'état de course. Selon les raisons du blocage, les états de blocage peuvent être divisés en trois types:
① En attente de blocage: le thread de l'état en cours d'exécution exécute la méthode wait () pour faire entrer le thread dans l'état de blocage d'attente;
② Blocage synchronisé: le thread ne parvient pas à acquérir le verrou de synchronisation synchronisé (car le verrou est occupé par d'autres threads), et il entrera dans l'état de blocage synchronisé;
③Ather Blocking: Lors de l'appel du Sleep () du thread () ou de la jointure () ou de l'envoi d'une demande d'E / S, le thread entrera un état de blocage. Lorsque l'état de sommeil () a chronométré, join () a attendu que le fil se termine ou l'expression, ou le traitement d'E / S a été terminé, le fil est rentré à l'état prêt.
(5) État mort: le thread a fini d'exécuter ou de quitter la méthode run () en raison d'une exception, et le fil met fin à son cycle de vie.
3. Implémentation de Java Multithreading
En Java, si vous souhaitez implémenter un programme multithread, vous devez vous fier à la classe principale d'un fil (comme le concept d'une classe principale, qui représente la classe principale d'un fil), mais la classe principale de ce fil doit avoir des exigences particulières lors de la définition. Cette classe peut hériter de la classe de threads ou implémenter l'interface Runnable pour compléter la définition.
1. Hériter de la classe de threads pour implémenter le multi-threading
java.lang.thread est une classe responsable des opérations de threads. Toute classe peut devenir la classe principale d'un fil s'il hérite de la classe de thread. Puisqu'il s'agit de la classe principale, il doit avoir ses méthodes d'utilisation et la méthode principale démarrée par le thread doit écraser la méthode run () dans la classe de threads.
Définissez la classe corporelle d'un fil:
class Mythread étend Thread {// La classe principale du titre de chaîne privée de thread; public mythread (string title) {this.title = title; } @Override public void run () {// La méthode principale du thread pour (int x = 0; x <10; x ++) {System.out.println (this.title + "run, x =" + x); }}}Maintenant qu'il existe une classe de thread et qu'il y a des méthodes de fonctionnement correspondantes, l'objet doit être généré et les méthodes à l'intérieur doivent être appelées, donc le programme suivant est écrit:
classe publique TestDemo {public static void main (String [] args) {mythread mt1 = new mythread ("thread a"); Mythread mt2 = new mythread ("thread b"); Mythread mt3 = new mythread ("thread c"); mt1.run (); mt2.run (); mt3.run (); }Résultats en cours:
Thread a Runs, x = 0
Fiffez A Runs, x = 1
Thread a Runs, x = 2
Thread a Runs, x = 3
Thread A Runs, x = 4
Thread a Runs, x = 5
Thread a Runs, x = 6
Thread A Runs, x = 7
Thread a Runs, x = 8
Thread A Runs, x = 9
Le thread B fonctionne, x = 0
Le thread b fonctionne, x = 1
Le thread B fonctionne, x = 2
Le thread B fonctionne, x = 3
Le thread B fonctionne, x = 4
Le thread B fonctionne, x = 5
Le thread B fonctionne, x = 6
Le thread B fonctionne, x = 7
Le thread B fonctionne, x = 8
Le thread B fonctionne, x = 9
Le thread C fonctionne, x = 0
Le thread C fonctionne, x = 1
Le thread C fonctionne, x = 2
Le thread C fonctionne, x = 3
Le thread C fonctionne, x = 4
Le thread C fonctionne, x = 5
Le thread C fonctionne, x = 6
Le thread C fonctionne, x = 7
Le thread C fonctionne, x = 8
Le thread C fonctionne, x = 9
Nous avons constaté que les opérations ci-dessus ne démarrent pas vraiment le multi-threading, car l'exécution de plusieurs threads doit être exécutée alternativement, et à ce moment-là, il est exécuté séquentiellement, et le code de chaque objet continuera d'être exécuté vers le bas après l'exécution du code de chaque objet.
Si vous voulez vraiment démarrer le multi-threading dans un programme, vous devez vous fier à une méthode de la classe de threads: public void start (), ce qui signifie que vous commencez vraiment le multi-threading. Après avoir appelé cette méthode, la méthode run () sera appelée indirectement:
classe publique TestDemo {public static void main (String [] args) {mythread mt1 = new mythread ("thread a"); Mythread mt2 = new mythread ("thread b"); Mythread mt3 = new mythread ("thread c"); mt1.start (); mt2.start (); mt3.start (); }}Résultats en cours:
Le thread C fonctionne, x = 0
Thread a Runs, x = 0
Le thread B fonctionne, x = 0
Fiffez A Runs, x = 1
Le thread C fonctionne, x = 1
Thread a Runs, x = 2
Le thread b fonctionne, x = 1
Thread a Runs, x = 3
Thread A Runs, x = 4
Thread a Runs, x = 5
Le thread C fonctionne, x = 2
Le thread C fonctionne, x = 3
Le thread C fonctionne, x = 4
Le thread C fonctionne, x = 5
Le thread C fonctionne, x = 6
Le thread C fonctionne, x = 7
Le thread C fonctionne, x = 8
Le thread C fonctionne, x = 9
Thread a Runs, x = 6
Thread A Runs, x = 7
Thread a Runs, x = 8
Thread A Runs, x = 9
Le thread B fonctionne, x = 2
Le thread B fonctionne, x = 3
Le thread B fonctionne, x = 4
Le thread B fonctionne, x = 5
Le thread B fonctionne, x = 6
Le thread B fonctionne, x = 7
Le thread B fonctionne, x = 8
Le thread B fonctionne, x = 9
Pour le moment, vous pouvez constater que plusieurs threads s'exécutent alternativement les uns avec les autres, mais les résultats de chaque exécution sont différents. Grâce au code ci-dessus, nous pouvons tirer une conclusion: si vous souhaitez démarrer un thread, vous devez vous fier à la méthode start () de la classe de threads à exécuter. Après le début du thread, la méthode run () sera appelée par défaut.
Après avoir appelé la méthode start (), une série de choses compliquées s'est produite:
(1) démarrer un nouveau thread d'exécution (avec une nouvelle pile d'appels);
(2) le thread est transféré du nouvel état à l'état runnable;
(3) Lorsque le thread aura la possibilité d'exécuter, sa méthode Target Run () s'exécutera.
Remarque: Pour Java, la méthode run () n'a rien de spécial. Comme la méthode Main (), cela signifie simplement que le nouveau thread connaît le nom (et la signature) de la méthode de l'appel. Par conséquent, il est légal d'appeler la méthode d'exécution sur Runnable ou Thread, mais ne démarre pas un nouveau thread.
Explication: Pourquoi devez-vous appeler start () au lieu d'appeler directement Run () quand un thread démarre?
Nous avons constaté qu'après appeler start (), il exécute en fait la méthode Run () remplacée, alors pourquoi ne pas appeler directement la méthode run ()? Pour expliquer ce problème, ouvrez le code source de la classe de thread et observez la définition de la méthode start ():
public synchronisé void start () {if (ThreadStatus! = 0) Jetez un nouveau IllégalThreadStateException (); groupe.add (ceci); booléen a démarré = false; essayez {start0 (); démarré = true; } enfin {try {if (! démarré) {group.ThreadStartFailed (this); }} catch (Throwable ignore) {}}} private native void start0 ();Ouvrez le code source de cette méthode et vous constaterez d'abord que la méthode lancera une exception "illégalTreadStateException". D'une manière générale, si une méthode utilise le lancer pour lancer un objet d'exception, cette exception doit être capturée à l'aide d'essayer ... Catch ou lancé en utilisant les lancers sur la déclaration de la méthode, mais il n'y a rien dans ce domaine. Pourquoi? Parce que cette classe d'exception appartient à une sous-classe d'exception d'exécution (RuntimeException):
java.lang.object
| - java.lang.throwable
| - java.lang.exception
| - java.lang.runtimeException
| - java.lang.illegalargumentException
| - java.lang.illegalthreadstateException
Cette exception sera lancée lorsqu'un objet de thread sera démarré à plusieurs reprises, c'est-à-dire qu'un objet de thread ne peut être démarré qu'une seule fois.
L'une des parties les plus critiques de la méthode start () est la méthode start0 (), et cette méthode utilise une définition de mots clés native.
Le mot-clé natif fait référence à l'interface native Java, c'est-à-dire que Java est utilisé pour appeler les fonctions de fonction du système d'exploitation natif pour effectuer certaines opérations spéciales. Un tel développement de code est presque rarement observé dans Java car la plus grande caractéristique de Java est la portabilité. Si un programme ne peut être utilisé que sur un système d'exploitation fixe, la portabilité sera complètement perdue, donc cette opération n'est généralement pas utilisée.
La mise en œuvre du multithreading doit nécessiter le support du système d'exploitation. Ensuite, la méthode Start0 () ci-dessus est en fait très similaire à la méthode abstraite sans corps de méthode. Ce corps de méthode est remis au JVM pour implémenter, c'est-à-dire que le JVM dans Windows peut utiliser la méthode A pour implémenter Start0 (), tandis que le JVM dans Linux peut utiliser la méthode B pour implémenter Start0 (), mais lors de l'appel, il ne se souciera pas de la méthode spécifique de mise en œuvre de la méthode start0 (), mais uniquement sur le résultat de l'opération finale et remis à la JVM pour correspondre à différents systèmes opérationnels.
Par conséquent, dans les opérations multi-thread, l'utilisation de la méthode start () pour démarrer des opérations multithread nécessite des appels de fonction du système d'exploitation.
2. Implémentez l'interface runnable pour implémenter
L'utilisation de la classe de threads peut en effet faciliter la mise en œuvre multi-lancement, mais le plus grand inconvénient de cette méthode est le problème de l'héritage unique. À cette fin, l'interface Runnable peut également être utilisée en Java pour implémenter le multi-threading. La définition de cette interface est la suivante:
Interface publique Runnable {public void run ();}Implémentez le multi-threading via l'interface Runnable:
class Mythread implémente Runnable {// Le titre de chaîne privée de classe principale du thread; public mythread (string title) {this.title = title; } @Override public void run () {// La méthode principale du thread pour (int x = 0; x <10; x ++) {System.out.println (this.title + "run, x =" + x); }}}Ce n'est pas très différent de la manière précédente de hériter des classes de threads, mais un avantage est qu'il évite la limitation de l'héritage unique.
Mais le problème est là. Comme mentionné précédemment, si vous souhaitez commencer le multi-lancement, vous devez vous fier à la méthode start () de la classe de threads. Lorsque vous héritez de la classe de threads, vous pouvez hériter directement de cette méthode et l'utiliser. Mais maintenant, vous implémentez l'interface gérée. Sans cette méthode, vous pouvez l'hériter. Que devez-vous faire?
Pour résoudre ce problème, vous devez toujours compter sur la classe de threads pour le terminer. Un constructeur est défini dans la classe de threads pour recevoir l'objet d'interface Runnable:
Thread public (cible Runnable);
Démarrer le multithreading à l'aide de la classe de threads:
classe publique TestDemo {public static void main (String [] args) lève l'exception {mythread mt1 = new mythread ("thread a"); Mythread mt2 = new mythread ("thread b"); Mythread mt3 = new mythread ("thread c"); nouveau thread (mt1) .start (); nouveau thread (mt2) .start (); nouveau thread (mt3) .start (); }}Résultats en cours:
Thread a Runs, x = 0
Le thread B fonctionne, x = 0
Le thread b fonctionne, x = 1
Le thread C fonctionne, x = 0
Le thread B fonctionne, x = 2
Fiffez A Runs, x = 1
Le thread B fonctionne, x = 3
Le thread C fonctionne, x = 1
Le thread C fonctionne, x = 2
Le thread B fonctionne, x = 4
Le thread B fonctionne, x = 5
Thread a Runs, x = 2
Thread a Runs, x = 3
Thread A Runs, x = 4
Thread a Runs, x = 5
Thread a Runs, x = 6
Thread A Runs, x = 7
Thread a Runs, x = 8
Thread A Runs, x = 9
Le thread B fonctionne, x = 6
Le thread B fonctionne, x = 7
Le thread B fonctionne, x = 8
Le thread B fonctionne, x = 9
Le thread C fonctionne, x = 3
Le thread C fonctionne, x = 4
Le thread C fonctionne, x = 5
Le thread C fonctionne, x = 6
Le thread C fonctionne, x = 7
Le thread C fonctionne, x = 8
Le thread C fonctionne, x = 9
À l'heure actuelle, non seulement le démarrage multithread est atteint, mais aussi aucune limitation d'héritage unique.
3. La troisième méthode pour implémenter le multi-threading: utilisez l'interface appelée pour implémenter
Le multithreading à l'aide de l'interface Runnable peut éviter la limitation de l'héritage unique, mais il y a un problème que la méthode run () dans l'interface Runnable ne peut pas renvoyer le résultat de l'opération. Pour résoudre ce problème, une nouvelle interface est fournie: l'interface appelable (java.util.concurrent. callable).
Interface publique Callable <v> {public V Call () lève une exception;}Après avoir exécuté la méthode Call () dans l'interface appelée, un résultat sera renvoyé. Le type de résultat renvoyé est déterminé par les génériques sur l'interface appelable.
Le fonctionnement spécifique de l'implémentation de l'interface appelée pour implémenter le multi-threading est:
Créez une classe d'implémentation de l'interface appelée et implémentation de la méthode clall (); Ensuite, utilisez la classe FutureTask pour envelopper l'objet de la classe d'implémentation callable et utilisez cet objet FutureTask comme cible de l'objet de thread pour créer un thread.
Définissez une classe de carrosserie de filetage:
import java.util.concurrent.callable; class myThread implémente callable <string> {private int ticket = 10; @Override public String Call () lève une exception {pour (int i = 0; i <20; i ++) {if (this.ticket> 0) {System.out.println ("Vendre des billets, le nombre restant de votes est" + this.ticket -); }} RETOUR "Les billets ont été épuisés"; }}La classe de thread ne prend pas directement en charge l'interface appelée. Après JDK1.5, une classe est fournie:
java.util.concurrent.futuretask <v>
Cette classe est principalement responsable du fonctionnement de l'objet d'interface appelable. Sa structure de définition est la suivante:
classe publique Futuretask <v>
prolonge l'objet
implémente RunnableFurture <v>
L'interface RunnableFurture a la définition suivante:
Interface publique RunnableFurture <v>
étend coulable, futur <v>
Le constructeur suivant est défini dans la classe Futuretask:
public Futuretask (callable <v> callable)
Maintenant, vous pouvez enfin recevoir l'objet d'interface appelable via la classe FutureTask. Le but de la réception est d'obtenir le résultat de retour de la méthode Call ().
D'après l'analyse ci-dessus, nous pouvons trouver:
La classe FutureTask peut recevoir des objets d'interface appelés, et la classe FutureTask implémente l'interface RunnableFurture, qui hérite de l'interface Runnable.
Ainsi, nous pouvons commencer le multithreading comme ceci:
classe publique TestDemo {public static void main (String [] args) lève une exception {mythread mt1 = new mythread (); Mythread mt2 = new mythread (); FutureTask <string> task1 = new FutureTask <string> (mt1); // get call () Method to Retour le résultat FutureTask <string> task2 = new FutureTask <string> (mt2); // get call () méthode pour renvoyer le résultat // FutureTask est une sous-classe de l'interface Runnable. Vous pouvez utiliser la construction de classe de threads pour recevoir des objets de tâche Nouveau thread (tâche1) .start (); nouveau thread (tâche2) .start (); // Une fois l'exécution multithread terminée, vous pouvez utiliser la méthode GET () dans l'interface parent de FutureTask pour obtenir le résultat d'exécution System.out.println ("Retour du thread 1:" + tâche1.get ()); System.out.println ("Résultat de retour de Thread 2:" + Task2.get ()); }}Résultats en cours:
Vendre des billets, le nombre restant de votes est de 10
Vendre des billets, le nombre restant de votes est de 10
Vendre des billets, le nombre restant de votes est de 9
Vendre des billets, le nombre restant de votes est de 8
Vendre des billets, le nombre restant de votes est de 7
Vendre des billets, le nombre restant de votes est de 9
Vendre des billets, le nombre restant de votes est de 6
Vendre des billets, le nombre restant de votes est de 8
Vendre des billets, le nombre restant de votes est de 5
Vendre des billets, le nombre restant de votes est de 7
Vendre des billets, le nombre restant de votes est de 4
Vendre des billets, le nombre restant de votes est de 6
Vendre des billets, le nombre restant de votes est 3
Vendre des billets, le nombre restant de votes est de 5
Vendre des billets, le nombre restant de votes est 2
Vendre des billets, le nombre restant de votes est de 4
Vendre des billets, le nombre restant de votes est 1
Vendre des billets, le nombre restant de votes est 3
Vendre des billets, le nombre restant de votes est 2
Vendre des billets, le nombre restant de votes est 1
Le résultat de retour du fil 1: le billet a été épuisé. Le résultat de retour du thread 2: le billet a été épuisé.
résumé:
Ce qui précède explique trois façons d'implémenter le multithreading. Pour le démarrage du thread, ils sont tous appelés la méthode start () de l'objet thread. Il est important de noter que la méthode start () ne peut pas être appelée deux fois sur le même objet de thread.