 |
 |
14) Les Threads multiples |
|
 |
|
Texte original |
 |
Traducteur :
Cédric Babault |
|
 |
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
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( ).
|
 |
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().
|
 |
 |
 |
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:
|
 |
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:
|
 |
 |
 |
- 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.
- 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.
- 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.
|
 |
-
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.
-
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.
-
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
.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
Blocking
|
 |
Blocage
[Blocking]
|
 |
 |
 |
A thread can be in any one of four
states:
|
 |
Un thread peut être dans un des quatre états
suivants:
|
 |
 |
 |
- New: The
thread object has been created but it hasn’t been started yet so it cannot
run.
- 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.
- 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.
- 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.
|
 |
-
Nouveau (New): L'objet thread a été créé
mais il n'a pas encore été démarré donc il ne peut pas
tourné.
-
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é.
-
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.
-
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.
|
 |
 |
 |
Becoming blocked
|
 |
Passer à l'état bloqué
|
 |
 |
 |
The blocked state is the most interesting
one, and is worth further examination. A thread can become blocked for five
reasons:
|
 |
L'état bloqué est le plus intéressant, et
nécessite un examen. Un thread peut devenir bloqué pour cinq raisons:
|
 |
 |
 |
- You’ve put the
thread to sleep by calling sleep(milliseconds), in which case it will not
be run for the specified
time.
- 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).
- 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.)
- The
thread is waiting for some I/O to
complete.
- The thread
is trying to call a synchronized method on another object, and that
object’s lock is not
available.
|
 |
-
Vous avez mis le thread à dormir en appelant
sleep(milliseconds), auquel cas il ne tournera pas pendant le temps
spécifé.
-
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).
-
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.)
-
Le thread attend la fin d'une I/O.
-
Le thread essaye d'appelé une méthode synchronized sur
un autre objet, et le verrou de cet objet n'est pas libre.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.)
|
 |
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.)
|
 |
 |
 |
Because this example demonstrates some
deprecated methods, you will get deprecation messages when it is
compiled.
|
 |
Étant donné que cet exemple montre des méthodes
dépréciées, vous obtiendrez des messages de depreciation lors de la
compilation.
|
 |
 |
 |
First, the basic
framework:
|
 |
D'abord le programme de base:
|
 |
 |
 |
//: 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
|
 |
//: 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
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
The indicator of change in
Blockable is an int i, which will be incremented by the
run( ) method of the derived class.
|
 |
L'indicateur de changement dans Blockable est un int i, qui
sera incrémenter par la méthode run() de la classe
dérivé.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
Sleeping
|
 |
Dormant (Sleeping)
|
 |
 |
 |
The first test in this program is with
sleep( ):
|
 |
Le premier test de ce programme est avec sleep():
|
 |
 |
 |
///: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
|
 |
///: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
|
 |
 |
 |
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.
|
 |
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é.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
Suspending and resuming
|
 |
Suspension et reprise
|
 |
 |
 |
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:
|
 |
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é:
|
 |
 |
 |
///: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
|
 |
///: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
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
Wait and notify
|
 |
Attendre et notifier
|
 |
 |
 |
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.
|
 |
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é.
|
 |
 |
 |
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.
|
 |
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é.
|
 |
 |
 |
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.
|
 |
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é.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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:
|
 |
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:
|
 |
 |
 |
synchronized(wn2) {
wn2.notify();
}
|
 |
synchronized(wn2) { wn2.notify(); }
|
 |
 |
 |
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.
|
 |
où 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.
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |