Préface. Oncle Petia
Alors, disons que nous voulions remplir une bouteille d'eau. Une bouteille et un robinet d'eau de l'oncle Petya sont disponibles. Aujourd'hui, l'oncle Petya a fait installer un nouveau robinet et il ne cessait de vanter sa beauté. Avant cela, il n'utilisait qu'un vieux robinet bouché, donc les files d'attente à la mise en bouteille étaient énormes. Après avoir tâtonné un peu, le bruit de l'eau se remplissant s'est fait entendre dans la direction du déversement, après 2 minutes la bouteille est encore en phase de remplissage, la file d'attente habituelle s'est formée derrière nous, et l'image dans ma tête est de la façon dont oncle est attentionné. Petya sélectionne uniquement les meilleures molécules H2O dans notre bouteille. L'oncle Petya, entraîné par la vie, calme les plus agressifs et promet d'en finir au plus vite. Ayant fini avec la bouteille, il prend la suivante et allume la pression habituelle, ce qui ne révèle pas toutes les capacités du nouveau robinet. Les gens ne sont pas contents...
Théorie
Le multithreading est la capacité d'une plateforme à créer plusieurs threads au sein d'un seul processus. Créer et exécuter un thread est beaucoup plus simple que créer un processus, donc s'il est nécessaire de mettre en œuvre plusieurs actions parallèles dans un même programme, des threads supplémentaires sont utilisés. Dans la JVM, n'importe quel programme s'exécute dans le thread principal et les autres sont lancés à partir de celui-ci. Au sein d’un même processus, les threads sont capables d’échanger des données entre eux. Lors du démarrage d'un nouveau thread, vous pouvez le déclarer comme thread utilisateur en utilisant la méthode
setDaemon(true);
ces threads se termineront automatiquement s’il n’y a plus d’autres threads en cours d’exécution. Les threads ont une priorité de travail (le choix de la priorité ne garantit pas que le thread de priorité la plus élevée se terminera plus rapidement que le thread de priorité inférieure).
- MIN_PRIORITÉ
- NORM_PRIORITY (par défaut)
- MAX_PRIORITY
Méthodes de base lorsque vous travaillez avec des flux :
run()
– exécute le thread
start()
– démarre un fil de discussion
getName()
– renvoie le nom du fil
setName()
– précise le nom du flux
wait()
– méthode héritée, le thread attend que la méthode soit appelée notify()
depuis un autre thread
notify()
– méthode héritée, reprend un thread précédemment arrêté
notifyAll()
– méthode héritée, reprend les threads précédemment arrêtés
sleep()
– met le flux en pause pendant une durée spécifiée
join()
– attend que le fil soit terminé
interrupt()
– interrompt l’exécution du thread
Plus de méthodes peuvent être trouvées
ici. Il est temps de penser à de nouveaux threads si votre programme a :
- L'accès au réseau
- Accès au système de fichiers
- Interface graphique
Classe de fil de discussion
Les threads en Java sont représentés comme une classe
Thread
et ses descendants. L'exemple ci-dessous est une implémentation simple de la classe stream.
import static java.lang.System.out;
public class ExampleThread extends Thread{
public static void main(String[] args) {
out.println("Основной поток");
new ExampleThread().start();
}
@Override
public void run() {
out.println("Новый поток");
}
}
En conséquence nous obtenons
Основной поток
Новый поток
Ici, nous créons notre classe et en faisons un descendant de la classe
Thread
, après quoi nous écrivons la méthode main() pour lancer le thread principal et remplacer la méthode
run()
de classe
Thread
. Maintenant, après avoir créé une instance de notre classe et exécuté sa méthode héritée,
start()
nous allons lancer un nouveau thread dans lequel tout ce qui est décrit dans le corps de la méthode sera exécuté
run()
. Cela semble compliqué, mais en regardant l'exemple de code, tout devrait être clair.
Interface exécutable
Oracle suggère également d'implémenter l'interface pour démarrer un nouveau thread
Runnable
, ce qui nous donne plus de flexibilité de conception que le seul héritage disponible dans l'exemple précédent (si vous regardez la source de la classe,
Thread
vous pouvez voir qu'elle implémente également l'interface
Runnable
). Utilisons la méthode recommandée pour créer un nouveau fil de discussion.
import static java.lang.System.out;
public class ExampleRunnable implements Runnable {
public static void main(String[] args) {
out.println("Основной поток");
new Thread(new ExampleRunnable()).start();
}
@Override
public void run() {
out.println("Новый поток");
}
}
En conséquence nous obtenons
Основной поток
Новый поток
Les exemples sont très similaires, car Lors de l'écriture du code, nous avons dû implémenter une méthode abstraite
run()
décrite dans l'interface
Runnable
. Lancer un nouveau fil de discussion est un peu différent. Nous avons créé une instance de la classe
Thread
en passant une référence à une instance de notre implémentation d'interface en tant que paramètre
Runnable
. C'est cette approche qui vous permet de créer de nouveaux threads sans hériter directement de la classe
Thread
.
Opérations longues
L'exemple suivant montrera clairement les avantages de l'utilisation de plusieurs threads. Disons que nous avons une tâche simple qui nécessite plusieurs calculs longs, avant cet article, nous l'aurions résolue dans une méthode,
main()
peut-être en la décomposant en méthodes distinctes pour faciliter la perception, peut-être même en classes, mais l'essence serait la même. Toutes les opérations seraient effectuées séquentiellement les unes après les autres. Simulons des calculs lourds et mesurons leur temps d'exécution.
public class ComputeClass {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 1");
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 2");
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 3");
long timeSpent = System.currentTimeMillis() - startTime;
System.out.println("программа выполнялась " + timeSpent + " миллисекунд");
}
}
En conséquence nous obtenons
complete 1
complete 2
complete 3
программа выполнялась 9885 миллисекунд
Le temps d'exécution laisse beaucoup à désirer, et pendant tout ce temps, nous regardons un écran de sortie vide, et la situation est très similaire à l'histoire de l'oncle Petya, seulement maintenant, dans son rôle, nous, les développeurs, n'avons pas profité de toutes les capacités des appareils modernes. Nous allons nous améliorer.
public class ComputeClass {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
new MyThread(1).start();
new MyThread(2).start();
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 3");
long timeSpent = System.currentTimeMillis() - startTime;
System.out.println("программа выполнялась " + timeSpent + " миллисекунд");
}
}
class MyThread extends Thread{
int n;
MyThread(int n){
this.n = n;
}
@Override
public void run() {
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete " + n);
}
}
En conséquence nous obtenons
complete 1
complete 2
complete 3
программа выполнялась 3466 миллисекунд
Le temps d'exécution a été considérablement réduit (cet effet peut ne pas être obtenu ou peut même augmenter le temps d'exécution sur les processeurs qui ne prennent pas en charge le multithreading). Il convient de noter que les threads peuvent se terminer dans le désordre et que si le développeur a besoin de prévisibilité des actions, il doit la mettre en œuvre de manière indépendante pour un cas spécifique.
Groupes de discussions
Les threads en Java peuvent être combinés en groupes ; la classe est utilisée pour cela
ThreadGroup
. Les groupes peuvent inclure à la fois des threads uniques et des groupes entiers. Cela peut être pratique si vous devez interrompre les flux associés, par exemple, au réseau lorsque la connexion est perdue. Vous pouvez en savoir plus sur les groupes
ici. J'espère que maintenant le sujet est devenu plus clair pour vous et que vos utilisateurs seront satisfaits.
GO TO FULL VERSION