Dans ce chapitre, nous présenterons la méthode d'attente / réveil du fil. Le contenu impliqué comprend:
1. Introduction à wait (), notify (), notifyall () et d'autres méthodes
2. wait () et notify ()
3. Attendez (temps de temps long) et notify ()
4. wait () et notifyall ()
5. Pourquoi notify (), wait () et d'autres fonctions définies dans l'objet, pas le thread
Introduction à wait (), notify (), notifyall () et d'autres méthodes
Dans object.java, les interfaces telles que attendre (), notify () et notifyall () sont définies. La fonction d'attente () est de permettre au thread actuel de saisir un état d'attente, et Wait () permettra également au thread actuel de libérer le verrouillage qu'il conserve. Le rôle de notify () et de notifyall () est de réveiller le thread d'attente sur l'objet actuel;
Les détails de l'API sur l'attente / le sillage dans la classe d'objets sont les suivants:
notify () - Réveillez un seul thread en attente de ce moniteur d'objet.
notifyall () - Réveillez tous les threads en attente de ce moniteur d'objet.
Wait () - Mettez le thread actuel dans un "état d'attente (blocage)" et "jusqu'à ce que d'autres threads appellent la méthode Notify () ou NOTIFYALL () de cet objet", et le thread actuel est éveillé (entré au " État prêt ").
attendre (temps de temps long) - laissez le thread actuel être dans un état "attendre (blocage)" et "jusqu'à ce que d'autres threads appellent la méthode notify () ou la méthode notifyall () de cet objet, ou dépasser la durée spécifiée", et le thread actuel est éveillé (entrez "Ready").
attendre (temps d'attente long, int nanos) - Mettez le thread actuel dans un état "attendre (blocage)" jusqu'à ce qu'un autre thread appelle la méthode notify () ou notifyall () de cet objet, ou un autre thread interrompt le thread actuel, ou la durée réelle a dépassé ", et le thread actuel est éveillé (entré à" État prêt ").
2. Wait () et notify () Exemples
Ce qui suit est un exemple pour démontrer "wait () et notify () ensemble".
La copie de code est la suivante:
// Code source WaitTest.Java
classe threada étend Thread {
public threada (nom de chaîne) {
super (nom);
}
public void run () {
synchronisé (this) {
System.out.println (thread.currentThread (). GetName () + "call notify ()");
// Réveille le fil d'attente actuel
notify ();
}
}
}
classe publique WaitTest {
public static void main (String [] args) {
Threada t1 = new Threada ("t1");
synchronisé (t1) {
essayer {
// Démarrer "Thread T1"
System.out.println (thread.currentThread (). GetName () + "start t1");
t1.start ();
// Le fil principal attend que T1 se réveille via notify ().
System.out.println (thread.currentThread (). GetName () + "wait ()");
t1.Wait ();
System.out.println (thread.currentThread (). GetName () + "Continuer");
} catch (InterruptedException e) {
e.printStackTrace ();
}
}
}
}
Résultats en cours:
La copie de code est la suivante:
Démarrage principal T1
attend principal ()
T1 appel notify ()
Principal continue
Description des résultats:
La figure suivante illustre le flux de "Thread principal" et "Thread T1".
(01) Notez que le "thread principal" sur la figure représente "le fil principal principal". "Thread T1" Représente "Thread T1" démarré dans Waitstest. Et "Lock" représente "un verrou synchrone de l'objet T1".
(02) "Le thread principal" crée un nouveau "thread T1" via un nouveau threada ("T1"). Ensuite, le "verrouillage synchrone de l'objet T1" est obtenu par synchronisé (T1). Ensuite, appelez t1.start () pour démarrer "Thread T1".
(03) "Le thread principal" exécute T1.Wait () pour libérer "Lock de l'objet T1" et entre dans l'état "Wait (Blocking)". Attendez que les threads sur les objets T1 le réveillent via notify () ou notifyall ().
(04) Une fois que "Thread T1" est exécuté, le "verrouillage de l'objet actuel" est obtenu via Synchronisé (ceci); le "fil principal".
(05) Une fois "Thread T1" terminé, relâchez le "verrouillage de l'objet actuel". Immédiatement après, le "Thread principal" acquiert le "verrouillage de l'objet T1" puis s'exécute.
Pour le code ci-dessus? Un ami a demandé une fois: T1.Wait () devrait faire du "fil T1", mais pourquoi le thread principal principal "attend-il?
Avant de répondre à cette question, jetons un coup d'œil à un paragraphe sur l'attente dans le document JDK:
La copie de code est la suivante:
Fait attendre le thread actuel qu'un autre thread invoque la méthode Notify () ou la méthode notifyall () pour cet objet.
En d'autres termes, cette méthode se comporte exactement comme si elle effectue simplement l'appel d'appel (0).
Le thread actuel doit posséder le moniteur de cet objet. peut réobjecter la propriété du moniteur et reprendre l'exécution.
Le sens en chinois est grossièrement:
Fait attendre le "thread actuel" qu'un autre thread appelle Notify () ou notifyall () pour réveiller le thread. En d'autres termes, cette méthode a le même effet que Wait (0)! (Supplémentaire, pour la méthode d'attente (Long Millis), lorsque Millis est 0, cela signifie infini d'attente jusqu'à ce qu'il soit réveillé par notify () ou notifyall ()).
Lorsque le "thread actuel" appelle Wait (), il doit avoir le verrouillage de synchronisation pour l'objet. Une fois que le thread appelle Wait (), le verrou sera libéré; puis il attendra que les "autres threads" appellent la méthode NOTIFY () ou NOTIFYALL () de l'objet. Le fil continue ensuite d'attendre qu'il réacquiert la "verrouillage de synchronisation pour cet objet", puis il peut continuer à s'exécuter.
Remarque: Dans l'explication de JDK, il est dit que la fonction d'attente () est de faire attendre le "thread actuel", et le "thread actuel" fait référence au thread fonctionnant sur le CPU!
Cela signifie également que bien que T1.Wait () soit la méthode Wait () appelée via "Thread T1", l'endroit où T1.Wait () est appelé est dans "Main Thread Main". Le thread principal doit être le "thread actuel", c'est-à-dire l'état en cours d'exécution, avant que T1.Wait () ne puisse être exécuté. Par conséquent, le "thread actuel" à l'heure actuelle est le "Main Thread Main"! Par conséquent, T1.Wait () est de faire attendre le "fil principal", pas "Thread T1"!
3. Attendez (temps de temps long) et notify ()
Wait (Long Timeout) mettra le thread actuel dans un état "d'attente (blocage)" et "jusqu'à ce que d'autres threads appellent la méthode notify () ou la méthode notifyall () de cet objet, ou dépasse la durée spécifiée" et la Le thread actuel est éveillé (entré) "prêt").
L'exemple suivant démontre le délai d'attente (temps d'attente long) et le fil est éveillé.
La copie de code est la suivante:
// Code source de WaittimeoutTest.java
classe threada étend Thread {
public threada (nom de chaîne) {
super (nom);
}
public void run () {
System.out.println (thread.currentThread (). GetName () + "run");
// un cercle vicieux, fonctionnant en continu.
Tandis que (vrai)
}
}
classe publique Wait TimeOutTest {
public static void main (String [] args) {
Threada t1 = new Threada ("t1");
synchronisé (t1) {
essayer {
// Démarrer "Thread T1"
System.out.println (thread.currentThread (). GetName () + "start t1");
t1.start ();
// Le fil principal attend que T1 se réveille via notify () ou notifyall (), ou des retards dépassant 3000 ms;
System.out.println (thread.currentThread (). GetName () + "Appel Wait");
t1.Atthe (3000);
System.out.println (thread.currentThread (). GetName () + "Continuer");
} catch (InterruptedException e) {
e.printStackTrace ();
}
}
}
}
Résultats en cours:
La copie de code est la suivante:
Démarrage principal T1
Appel principal attendre
T1 Run // Après environ 3 secondes ... Sortie "Main continu"
Principal continue
Description des résultats:
La figure suivante illustre le flux de "Thread principal" et "Thread T1".
(01) Notez que le "thread principal" de la figure représente le thread principal du temps de serve (c'est-à-dire le fil principal). "Thread T1" représente le thread T1 démarré dans Waitest. Et "Lock" représente "un verrou synchrone de l'objet T1".
(02) Le thread principal principal exécute t1.start () pour démarrer "Thread T1".
(03) Le thread principal principal exécute T1.Atthe (3000), et à ce moment, le thread principal entre dans "l'état de blocage". Il est nécessaire de "le thread utilisé pour le verrouillage d'objet T1 pour le réveiller via notify () ou notifyall ()" ou "après 3000 ms délai d'expiration", le thread principal principal entre dans "l'état prêt", puis il peut s'exécuter.
(04) Une fois "Thread T1" en cours d'exécution, il entre dans une boucle morte et continue de fonctionner.
(05) Après le délai d'expiration de 3000 ms, le thread principal principal entrera dans "l'état prêt" puis entrera dans "l'état de course".
4. wait () et notifyall ()
Grâce à l'exemple précédent, nous savons que Notify () peut réveiller un seul thread en attente de ce moniteur d'objet.
Ci-dessous, nous démontons l'utilisation de Notifyall () à travers un exemple;
La copie de code est la suivante:
classe publique NotifyAllTest {
objet statique privé obj = nouveau objet ();
public static void main (String [] args) {
Threada t1 = new Threada ("t1");
Threada t2 = new Threada ("T2");
Threada t3 = new Threada ("T3");
t1.start ();
t2.start ();
t3.start ();
essayer {
System.out.println (thread.currentThread (). GetName () + "Sleep (3000)");
Thread.Sleep (3000);
} catch (InterruptedException e) {
e.printStackTrace ();
}
synchronisé (obj) {
// Le fil principal attend le réveil.
System.out.println (thread.currentThread (). GetName () + "notifyall ()");
obj.notifyall ();
}
}
classe statique Threada étend Thread {
public threada (nom de chaîne) {
super (nom);
}
public void run () {
synchronisé (obj) {
essayer {
// Imprimer le résultat de la sortie
System.out.println (thread.currentThread (). GetName () + "Wait");
// Réveille le fil d'attente actuel
obj.wait ();
// Imprimer le résultat de la sortie
System.out.println (thread.currentThread (). GetName () + "Continuer");
} catch (InterruptedException e) {
e.printStackTrace ();
}
}
}
}
}
Résultats en cours:
La copie de code est la suivante:
t1 attendre
sommeil principal (3000)
t3 attendre
t2 attendre
principale notifyall ()
T2 continue
T3 Continuer
T1 Continuer
Description des résultats:
Reportez-vous à l'organigramme ci-dessous.
(01) 3 Threads "T1", "T2" et "T3" ont été créés et démarrés dans le thread principal.
(02) Le fil principal dort pendant 3 secondes par le sommeil (3000). Pendant le sommeil principal pendant 3 secondes, nous supposons que les trois fils "T1", "T2" et "T3" fonctionnent tous. Prenez "T1" comme exemple. Attendra également que d'autres fils les réveillent via nofity () ou nofityall ().
(03) Le fil principal dort pendant 3 secondes puis fonctionne. Exécutez obj.notifyall () pour réveiller le fil d'attente sur OBJ, c'est-à-dire réveiller les trois threads "T1", "T2" et "T3". Immédiatement après l'exécution du fil de filetage principal (OBJ), le thread principal libère le "verrouillage OBJ". De cette façon, "T1", "T2" et "T3" peuvent obtenir le "verrouillage Obj" et continuer à fonctionner!
5. Pourquoi notify (), wait () et d'autres fonctions définies dans l'objet, pas le thread
Des fonctions telles que Wait (), Notify () dans l'objet, comme synchronisé, fonctionneront sur "Lock de synchronisation d'objet".
Wait () fera attendre le "thread actuel". Parce que le thread entre dans l'état d'attente, le thread doit libérer le "serrure synchrone" maintenue par sa serrure, sinon d'autres threads ne pourront pas obtenir le "verrou synchrone" et Will ne pas pouvoir courir!
Ok, après que le thread appelle Wait (), il libérera le "verrouillage synchrone" maintenu par sa serrure; et, selon l'introduction précédente, nous savons que le fil d'attente peut être éveillé par notify () ou notifyall (). Maintenant, pensez à une question: qu'est-ce que notify () basé sur l'éveil du fil d'attente? Ou, quelle est la corrélation entre attendre () et notify ()? La réponse est: basée sur "Lock de synchronisation des objets".
Le fil responsable du réveil du thread d'attente (nous l'appelons "Wake Up Thread"), il n'acquiert que le "verrouillage de synchronisation de l'objet" (le verrou de synchronisation ici doit être le même que le verrou de synchronisation du fil d'attente), et appels notify () ou après la méthode notifyall (), le thread d'attente peut être éveillé. Cependant, le fil d'attente est éveillé; Vous devez attendre que le fil de réveil libère le "verrouillage de synchronisation de l'objet" avant de pouvoir obtenir le "verrou de synchronisation de l'objet" et continuer à s'exécuter.
En bref, Notify (), Wait () s'appuie sur "Synchronous Lock", qui est maintenu par les verrous d'objet, et chaque objet en a et un seul! C'est pourquoi les fonctions telles que Notify (), Wait () sont définies dans la classe d'objets, pas dans la classe de threads.