Salut! Aujourd'hui, nous allons continuer à parler de multithreading. Examinons la classe Thread et ce que font quelques-unes de ses méthodes. Lorsque nous avons étudié les méthodes de classe précédemment, nous écrivions généralement ceci : <nom de la méthode> -> <ce que fait la méthode>. Cela ne fonctionnera pas avec
Thread
les méthodes de :) Elles ont une logique plus complexe que vous ne pourrez pas comprendre sans quelques exemples.
La méthode Thread.start()
Commençons par nous répéter. Comme vous vous en souvenez probablement, vous pouvez créer un thread en faisant en sorte que votre classe hérite de laThread
classe et en remplaçant la run()
méthode. Mais il ne démarre pas tout seul, bien sûr. Pour ce faire, nous appelons start()
la méthode de notre objet. Rappelons l'exemple de la leçon précédente :
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("Thread executed: " + getName());
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.start();
}
}
}
Remarque : Pour démarrer un thread, vous devez appeler lastart()
méthode spéciale plutôt que larun()
méthode ! C'est une erreur facile à commettre, surtout lorsque vous commencez à étudier le multithreading. Dans notre exemple, si vous appelez larun()
méthode 10 fois au lieu destart()
, vous obtiendrez ceci :
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.run();
}
}
}
Regardez les résultats de notre programme : Thread exécuté : Thread-0 Thread exécuté : Thread-1 Thread exécuté : Thread-2 Thread exécuté : Thread-3 Thread exécuté : Thread-4 Thread exécuté : Thread-5 Thread exécuté : Thread-6 Thread exécuté : Thread-7 Thread exécuté : Thread-8 Thread exécuté : Thread-9 Regardez l'ordre de la sortie : Tout se passe dans un ordre parfait. Bizarre, hein ? Nous n'y sommes pas habitués, car nous savons déjà que l'ordre dans lequel les threads sont démarrés et exécutés est déterminé par un intellect supérieur à l'intérieur de notre système d'exploitation : le planificateur de threads. Peut-être qu'on a juste eu de la chance ? Bien sûr, ce n'est pas une question de chance. Vous pouvez le vérifier en exécutant le programme plusieurs fois. Le problème est qu'appeler lerun()
La méthode directement n'a rien à voir avec le multithreading. Dans ce cas, le programme sera exécuté sur le thread principal, le même thread qui exécute la main()
méthode. Il suffit d'imprimer successivement 10 lignes sur la console et le tour est joué. 10 discussions n'ont pas été lancées. Alors, souvenez-vous de cela à l'avenir et vérifiez-vous constamment. Si vous voulez que la run()
méthode soit appelée, appelez start()
. Allons plus loin.
La méthode Thread.sleep()
Pour suspendre l'exécution du thread en cours pendant un certain temps, nous utilisons lasleep()
méthode . La sleep()
méthode prend un certain nombre de millisecondes comme argument, ce qui indique le temps nécessaire pour mettre le thread en veille.
public class Main {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(3000);
System.out.println(" - How long did I sleep? \n - " + ((System.currentTimeMillis()-start)) / 1000 + " seconds");
}
}
Sortie console : - Combien de temps ai-je dormi ? - 3 secondes Remarque : la sleep()
méthode est statique : elle dort le thread en cours. C'est-à-dire celui en cours d'exécution. Voici un autre point important : un thread endormi peut être interrompu. Dans ce cas, le programme lance un InterruptedException
. Nous allons considérer un exemple ci-dessous. Au fait, que se passe-t-il après le réveil du fil ? Continuera-t-il à être exécuté là où il s'est arrêté ? Non. Une fois qu'un thread s'est réveillé, c'est-à-dire que le temps passé en tant qu'argument à Thread.sleep()
s'est écoulé, il passe en runnableÉtat. Mais cela ne signifie pas que le planificateur de threads l'exécutera. Il peut très bien donner la préférence à quelque autre fil non dormant et permettre à notre fil fraîchement réveillé de continuer son travail un peu plus tard. N'oubliez pas ceci : se réveiller ne veut pas dire continuer à travailler immédiatement !
La méthode Thread.join()
Lajoin()
méthode suspend l'exécution du thread en cours jusqu'à ce qu'un autre thread se termine. Si nous avons 2 threads, t1
et t2
, et nous écrivons
t1.join()
puis t2
ne commencera pas tant qu'il t1
n'aura pas terminé son travail. La join()
méthode peut être utilisée pour garantir l'ordre d'exécution des threads. Considérons le join()
fonctionnement de la méthode dans l'exemple suivant :
public class ThreadExample extends Thread {
@Override
public void run() {
System.out.println("Thread started: " + getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + getName() + " is finished.");
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
ThreadExample t1 = new ThreadExample();
ThreadExample t2 = new ThreadExample();
t1.start();
/* The second thread (t2) will start running only after the first thread (t1)
is finished (or an exception is thrown) */
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
// The main thread will continue running only after t1 and t2 have finished
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All threads have finished. The program is finished.");
}
}
Nous avons créé une ThreadExample
classe simple. Sa tâche est d'afficher un message indiquant que le fil a commencé, de s'endormir pendant 5 secondes, puis de signaler enfin que le travail est terminé. Part de gâteau. La logique principale est dans la Main
classe. Regardez les commentaires : nous utilisons la join()
méthode pour gérer avec succès l'ordre d'exécution des threads. Si vous vous souvenez comment nous avons commencé ce sujet, l'ordre d'exécution est géré par le planificateur de threads. Il exécute les threads à sa propre discrétion : chaque fois d'une manière différente. Ici, nous utilisons la méthode pour garantir que le t1
thread sera d'abord démarré et exécuté en premier, puis let2
thread, et seulement après cela, le thread principal du programme continuera. Passons à autre chose. Dans les programmes réels, vous rencontrerez souvent des situations où vous devrez interrompre l'exécution d'un thread. Par exemple, notre thread est en cours d'exécution, mais il attend un certain événement ou une certaine condition. Si cela se produit, le thread s'arrête. Cela aurait probablement du sens s'il y avait une sorte de stop()
méthode. Mais ce n'est pas si simple. Il était une fois, Java disposait d'une Thread.stop()
méthode et permettait d'interrompre un thread. Mais il a ensuite été supprimé de la bibliothèque Java. Vous pouvez le trouver dans la documentation Oracle et voir qu'il est marqué comme obsolète. Pourquoi? Parce qu'il vient d'arrêter le fil sans rien faire d'autre. Par exemple, le thread peut travailler avec des données et changer quelque chose. Puis au milieu de son travail il fut brusquement et sans ménagement coupé par la stop()
méthode. Sans un arrêt approprié, ni la libération de ressources, ni même la gestion des erreurs - il n'y avait rien de tout cela. Pour exagérer légèrement, la stop()
méthode a simplement tout détruit sur son passage. C'était comme tirer le cordon d'alimentation de la prise pour éteindre l'ordinateur. Oui, vous pouvez obtenir le résultat souhaité. Mais tout le monde sait qu'après quelques semaines, l'ordinateur ne vous remerciera plus de le traiter de cette façon. C'est pourquoi la logique d'interruption des threads a changé en Java et utilise désormais une interrupt()
méthode spéciale.
La méthode Thread.interrupt()
Que se passe-t-il si lainterrupt()
méthode est appelée sur un thread ? Il y a 2 possibilités :
- Si l'objet était dans l'état d'attente, par exemple, en raison des méthodes
join
ousleep
, l'attente sera interrompue et le programme lancera unInterruptedException
. - Si le thread était dans un état de fonctionnement, l'
interrupted
indicateur booléen sera défini sur l'objet.
Thread
classe a la boolean isInterrupted()
méthode. Revenons à l'exemple de l'horloge qui était dans une leçon du cours de base. Pour plus de commodité, nous l'avons légèrement simplifié :
public class Clock extends Thread {
public static void main(String[] args) throws InterruptedException {
Clock clock = new Clock();
clock.start();
Thread.sleep(10000);
clock.interrupt();
}
public void run() {
Thread current = Thread.currentThread();
while (!current.isInterrupted())
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("The thread was interrupted");
break;
}
System.out.println("Tick");
}
}
}
Dans ce cas, l'horloge est démarrée et commence à tourner toutes les secondes. A la 10ème seconde, on interrompt le fil de l'horloge. Comme vous le savez déjà, si le thread que nous essayons d'interrompre est dans l'un des états d'attente, le résultat est un InterruptedException
. Il s'agit d'une exception vérifiée, nous pouvons donc facilement l'attraper et exécuter notre logique pour terminer le programme. Et c'est exactement ce que nous avons fait. Voici notre résultat : Tick Tick Tick Tcik Tick Tick Tick Tick Tick Le thread a été interrompu Ceci conclut notre introduction aux Thread
méthodes les plus importantes de la classe. Bonne chance!
GO TO FULL VERSION