t
t
t
t
t t   14) Les Threads multiples
tttt
t
carrea) Préface carreb) Avant-propos carre1) Introduction sur les &laqo; objets » carre2) Tout est &laqo; objet » carre3) Contrôle du flux du programme carre4) Initialization & Cleanup carre5) Cacher l'implémentation carre6) Réutiliser les classes carre7) Polymorphisme carre8) Interfaces & classes internes carre9) Stockage des objets carre10) Error Handling with Exceptions carre11) Le système d’E/S de Java carre12) Identification dynamique de type carre13) Création de fenêtres & d'Applets 14) Les &laqo; Threads » multiples carre15) Informatique distribuée carreA) Passage et retour d'objets carreB) L'Interface Java Natif (JNI) carreC) Conseils pour une programation stylée en Java carreD) Resources
Texte original t Traducteur : Cédric Babault
t
t
///
Ce chapitre contient 9 pages
1 2 3 4 5 6 7 8 9
\\\
t t t
t t t
t
t t t
You can see that the method notifyListeners( ) is not synchronized. It can be called from more than one thread at a time. It’s also possible for addActionListener( ) or removeActionListener( ) to be called in the middle of a call to notifyListeners( ), which is a problem since it traverses the ArrayList actionListeners. To alleviate the problem, the ArrayList is cloned inside a synchronized clause and the clone is traversed (see Appendix A for details of cloning). This way the original ArrayList can be manipulated without impact on notifyListeners( ).
t Vous pouvez voir que la méthode notifyListeners() n'est pas synchronized. Elle peut être appelé depuis plus d'un thread à la fois. Il est aussi possible pour addActionListener() ou removeActionListener() d'être appelé dans le milieu d'un appel de notifyListeners(), ce qui est un problème puisqu'il traverse l'ArrayList actionListeners. Pour réduire le problème, l'ArrayList est clonée dans une clause synchronized et le clone est traversé (voir l'appendice A pour les détails sur le clonage). De cette façon l'ArrayList originale peut être manipulée sans impact sur notifyListeners().
t t t
The paintComponent( ) method is also not synchronized. Deciding whether to synchronize overridden methods is not as clear as when you’re just adding your own methods. In this example it turns out that paint( ) seems to work OK whether it’s synchronized or not. But the issues you must consider are:
t La méthode paintComponent() n'est pas non plus synchronized. Décider de synchroniser les méthodes surchargées n'est pas aussi clair que quand vous ajoutez vos propres méthodes. Dans cet exemple, il semblerait que paint() fonctionne correctement qu'il soit synchronized ou non. Mais les points que vous devez considérés sont:
t t t
  1. Does the method modify the state of “critical” variables within the object? To discover whether the variables are “critical” you must determine whether they will be read or set by other threads in the program. (In this case, the reading or setting is virtually always accomplished via synchronized methods, so you can just examine those.) In the case of paint( ), no modification takes place.
  2. Does the method depend on the state of these “critical” variables? If a synchronized method modifies a variable that your method uses, then you might very well want to make your method synchronized as well. Based on this, you might observe that cSize is changed by synchronized methods and therefore paint( ) should be synchronized. Here, however, you can ask “What’s the worst thing that will happen if cSize is changed during a paint( )?” When you see that it’s nothing too bad, and a transient effect at that, you can decide to leave paint( ) un-synchronized to prevent the extra overhead from the synchronized method call.
  3. A third clue is to notice whether the base-class version of paint( ) is synchronized, which it isn’t. This isn’t an airtight argument, just a clue. In this case, for example, a field that is changed via synchronized methods (that is cSize) has been mixed into the paint( ) formula and might have changed the situation. Notice, however, that synchronized doesn’t inherit—that is, if a method is synchronized in the base class then it is not automatically synchronized in the derived class overridden version.
t
  1. Cette méthode modifie-t-elle l'état des variables « critiques » de l'objet. Pour découvrir si les variables sont « critiques » vous devez déterminer si elles seront lues ou modifiées par d'autres threads dans le programme. (Dans ce cas, la lecture ou la modification sont pratiquement toujours réalisées par des méthodes synchronized, ainsi vous pouvez examiner juste celle-là.) Dans le cas de paint(), aucune modification n'apparaît.
  2. Cette méthode dépend-elle de l'état de ces variables « critiques »? Si une méthode synchronized modifie une variable que votre méthode utilise, alors vous devriez vouloir mettre aussi votre méthode synchronized. En vous basant la dessus, vous devriez observer que cSize est changé par des méthodes synchronized et en conséquence paint() devrait être synchronized. Ici, pourtant, vous pouvez vous demander « Quelle serait la pire chose qui arriverai si cSize changeait durant paint()? » Quand vous voyez que ce n'est rien de trop grave, et un effet transitoire en plus, vous pouvez décider de laisser paint() un-synchronized pour éviter le temps supplémentaire de l'appel de méthode synchronized.
  3. Un troisième indice est de noter si la version de la classe de base de paint() est synchronized, ce qui n'est pas le cas. Ce n'est pas un argument hermétique, juste un indice. Dans ce cas, par exemple, un champ qui est changé via des méthodes synchronizes (il s'agit de cSize) a été mélangé dans la formule de paint() et devrait avoir changé la situation. Notez, cependant, que synchronized n'est pas hérité —  c'est à dire que si une méthode est synchronized dans une classe de base, alors il n'est pas automatiquement synchronized dans la version redéfinie de la classe dérivée .
t t t
The test code in TestBangBean2 has been modified from that in the previous chapter to demonstrate the multicast ability of BangBean2 by adding extra listeners.
t Le code de test dans TestBangBean2 a été modifié depuis celui du chapitre précédent pour démontrer la possibilité de multicast de BangBean2 en ajoutant des listeners supplémentaires.
t t t

Blocking

t

Blocage [Blocking]

t t t
A thread can be in any one of four states:
t Un thread peut être dans un des quatre états suivants:
t t t
  1. New: The thread object has been created but it hasn’t been started yet so it cannot run.
  2. Runnable: This means that a thread can be run when the time-slicing mechanism has CPU cycles available for the thread. Thus, the thread might or might not be running, but there’s nothing to prevent it from being run if the scheduler can arrange it; it’s not dead or blocked.
  3. Dead: The normal way for a thread to die is by returning from its run( ) method. You can also call stop( ), but this throws an exception that’s a subclass of Error (which means you aren’t forced to put the call in a try block). Remember that throwing an exception should be a special event and not part of normal program execution; thus the use of stop( ) is deprecated in Java 2. There’s also a destroy( ) method (which has never been implemented) that you should never call if you can avoid it since it’s drastic and doesn’t release object locks.
  4. Blocked: The thread could be run but there’s something that prevents it. While a thread is in the blocked state the scheduler will simply skip over it and not give it any CPU time. Until a thread reenters the runnable state it won’t perform any operations.
t
  1. Nouveau (New): L'objet thread a été créé mais il n'a pas encore été démarré donc il ne peut pas tourné.
  2. Runnable: Cela signifie qu'un thread peut tourner quand le mécanisme de découpage du temps a des cycles CPU disponibles pour le thread. Ainsi, le thread pourra ou non tourner, mais il n'y a rien pour l'empêcher de tourner si le scheduler peut l'organiser; il n'est ni mort ni bloqué.
  3. Mort (Dead): La façon normale pour un thread de mourir est de terminer sa méthode run(). Vous pouvez également appelé stop(), mais cela déclenche une exception qui est une sous-classe de Error (ce qui signifie que vous n'êtes pas obligé de placer l'appel dans un bloc try). Souvenez vous que déclencher une exception devrait être un événement spécial et non une partie de l'exécution normale du programme; ainsi l'utilisation de stop() est déprécier dans Java 2. Il y a aussi une méthode destroy() (qui n'a jamais été implémentée) qui vous ne devriez jamais utiliser si vous pouvez l'éviter puisqu'elle est radicale et ne libère pas les verrous de l'objet.
  4. Bloqué (Blocked): Le thread pourrait tourner mais quelque chose l'en empêche. Tant qu'un thread est dans un état bloqué le scheduler le sautera et ne lui donnera pas de temps CPU. Jusqu'à ce que le thread repasse dans un état runnable il ne réalisera aucune opérations.
t t t

Becoming blocked

t

Passer à l'état bloqué

t t t
The blocked state is the most interesting one, and is worth further examination. A thread can become blocked for five reasons:
t L'état bloqué est le plus intéressant, et nécessite un examen. Un thread peut devenir bloqué pour cinq raisons:
t t t
  1. You’ve put the thread to sleep by calling sleep(milliseconds), in which case it will not be run for the specified time.
  2. You’ve suspended the execution of the thread with suspend( ). It will not become runnable again until the thread gets the resume( ) message (these are deprecated in Java 2, and will be examined further).
  3. You’ve suspended the execution of the thread with wait( ). It will not become runnable again until the thread gets the notify( ) or notifyAll( ) message. (Yes, this looks just like number 2, but there’s a distinct difference that will be revealed.)
  4. The thread is waiting for some I/O to complete.
  5. The thread is trying to call a synchronized method on another object, and that object’s lock is not available.
t
  1. Vous avez mis le thread à dormir en appelant sleep(milliseconds), auquel cas il ne tournera pas pendant le temps spécifé.
  2. Vous avez suspendu l'exécution du thread avec suspend(). Il ne redeviendra pas runnable avant que le thread ne reçoive le message resume() (ces possibilités sont dépréciés dans Java2, et seront examinés plus tard).
  3. Vous avez suspendu l'exécution du thread avec wait(). Il ne redeviendra pas runnable tant qu'il n'aura pas reçu l'un des messages notify() ou notifyAll(). (Oui, c'est comme le point 2, mais il y a une distinction qui sera vu plus tard.)
  4. Le thread attend la fin d'une I/O.
  5. Le thread essaye d'appelé une méthode synchronized sur un autre objet, et le verrou de cet objet n'est pas libre.
t t t
You can also call yield( ) (a method of the Thread class) to voluntarily give up the CPU so that other threads can run. However, the same thing happens if the scheduler decides that your thread has had enough time and jumps to another thread. That is, nothing prevents the scheduler from moving your thread and giving time to some other thread. When a thread is blocked, there’s some reason that it cannot continue running.
t Vous pouvez aussi appelé yield() (une méthode de la classe Thread) pour volontairement donner le CPU afin que d'autres threads puisse tournées. Toutefois, il se passe la même chose si le scheduler décide que votre thread a eu assez de temps et saute à un autre thread. En fait, rien n'empêche le scheduler de partir de votre thread et de donner du temps à un autre thread. Quand un thread est bloqué, c'est qu'il y a une raison pour qu'il ne puisse continuer à tourner.
t t t
The following example shows all five ways of becoming blocked. It all exists in a single file called Blocking.java, but it will be examined here in discrete pieces. (You’ll notice the “Continued” and “Continuing” tags that allow the code extraction tool to piece everything together.)
t L'exemple suivant montre les cinq façons de devenir bloqué. Il existe en intégralité dans un seul fichier appelé Blocking.java, mais il sera examiné ici par morceau. (Vous remarquerez les tags « Continued » et « Continuing » qui permette à l'outil d'extraction de recoller les morceaux.)
t t t
Because this example demonstrates some deprecated methods, you will get deprecation messages when it is compiled.
t Étant donné que cet exemple montre des méthodes dépréciées, vous obtiendrez des messages de depreciation lors de la compilation.
t t t
First, the basic framework:
t D'abord le programme de base:
t t t
//: c14:Blocking.java // Demonstrates the various ways a thread // can be blocked. // <applet code=Blocking width=350 height=550> // </applet> import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; import com.bruceeckel.swing.*; //////////// The basic framework /////////// class Blockable extends Thread { private Peeker peeker; protected JTextField state = new JTextField(30); protected int i; public Blockable(Container c) { c.add(state); peeker = new Peeker(this, c); } public synchronized int read() { return i; } protected synchronized void update() { state.setText(getClass().getName() + " state: i = " + i); } public void stopPeeker() { // peeker.stop(); Deprecated in Java 1.2 peeker.terminate(); // The preferred approach } } class Peeker extends Thread { private Blockable b; private int session; private JTextField status = new JTextField(30); private boolean stop = false; public Peeker(Blockable b, Container c) { c.add(status); this.b = b; start(); } public void terminate() { stop = true; } public void run() { while (!stop) { status.setText(b.getClass().getName() + " Peeker " + (++session) + "; value = " + b.read()); try { sleep(100); } catch(InterruptedException e) { System.err.println("Interrupted"); } } } } ///:Continued t
//: c14:Blocking.java
// Démontre les différentes façon de
// bloqué un thread.
// <applet code=Blocking width=350 height=550>
// </applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import com.bruceeckel.swing.*;

//////////// Le framework de base ///////////
class Blockable extends Thread {
  private Peeker peeker;
  protected JTextField state = new JTextField(30);
  protected int i;
  public Blockable(Container c) {
    c.add(state);
    peeker = new Peeker(this, c);
  }
  public synchronized int read() { return i; }
  protected synchronized void update() {
    state.setText(getClass().getName()
      + " state: i = " + i);
  }
  public void stopPeeker() {
    // peeker.stop(); Déprécié en Java 1.2
    peeker.terminate(); // L'approche préférée
  }
}

class Peeker extends Thread {
  private Blockable b;
  private int session;
  private JTextField status = new JTextField(30);
  private boolean stop = false;
  public Peeker(Blockable b, Container c) {
    c.add(status);
    this.b = b;
    start();
  }
  public void terminate() { stop = color="#0000ff">true; }
  public void run() {
    while (!stop) {
      status.setText(b.getClass().getName()
        + " Peeker " + (++session)
        + "; value = " + b.read());
       try {
        sleep(100);
      } catch(InterruptedException e) {
        System.err.println("Interrupted");
      }
    }
  }
} ///:Continued
t t t
The Blockable class is meant to be a base class for all the classes in this example that demonstrate blocking. A Blockable object contains a JTextField called state that is used to display information about the object. The method that displays this information is update( ). You can see it uses getClass( ).getName( ) to produce the name of the class instead of just printing it out; this is because update( ) cannot know the exact name of the class it is called for, since it will be a class derived from Blockable.
t La classe Blockable est destinée à être la classe de base pour toutes les classes de cet exemple qui démontre le blocking. Un objet Blockable contient un JTextField appelé state qui est utilisé pour afficher l'information sur l'objet. La méthode qui affiche cette information est update(). Vous pouvez voir qu'elle utilise getClass().getName() pour produire le nom de la classe plutôt que de l'afficher directement; c'est parce que update() ne peut pas connaître le nom exact de la classe pour laquelle elle est appelée, puisque ce sera une classe dérivée de Blockable.
t t t
The indicator of change in Blockable is an int i, which will be incremented by the run( ) method of the derived class.
t L'indicateur de changement dans Blockable est un int i, qui sera incrémenter par la méthode run() de la classe dérivé.
t t t
There’s a thread of class Peeker that is started for each Blockable object, and the Peeker’s job is to watch its associated Blockable object to see changes in i by calling read( ) and reporting them in its status JTextField. This is important: Note that read( ) and update( ) are both synchronized, which means they require that the object lock be free.
t Il y a un thread de la classe Peeker qui est démarré pour chaque objet Blockable, et le travail de Peeker est de regarder son objet Blockable associé pour voir les changements de i en appelant read() et en les reportant dans son status JTextField. C'est important: Notez que read() et update() sont toutes les deux synchronized, ce qui signifie qu'elles nécessite que le verrou de l'objet soit libre.
t t t

Sleeping

t

Dormant (Sleeping)

t t t
The first test in this program is with sleep( ):
t Le premier test de ce programme est avec sleep():
t t t
///:Continuing ///////////// Blocking via sleep() /////////// class Sleeper1 extends Blockable { public Sleeper1(Container c) { super(c); } public synchronized void run() { while(true) { i++; update(); try { sleep(1000); } catch(InterruptedException e) { System.err.println("Interrupted"); } } } } class Sleeper2 extends Blockable { public Sleeper2(Container c) { super(c); } public void run() { while(true) { change(); try { sleep(1000); } catch(InterruptedException e) { System.err.println("Interrupted"); } } } public synchronized void change() { i++; update(); } } ///:Continued t
///:Continuing
///////////// Bloqué via sleep() ///////////
class Sleeper1 extends Blockable {
  public Sleeper1(Container c) { super(c); }
  public synchronized void run() {
    while(true) {
      i++;
      update();
       try {
        sleep(1000);
      } catch(InterruptedException e) {
        System.err.println("Interrupted");
      }
    }
  }
}
  
class Sleeper2 extends Blockable {
  public Sleeper2(Container c) { super(c); }
  public void run() {
    while(true) {
      change();
       try {
        sleep(1000);
      } catch(InterruptedException e) {
        System.err.println("Interrupted");
      }
    }
  }
  public synchronized void change() {
      i++;
      update();
  }
} ///:Continued
t t t
In Sleeper1 the entire run( ) method is synchronized. You’ll see that the Peeker associated with this object will run along merrily until you start the thread, and then the Peeker stops cold. This is one form of blocking: since Sleeper1.run( ) is synchronized, and once the thread starts it’s always inside run( ), the method never gives up the object lock and the Peeker is blocked.
t Dans Sleeper1 la méthode run() est entièrement synchronized. Vous verrez que le Peeker associé avec cet objet tournera tranquillement jusqu'à vous démarriez le thread, ensuite le Peeker sera gelé. C'est une forme de blocage: puisque Sleeper1.run() est synchronized, et une fois que le thread démarre dans run(), la méthode ne redonne jamais le verrou de l'objet et le Peeker est bloqué.
t t t
Sleeper2 provides a solution by making run( ) un-synchronized. Only the change( ) method is synchronized, which means that while run( ) is in sleep( ), the Peeker can access the synchronized method it needs, namely read( ). Here you’ll see that the Peeker continues running when you start the Sleeper2 thread.
t Sleeper2 fournit une solution en rendant run() un-synchronized. Seule la méthode change() est synchronized, ce qui signifie que tant que run() est dans sleep(), le Peeker peut accéder à la méthode synchronized dont il a besoin, nommée read(). Ici vous verrez que Peeker continue à tourner quand vous démarrez le thread Sleeper2.
t t t

Suspending and resuming

t

Suspension et reprise

t t t
The next part of the example introduces the concept of suspension. The Thread class has a method suspend( ) to temporarily stop the thread and resume( ) that restarts it at the point it was halted. resume( ) must be called by some thread outside the suspended one, and in this case there’s a separate class called Resumer that does just that. Each of the classes demonstrating suspend/resume has an associated resumer:
t La partie suivante de l'exemple introduit le concept de suspension. La classe Thread a une méthode suspend() pour arrêter temporairement le thread et resume() qui le redémarre au point ou il était arrêté. resume() doit être appelé par un thread extérieur à celui suspendu, et dans ce cas il y a une classe séparée appelé Resumer qui fait juste ça. Chacune des classes démontrant suspend/resume a un resumer associé:
t t t
///:Continuing /////////// Blocking via suspend() /////////// class SuspendResume extends Blockable { public SuspendResume(Container c) { super(c); new Resumer(this); } } class SuspendResume1 extends SuspendResume { public SuspendResume1(Container c) { super(c);} public synchronized void run() { while(true) { i++; update(); suspend(); // Deprecated in Java 1.2 } } } class SuspendResume2 extends SuspendResume { public SuspendResume2(Container c) { super(c);} public void run() { while(true) { change(); suspend(); // Deprecated in Java 1.2 } } public synchronized void change() { i++; update(); } } class Resumer extends Thread { private SuspendResume sr; public Resumer(SuspendResume sr) { this.sr = sr; start(); } public void run() { while(true) { try { sleep(1000); } catch(InterruptedException e) { System.err.println("Interrupted"); } sr.resume(); // Deprecated in Java 1.2 } } } ///:Continued t
///:Continuing
/////////// Bloqué via suspend() ///////////
class SuspendResume extends Blockable {
  public SuspendResume(Container c) {
    super(c);    
    new Resumer(this);
  }
}

class SuspendResume1 extends SuspendResume {
  public SuspendResume1(Container c) { super(c);}
  public synchronized void run() {
    while(true) {
      i++;
      update();
      suspend(); // Déprecié en Java 1.2
    }
  }
}

class SuspendResume2 extends SuspendResume {
  public SuspendResume2(Container c) { super(c);}
  public void run() {
    while(true) {
      change();
      suspend(); // Déprecié en Java 1.2
    }
  }
  public synchronized void change() {
      i++;
      update();
  }
}

class Resumer extends Thread {
  private SuspendResume sr;
  public Resumer(SuspendResume sr) {
    this.sr = sr;
    start();
  }
  public void run() {
    while(true) {
       try {
        sleep(1000);
      } catch(InterruptedException e) {
        System.err.println("Interrupted");
      }
      sr.resume(); // Déprecié en Java 1.2
    }
  }
} ///:Continued
t t t
SuspendResume1 also has a synchronized run( ) method. Again, when you start this thread you’ll see that its associated Peeker gets blocked waiting for the lock to become available, which never happens. This is fixed as before in SuspendResume2, which does not synchronize the entire run( ) method but instead uses a separate synchronized change( ) method.
t SuspendResume1 a aussi une méthode synchronized run(). Une nouvelle fois, lorsque vous démarrerez ce thread vous verrez que son Peeker associé se bloque en attendant que le verrou devienne disponible, ce qui n'arrive jamais. Ce problème est corrigé comme précédemment dans SuspendResume2, qui ne place pas la méthode run() entièrement synchronize mais utilise une méthode synchronized change() séparée.
t t t
You should be aware that Java 2 deprecates the use of suspend( ) and resume( ), because suspend( ) holds the object’s lock and is thus deadlock-prone. That is, you can easily get a number of locked objects waiting on each other, and this will cause your program to freeze. Although you might see them used in older programs you should not use suspend( ) and resume( ). The proper solution is described later in this chapter.
t Vous devez être au courant du fait que Java 2 déprécie l'utilisation de suspend() et resume(), parce que suspend() prend le verrou et est sujet aux deadlock. En fait, vous pouvez facilement avoir un certain nombre d'objets verrouillés s'attendant les uns les autres, et cela entraînera le gel du programme. Alors que vous pouvez voir leurs utilisations dans des programmes plus vieux, vous ne devriez pas utiliser suspend() et resume(). La solution adéquate est décrite plus tard dans ce chapitre.
t t t

Wait and notify

t

Attendre et notifier

t t t
In the first two examples, it’s important to understand that both sleep( ) and suspend( ) do not release the lock as they are called. You must be aware of this when working with locks. On the other hand, the method wait( ) does release the lock when it is called, which means that other synchronized methods in the thread object could be called during a wait( ). In the following two classes, you’ll see that the run( ) method is fully synchronized in both cases, however, the Peeker still has full access to the synchronized methods during a wait( ). This is because wait( ) releases the lock on the object as it suspends the method it’s called within.
t Dans les deux premiers exemples, il est important de comprendre que ni sleep() ni suspend() ne libère le verrou lorsqu'ils sont appelés. Vous devez faire attention à cela en travaillant avec les verrous. D'un autre coté, la méthode wait() libère le verrou quand elle est appelée, ce qui signifie que les autres méthodes synchronized de l'objet thread peuvent être appelée pendant un wait(). Dans les deux classes suivantes, vous verrez que la méthode run() est totalement synchronized dans les deux cas, toutefois, le Peeker a encore un accès complet aux méthodes synchronized pendant un wait(). C'est parce que wait() libère le verrou sur l'objet quand il suspend la méthode dans laquelle il a été appelé.
t t t
You’ll also see that there are two forms of wait( ). The first takes an argument in milliseconds that has the same meaning as in sleep( ): pause for this period of time. The difference is that in wait( ), the object lock is released and you can come out of the wait( ) because of a notify( ) as well as having the clock run out.
t Vous verrez également qu'il y a deux formes de wait(). La première prend un argument en milli-secondes qui a la même signification que dans sleep(): pause pour cette période de temps. La différence est que dans wait(), le verrou de l'objet est libéré et vous pouvez sortir du wait() à cause d'un notify() aussi bien que parce que le temps est écoulé.
t t t
The second form takes no arguments, and means that the wait( ) will continue until a notify( ) comes along and will not automatically terminate after a time.
t La seconde forme ne prend pas d'arguments, et signifie que le wait() continuera jusqu'à ce qu'un notify() arrive et ne sera pas automatiquement terminé après un temps donné.
t t t
One fairly unique aspect of wait( ) and notify( ) is that both methods are part of the base class Object and not part of Thread as are sleep( ), suspend( ), and resume( ). Although this seems a bit strange at first—to have something that’s exclusively for threading as part of the universal base class—it’s essential because they manipulate the lock that’s also part of every object. As a result, you can put a wait( ) inside any synchronized method, regardless of whether there’s any threading going on inside that particular class. In fact, the only place you can call wait( ) is within a synchronized method or block. If you call wait( ) or notify( ) within a method that’s not synchronized, the program will compile, but when you run it you’ll get an IllegalMonitorStateException with the somewhat nonintuitive message “current thread not owner.” Note that sleep( ), suspend( ), and resume( ) can all be called within non-synchronized methods since they don’t manipulate the lock.
t Le seul point commun entre wait() et notify() est que ce sont toutes les deux des méthodes de la classe de base Object et non une partie de Thread comme le sont sleep(), suspend(), et resume(). Alors que cela parait un peu étrange au début — avoir quelque chose exclusivement pour le threading comme partie de la classe de base universelle — c'est essentiel puisqu'elles manipulent le verrou qui fait aussi partie de chaque objet. Le résultat est que vous pouvez mettre un wait() dans n'importe quelle méthode synchronized, du fait qu'il y a du threading intervenant dans cette classe particulière. En fait, la seule place où vous pouvez appeler wait() ou notify() est dans une méthode ou un bloc synchronized. Si vous appelez wait() ou notify() dans une méthode qui n'est pas synchronized, le programme se compilera, mais quand vous l'exécuterez vous obtiendrez une IllegalMonitorStateException avec le message peu intuitif « current thread not owner. » Notez que sleep(), suspend(), et resume() peuvent toutes être appelées dans des méthodes non-synchronized puisqu'elle ne manipulent pas le verrou.
t t t
You can call wait( ) or notify( ) only for your own lock. Again, you can compile code that tries to use the wrong lock, but it will produce the same IllegalMonitorStateException message as before. You can’t fool with someone else’s lock, but you can ask another object to perform an operation that manipulates its own lock. So one approach is to create a synchronized method that calls notify( ) for its own object. However, in Notifier you’ll see the notify( ) call inside a synchronized block:
t Vous pouvez appelez wait() ou notify() seulement pour votre propre verrou. De nouveau, vous pouvez compiler un code qui essaye d'utiliser le mauvais verrou, mais il se produira le même message IllegalMonitorStateException que précédemment. Vous ne pouvez pas tricher avec le verrou de quelqu'un d'autre, mais vous pouvez demander à un autre objet de réaliser une opération qui manipule son verrou. Une approche possible est de créer une méthode synchronized qui appelle notify() pour son propre objet. Toutefois, dans Notifier vous verrez que notify() est appelé dans un bloc synchronized:
t t t
synchronized(wn2) { wn2.notify(); } t
synchronized(wn2) {
  wn2.notify();
}
t t t
where wn2 is the object of type WaitNotify2. This method, which is not part of WaitNotify2, acquires the lock on the wn2 object, at which point it’s legal for it to call notify( ) for wn2 and you won’t get the IllegalMonitorStateException.
t wn2 est l'objet de type WaitNotify2. Cette méthode, qui ne fait pas partie de WaitNotify2, acquiert le verrou sur l'objet wn2, à ce point il lui est possible d'appeler notify() pour wn2 et vous n'obtiendrez pas d'IllegalMonitorStateException.
t t t
t t t
t t
\\\
///
t t t
t
     
Sommaire Le site de Bruce Eckel