Le thread est l'unité de base du fonctionnement du système d'exploitation. Il est encapsulé dans un processus. Un processus peut contenir plusieurs threads. Même si nous ne créons pas de thread manuellement, le processus aura un thread par défaut en cours d'exécution.
Pour la JVM, lorsque nous écrivons un programme monothread à exécuter, il y a au moins deux threads en cours d'exécution dans la JVM, l'un est le programme que nous avons créé et l'autre est le garbage collection.
Informations de base sur le sujet
Nous pouvons obtenir des informations sur le thread actuel via la méthode Thread.currentThread() et les modifier.
Regardons le code suivant :
Thread.currentThread().setName("Test");
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
nom = Thread.currentThread().getName();
priorité = Thread.currentThread().getPriority();
groupName = Thread.currentThread().getThreadGroup().getName();
isDaemon = Thread.currentThread().isDaemon();
System.out.println("Nom du fil :" + nom);
System.out.println("Priorité :" + priorité);
GroupName , chaque thread sera dans un groupe de threads par défaut. Nous pouvons également créer explicitement un groupe de threads. Un groupe de threads peut également contenir des groupes de sous-threads, de sorte que les threads et les groupes de threads forment une arborescence.
Name , chaque thread aura un nom Si cela n'est pas spécifié explicitement, la règle de nom est "Thread-xxx".
Priority , chaque thread aura sa propre priorité et la manière dont la JVM gère la priorité est "préemptive". Lorsque la JVM trouve un thread avec une priorité élevée, elle exécute immédiatement le thread ; pour plusieurs threads avec des priorités égales, la JVM les interroge. La priorité des threads Java va de 1 à 10, la valeur par défaut étant 5. La classe Thread définit deux constantes : MIN_PRIORITY et MAX_PRIORITY pour représenter les priorités les plus élevées et les plus basses.
Nous pouvons regarder le code suivant, qui définit deux threads avec des priorités différentes :
Fil de discussion2 = nouveau fil("haut")
{
exécution publique vide()
{
pour (int i = 0; i < 5; i++)
{
System.out.println("Le thread 2 est en cours d'exécution.");
}
}
} ;
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.MAX_PRIORITY);
thread1.start();
thread2.start();
}
thread1.start();
}
Comment créer un fil
Le contenu ci-dessus montre tous certaines informations dans le fil de discussion par défaut, alors comment créer un fil de discussion ? En Java, nous avons 3 façons de créer des threads.
Les threads en Java héritent de la classe Thread ou implémentent l'interface Runnable. Passons-les un par un.
Utiliser des classes internes pour créer des threads
Nous pouvons utiliser des classes internes pour créer des threads. Le processus consiste à déclarer une variable de type Thread et à remplacer la méthode run. L'exemple de code est le suivant :
Nous pouvons dériver une classe de Thread et remplacer sa méthode run de la même manière que ci-dessus. L'exemple de code est le suivant :
vide statique public createThreadBySubClass()
{
Fil MyThread = new MyThread();
thread.start();
}
Nous pouvons définir une classe pour implémenter l'interface Runnable, puis utiliser une instance de cette classe comme paramètre pour construire le constructeur de variable Thread. L'exemple de code est le suivant :
public static void createThreadByRunnable()
{
MyRunnable runnable = new MyRunnable();
Thread thread = nouveau Thread (exécutable);
thread.start();
}
Cela implique le mode d'exécution du multi-threading en Java. Pour Java, lorsque le multi-threading est en cours d'exécution, il existe des différences entre le « multi-threading multi-objets » et le « multi-threading mono-objet » :
Multi-objet multi-threading , le programme crée plusieurs objets thread pendant l'exécution et un thread s'exécute sur chaque objet.
Multi-threading à objet unique , le programme crée un objet thread pendant l'exécution et exécute plusieurs threads dessus.
Évidemment, du point de vue de la synchronisation et de la planification des threads, le multi-threading multi-objets est plus simple. Parmi les trois méthodes de création de threads ci-dessus, les deux premières sont « multi-objets multi-thread » et la troisième peut utiliser soit « multi-objets multi-thread » soit « mono-objet mono-thread ».
Examinons l'exemple de code suivant, qui utilisera la méthode Object.notify. Cette méthode réveillera un thread sur l'objet et la méthode Object.notifyAll réveillera tous les threads sur l'objet.
public static void main (String[] args) lance InterruptedException
{
notifierTest();
notifierTest2();
notifierTest3();
}
private static void notifyTest() lance InterruptedException
{
MonThread[] arrThreads = nouveau MonThread[3];
pour (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = new MonThread();
arrThreads[i].id = je;
arrThreads[i].setDaemon(true);
arrThreads[i].start();
}
Thread.sleep(500);
pour (int i = 0; i < arrThreads.length; i++)
{
synchronisé(arrThreads[i])
{
arrThreads[i].notify();
}
}
}
private static void notifyTest2() lance InterruptedException
{
MyRunner[] arrMyRunners = new MyRunner[3];
Thread[] arrThreads = nouveau Thread[3];
pour (int i = 0; i < arrThreads.length; i++)
{
arrMyRunners[i] = new MyRunner();
arrMyRunners[i].id = i;
arrThreads[i] = nouveau Thread(arrMyRunners[i]);
arrThreads[i].setDaemon(true);
arrThreads[i].start();
}
Thread.sleep(500);
pour (int i = 0; i < arrMyRunners.length; i++)
{
synchronisé(arrMyRunners[i])
{
arrMyRunners[i].notify();
}
}
}
private static void notifyTest3() lance InterruptedException
{
Coureur MyRunner = new MyRunner();
Thread[] arrThreads = nouveau Thread[3];
pour (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = nouveau Thread(coureur);
arrThreads[i].setDaemon(true);
arrThreads[i].start();
}
Thread.sleep(500);
synchronisé (coureur)
{
coureur.notifyAll();
}
}
}
la classe MyThread étend Thread
{
identifiant public int = 0 ;
exécution publique vide()
{
System.out.println("Thread " + id + " est prêt à dormir pendant 5 minutes.");
essayer
{
synchronisé(ce)
{
this.wait(5*60*1000);
}
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println("Le "th" + id + "thread a été réveillé.");
}
}
la classe MyRunner implémente Runnable
{
identifiant public int = 0 ;
exécution publique vide()
{
System.out.println("Thread " + id + " est prêt à dormir pendant 5 minutes.");
essayer
{
synchronisé(ce)
{
this.wait(5*60*1000);
}
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println("Le "th" + id + "thread a été réveillé.");
}
}
La méthode notifyAll convient au scénario « objet unique multi-thread », car la méthode notify ne réveillera que de manière aléatoire un thread sur l'objet.
Changement d'état du thread
Pour un thread, à partir du moment où nous le créons jusqu'à la fin du thread, l'état du thread pendant ce processus peut être comme ceci :
Création : il existe déjà une instance de Thread, mais le CPU dispose toujours de ressources et de tranches de temps qui lui sont allouées.
Prêt : le thread a obtenu toutes les ressources nécessaires à son exécution et attend juste que le processeur planifie l'heure.
En cours d'exécution : le thread se trouve dans la tranche de temps CPU actuelle et exécute la logique associée.
Sommeil : généralement l'état après l'appel de Thread.sleep. À ce stade, le thread contient toujours diverses ressources nécessaires au fonctionnement, mais ne sera pas planifié par le processeur.
Suspendre : généralement l'état après l'appel de Thread.suspend. Semblable à sleep, le CPU ne planifiera pas le thread. La différence est que dans cet état, le thread libérera toutes les ressources.
Mort : le thread se termine ou la méthode Thread.stop est appelée.
Nous allons ensuite montrer comment changer d'état de thread. Nous allons d'abord utiliser la méthode suivante :
Thread() ou Thread(Runnable) : construisez un thread.
Thread.start : démarrer un fil de discussion.
Thread.sleep : passez le thread en état de veille.
Thread.interrupt : Interrompt l’exécution du thread.
Thread.join : attendez la fin d’un fil de discussion.
Thread.yield : Priver le thread de sa tranche de temps d'exécution sur le CPU et attendre la prochaine planification.
Object.wait : verrouille tous les threads sur l'objet et ne continuera pas à s'exécuter jusqu'à la méthode de notification.
Object.notify : réveille aléatoirement un thread sur l'objet.
Object.notifyAll : réveille tous les threads sur Object.
Maintenant, c’est l’heure de la démonstration ! ! !
Discussion en attente et réveil
Les méthodes Object.wait et Object.notify sont principalement utilisées ici, veuillez consulter l'instance de notification ci-dessus. Il convient de noter que wait et notify doivent cibler le même objet. Lorsque nous créons un thread en implémentant l'interface Runnable, nous devons utiliser ces deux méthodes sur l'objet Runnable au lieu de l'objet Thread.
Sujet dormir et se réveiller
public static void main (String[] args) lance InterruptedException
{
sommeilTest();
}
private static void sleepTest() lance InterruptedException
{
Fil de discussion = nouveau fil de discussion()
{
exécution publique vide()
{
System.out.println("Thread" + Thread.currentThread().getName() + "Il dormira pendant 5 minutes.");
essayer
{
Thread.sleep(5*60*1000);
}
catch (InterruptedException ex)
{
System.out.println("Thread" + Thread.currentThread().getName() + "Le sommeil a été interrompu.");
}
System.out.println("Thread" + Thread.currentThread().getName() + "Veille terminée.");
}
} ;
thread.setDaemon(true);
thread.start();
Thread.sleep(500);
thread.interrupt();
}
}
Bien qu'il existe une méthode Thread.stop, cette méthode n'est pas recommandée. Nous pouvons utiliser le mécanisme de veille et de réveil ci-dessus pour laisser le thread terminer le thread lors du traitement d'IterruptedException.
public static void main (String[] args) lance InterruptedException
{
stopTest();
}
private static void stopTest() lance InterruptedException
{
Fil de discussion = nouveau fil de discussion()
{
exécution publique vide()
{
System.out.println("Le thread est en cours d'exécution.");
essayer
{
Thread.sleep(1*60*1000);
}
catch (InterruptedException ex)
{
System.out.println("Interruption de thread, fin de thread");
retour;
}
System.out.println("Le fil de discussion s'est terminé normalement.");
}
} ;
thread.start();
Thread.sleep(500);
thread.interrupt();
}
}
Lorsque nous créons 10 sous-threads dans le thread principal, et que nous nous attendons à ce qu'une fois les 10 sous-threads terminés, le thread principal exécute la logique suivante. À ce moment-là, il est temps pour Thread.join d'apparaître.
public static void main (String[] args) lance InterruptedException
{
joinTest();
}
private static void joinTest() lance InterruptedException
{
Fil de discussion = nouveau fil de discussion()
{
exécution publique vide()
{
essayer
{
pour(int je = 0; je < 5; je++)
{
System.out.println("Le thread est en cours d'exécution.");
Thread.sleep(1000);
}
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
} ;
thread.setDaemon(true);
thread.start();
Thread.sleep(1000);
thread.join();
System.out.println("Le thread principal s'est terminé normalement.");
}
}
Nous savons que tous les threads d'un processus partagent de l'espace mémoire, alors comment transférer des messages entre différents threads ? Lors de l'examen des E/S Java, nous avons parlé de PipedStream et PipedReader, et c'est ici qu'ils entrent en jeu.
Les deux exemples suivants ont exactement les mêmes fonctions. La différence est que l'un utilise Stream et l'autre Reader/Writer.
Fil de discussion1 = nouveau fil de discussion()
{
exécution publique vide()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
essayer
{
tandis que (vrai)
{
Message de chaîne = br.readLine();
pos.write(message.getBytes());
if (message.equals("end")) break;
}
br.close();
pos.close();
}
attraper (exception ex)
{
ex.printStackTrace();
}
}
} ;
Fil de discussion2 = nouveau fil de discussion()
{
exécution publique vide()
{
octet[] tampon = nouvel octet[1024];
int octetsRead = 0 ;
essayer
{
while((bytesRead = pis.read(buffer, 0, buffer.length)) != -1)
{
System.out.println(nouvelle chaîne(tampon));
if (new String(buffer).equals("end")) break;
tampon = nul ;
tampon = nouvel octet[1024] ;
}
pis.close();
tampon = nul ;
}
attraper (exception ex)
{
ex.printStackTrace();
}
}
} ;
thread1.setDaemon(true);
thread2.setDaemon(true);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
Fil de discussion1 = nouveau fil de discussion()
{
exécution publique vide()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
essayer
{
tandis que (vrai)
{
Message de chaîne = br.readLine();
bw.write(message);
bw.newLine();
bw.flush();
if (message.equals("end")) break;
}
br.close();
pw.close();
bw.close();
}
attraper (exception ex)
{
ex.printStackTrace();
}
}
} ;
Fil de discussion2 = nouveau fil de discussion()
{
exécution publique vide()
{
Ligne de chaîne = null ;
essayer
{
while((line = br.readLine()) != null)
{
System.out.println(ligne);
if (line.equals("end")) break;
}
br.close();
pr.close();
}
attraper (exception ex)
{
ex.printStackTrace();
}
}
} ;
thread1.setDaemon(true);
thread2.setDaemon(true);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}