Java Multithreading (un)
En tant que point de connaissance très important à Java, il est toujours nécessaire de le résumer ici.
1. Le cycle de vie d'un fil et les cinq états de base
En ce qui concerne le cycle de vie des fils de Java, regardons d'abord la photo classique suivante:
La figure ci-dessus couvre essentiellement les points de connaissance importants du multi-threading en Java. Une fois que vous maîtrisez les points de connaissance de la figure ci-dessus, vous maîtriserez essentiellement le multi-threading en Java. Incluant principalement:
Les fils Java ont cinq États de base
Nouveau état (nouveau): Lorsqu'une paire d'objets de thread est créée, elle entre dans un nouvel état, tel que: Thread t = new Mythread ();
É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 ();
État en cours d'exécution: Lorsque le CPU commence à planifier des threads à l'état prêt, le fil 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;
É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:
1. 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;
2. Blocage synchronisé - Le fil ne parvient pas à acquérir le verrou de synchronisation synchronisé (car le verrou est occupé par d'autres threads), il entrera dans l'état de blocage synchronisé;
3. Autre blocage - le fil entrera un état de blocage en appelant Sleep () ou join () du thread ou en émettant une demande d'E / S. 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.
Dead: le thread a terminé l'exécution ou quitte la méthode run () en raison d'une exception, et le fil met fin à son cycle de vie.
2. Création et démarrage de Java Multithreads
Il existe trois formes de base de création de threads en Java
1. Hériter de la classe de thread et remplacer la méthode run () de la classe.
class Mythread étend Thread {private int i = 0; @Override public void run () {for (i = 0; i <100; i ++) {System.out.println (thread.currentThread (). GetName () + "" + i); }}} classe publique threadtest {public static void main (string [] args) {for (int i = 0; i <100; i ++) {System.out.println (thread.currentThread (). getName () + "" + i); if (i == 30) {thread mythread1 = new mythread (); // Créer un nouveau thread Mythread1 Ce fil entre dans le nouvel thread d'état myThread2 = new Mythread (); // Créer un nouveau fil MyThread2 Ce fil entre dans le nouvel état mythread1.start (); // appelle la méthode start () pour faire entrer le thread entre l'état prêt mythread2.start (); // appelle la méthode start () pour faire entrer le thread entre l'état prêt}}}}Comme indiqué ci-dessus, héritant de la classe de threads, une nouvelle classe de thread Mythread est définie en écrasant la méthode run (), où le corps de la méthode de la méthode Run () représente la tâche que le fil doit effectuer, et est appelé le corps d'exécution du thread. Lors de la création de cet objet de classe de thread, un nouveau thread est créé et entre dans le nouvel état de thread. En appelant la méthode start () référencée par l'objet thread, le thread entre dans l'état prêt. À l'heure actuelle, le thread ne peut pas être exécuté immédiatement, selon le synchronisation du CPU.
2. Implémentez l'interface Runnable et remplacez la méthode run () de l'interface. La méthode run () est également un corps d'exécution de thread, créent une instance de la classe d'implémentation Runnable et utilise cette instance comme cible de la classe de threads pour créer un objet de thread. L'objet thread est l'objet de thread réel.
class MyRunnable implémente runnable {private int i = 0; @Override public void run () {for (i = 0; i <100; i ++) {System.out.println (thread.currentThread (). GetName () + "" + i); }}} classe publique threadtest {public static void main (string [] args) {for (int i = 0; i <100; i ++) {System.out.println (thread.currentThread (). getName () + "" + i); if (i == 30) {runnable myrunnable = new myrunnable (); // Créer un objet de thread de classe d'implémentation Runnable Thread1 = nouveau thread (myrunnable); // Créez un nouveau thread avec MyRunnable comme un thread Thread Thread2 = nouveau thread (MyRunnable); thread1.start (); // appelle la méthode start () pour faire entrer le thread entre le thread de l'état prêt2.start (); }}}} Je crois que tout le monde connaît les deux façons ci-dessus de créer de nouveaux fils. Alors, quelle est la relation entre le thread et Runnable? Examinons d'abord l'exemple suivant.
classe publique threadtest {public static void main (string [] args) {for (int i = 0; i <100; i ++) {System.out.println (thread.currentThread (). getName () + "" + i); if (i == 30) {runnable myrunnable = new myrunnable (); Thread thread = new Mythread (myrunnable); thread.start (); }}}} classe MyRunnable implémente Runnable {private int i = 0; @Override public void run () {System.out.println ("dans Myrunnable Run"); pour (i = 0; i <100; i ++) {System.out.println (thread.currentThread (). getName () + "" + i); }}} class Mythread étend Thread {private int i = 0; public mythread (runnable runnable) {super (runnable); } @Override public void run () {System.out.println ("dans mythread run"); pour (i = 0; i <100; i ++) {System.out.println (thread.currentThread (). getName () + "" + i); }}}De même, il en va de même pour la création de threads qui implémentent l'interface runnable, la différence est que
1 thread thread = new Mythread (myrunnable);
Cette méthode peut-elle donc créer avec succès un nouveau fil? La réponse est oui. Quant au corps d'exécution du thread à l'heure actuelle, la méthode run () est-elle dans l'interface myrunnable ou la méthode run () dans la classe Mythread? Grâce à la sortie, nous savons que le corps d'exécution du thread est la méthode run () dans la classe Mythread. En fait, la raison est très simple, car la classe de thread elle-même implémente également l'interface Runnable, et la méthode run () est d'abord définie dans l'interface Runnable.
Interface publique Runnable {public abstract void run (); } Jetons un coup d'œil à l'implémentation de la méthode run () dans l'interface Runnable dans la classe de threads:
@Override public void run () {if (cible! = Null) {cible.run (); }}C'est-à-dire que lors de l'exécution de la méthode run () dans la classe de threads, il déterminera d'abord si la cible existe. S'il existe, la méthode run () dans la cible est exécutée, c'est-à-dire la méthode run () dans la classe qui implémente l'interface Runnable et écrase la méthode run (). Cependant, dans les colonnes ci-dessus, en raison de l'existence du polymorphisme, la méthode run () dans la classe de threads n'est pas du tout exécutée, mais le type d'exécution, c'est-à-dire que la méthode run () dans la classe Mythread est directement exécutée.
3. Créez des threads à l'aide d'interfaces appelables et futures. Plus précisément, il crée une classe d'implémentation pour l'interface appelable et implémente la méthode clall (). Et utilisez la classe FutureTask pour envelopper l'objet de classe d'implémentation callable et utilisez cet objet FutureTask comme cible de l'objet de thread pour créer un thread.
Cela semble un peu compliqué, mais il sera clair si vous regardez directement un exemple.
classe publique threadtest {public static void main (string [] args) {callable <nteger> myCallable = new myCallable (); // Créer un objet myCallable FutureTask <Integer> ft = new FutureTask <Integer> (myCallable); // Utilisez FutureTask pour envelopper l'objet myCallable pour (int i = 0; i <100; i ++) {System.out.println (Thread.currentThread (). GetName () + "" + i); if (i == 30) {thread thread = new Thread (ft); // L'objet FutureTask crée un nouveau thread comme cible de l'objet thread thread.start (); // Le thread entre dans l'état prêt}} System.out.println ("Le thread principal de Loop a été exécuté .."); essayez {int sum = ft.get (); // Obtenez le résultat renvoyé par la méthode Call () dans le nouveau thread System.out.out.print ("SUM =" + SUM); } catch (InterruptedException e) {e.printStackTrace (); } catch (EXECUTUTIONException e) {e.printStackTrace (); }}} classe MyCallable implémente callable <Integer> {private int i = 0; // Contrairement à la méthode RUN (), la méthode Call () a une valeur de retour @Override public Integer Call () {int sum = 0; for (; i <100; i ++) {System.out.println (thread.currentThread (). getName () + "" + i); sum + = i; } RETOUR SUM; }}Tout d'abord, nous avons constaté qu'en implémentant l'interface appelée, la méthode run () n'est plus la méthode run (), mais la méthode Call (). Cette méthode Call () est le corps d'exécution du thread et a également une valeur de retour! Lors de la création d'un nouveau fil, l'objet myCallable est enveloppé via FutureTask et sert également de cible pour l'objet de thread. Regardez ensuite la définition de la classe Futuretask:
classe publique FutureTask <v> implémente RunnableFuture <v> {// ....} Interface publique RunnableFuture <v> étend Runnable, Future <v> {void run (); }Par conséquent, nous avons constaté que la classe FutureTask implémente réellement les interfaces à la fois en cours et futurs, ce qui le rend deux caractéristiques de l'avenir et de la course. Grâce à la fonction Runnable, il peut être utilisé comme cible de l'objet de thread, et la fonction future lui permet d'obtenir la valeur de retour de la méthode Call () dans le thread nouvellement créé.
Après avoir exécuté ce programme, nous constatons que SUM = 4950 est toujours la dernière sortie. "Le thread principal de la boucle a été exécuté ..." est susceptible d'être sorti au milieu de la boucle de thread enfant. À partir du mécanisme de planification du thread du processeur, nous savons qu'il n'y a aucun problème avec la synchronisation de sortie de "Le thread principal de la boucle a été exécuté ...", alors pourquoi SUM = 4950 sera-t-il pour toujours?
La raison en est que lorsque la méthode Call () du thread de l'enfant est obtenue via la méthode ft.get (), lorsque la méthode du thread de l'enfant n'a pas encore été exécutée, la méthode ft.get () bloquera jusqu'à ce que la méthode Call () soit exécutée avant l'obtention de la valeur de retour.
Ce qui précède explique principalement trois méthodes de création de threads communs. Pour le démarrage des threads, 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.
Iii. Statut prêt, exécuté et mort de Java Multithreading
L'état prêt est converti à l'état en cours: lorsque ce thread obtient la ressource du processeur;
L'état en cours d'exécution est converti à l'état prêt: lorsque ce thread appelle activement la méthode de rendement () ou perd des ressources de processeur pendant l'exécution.
L'état de course est converti à l'état mort: lorsque le corps d'exécution du thread est terminé ou une exception se produit.
Il convient de noter ici que lorsque la méthode de rendement () du thread est appelée, le thread passe de l'état en cours d'exécution à l'état prêt, mais quel fil dans l'état prêt du CPU est planifié a un certain aléatoire. Par conséquent, il peut se produire qu'après que le thread A appelle la méthode de rendement (), le CPU planifie toujours le thread A.
En raison des besoins réels de l'entreprise, il est souvent rencontré qu'un fil doit être résilié à une occasion spécifique de le faire entrer dans un état mort. La méthode la plus courante est actuellement de définir une variable booléenne, et lorsque les conditions sont remplies, le corps d'exécution du thread sera exécuté rapidement. comme:
classe publique threadtest {public static void main (String [] args) {myrunnable myrunnable = new myrunnable (); Thread thread = nouveau thread (myrunnable); pour (int i = 0; i <100; i ++) {System.out.println (thread.currentThread (). getName () + "" + i); if (i == 30) {thread.start (); } if (i == 40) {myrunnable.stopThread (); }}}} classe MyRunnable implémente Runnable {private boolean stop; @Override public void run () {for (int i = 0; i <100 &&! Stop; i ++) {System.out.println (thread.currentThread (). GetName () + "" + i); }} public void starthread () {this.stop = true; }}Nous continuerons à régler les articles connexes à l'avenir. Merci pour votre soutien à ce site!
Série d'articles:
Explication des instances multipliées Java (i)
Explication détaillée des instances multipliées Java (II)
Explication détaillée des instances multipliées Java (III)