 |
 |
14) Les Threads multiples |
|
 |
|
Texte original |
 |
Traducteur : Cédric BABAULT |
|
 |
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
In ColorBoxes2 an array of
CBoxList is created and initialized to hold grid CBoxLists,
each of which knows how long to sleep. An equal number of CBox2 objects
is then added to each CBoxList, and each list is told to
go( ), which starts its thread.
|
 |
Dans ColorBoxes2 un tableau de CBoxList est
créé et initialisé pour contenir la grille de CBoxList [grid
CBoxList], chacune d'elles sait combien de temps dormir. Un nombre identique d'objets
CBox2 est alors ajouter à chaque CBoxList, et sur chaque liste est
appelé go(),qui démarre ce thread.
|
 |
 |
 |
CBox2 is similar to CBox:
it paints itself with a randomly chosen color. But that’s all a
CBox2 does. All of the threading has been moved into
CBoxList.
|
 |
CBox2 est similaire à CBox: elle se paint avec une
couleur choisie au hasard. Mais c'est tout ce qu'une CBox2 fait. Tout le threading a
été déplacé dans CBoxList.
|
 |
 |
 |
The CBoxList could also have
inherited Thread and had a member object of type ArrayList. That
design has the advantage that the add( ) and get( )
methods could then be given specific argument and return value types instead of
generic Objects. (Their names could also be changed to something
shorter.) However, the design used here seemed at first glance to require less
code. In addition, it automatically retains all the other behaviors of an
ArrayList. With all the casting and parentheses necessary for
get( ), this might not be the case as your body of code
grows.
|
 |
Le CBoxList pourrait aussi avoir hérité de
Thread et avoir un objet membre de type ArrayList. Ce design a l'avantage que les
méthodes add() et get() puissent avoir des arguments et un type de valeur de
retour spécifiques au lieu de ceux de la classe générique Object.
(Leurs noms pourraient aussi être changés en quelque chose de plus court.) Cependant,
le design utilisé ici semble au premier coup d'il nécessiter moins de code. En
plus, on garde automatiquement tous les autres comportements d'un ArrayList. Avec tous les
cast et parenthèses nécessaires pour get(), ça ne devrait pas
être le cas dès que votre code central augmente.
|
 |
 |
 |
As before, when you implement
Runnable you don’t get all of the equipment that comes with
Thread, so you have to create a new Thread and hand yourself to
its constructor in order to have something to start( ), as you can
see in the CBoxList constructor and in go( ). The
run( ) method simply chooses a random element number within the list
and calls nextColor( ) for that element to cause it to choose a new
randomly selected color.
|
 |
Comme précédemment, quand vous implémentez
Runnable vous ne disposez pas de tout l'équipement que vous avez avec Thread,
donc vous devez créer un nouveau Thread et passer vous même à son
constructeur pour avoir quelque chose à démarrer (par start()), comme vous
pouvez le voir dans le constructeur de CBoxList et dans go(). La méthode
run() choisit simplement un numéro d'élément au hasard dans la liste et
appelle nextColor() pour cet élément ce qui provoque le sélection au
hasard d'une nouvelle couleur.
|
 |
 |
 |
Upon running this program, you see that
it does indeed run faster and respond more quickly (for instance, when you
interrupt it, it stops more quickly), and it doesn’t seem to bog down as
much at higher grid sizes. Thus, a new factor is added into the threading
equation: you must watch to see that you don’t have “too many
threads” (whatever that turns out to mean for your particular program and
platform—here, the slowdown in ColorBoxes appears to be caused by
the fact that there’s only one thread that is responsible for all
painting, and it gets bogged down by too many requests). If you have too many
threads, you must try to use techniques like the one above to
“balance” the number of threads in your program. If you see
performance problems in a multithreaded program you now have a number of issues
to examine:
|
 |
En exécutant ce programme, vous voyez qu'effectivement il tourne et
répond plus vite (par exemple, quand vous l'interrompez, il s'arrête plus rapidement),
et il ne semble pas ralentir autant pour de grandes tailles de grilles. Ainsi, un nouveau facteur
est ajouter à l'équation du threading: vous devez regarder pour voir si vous n'avez
pas « trop de threads » (quelqu'en soit la signification pour votre programme
et plate-forme en particulier, le ralentissement dans ColorBoxes apparaît comme
provenant du fait qu'il n'y a qu'un thread qui répond pour toutes les colorations, et qu'il
est ralenti par trop de requêtes). Si vous avez trop de threads, vous devez essayez
d'utiliser des techniques comme celle ci-dessus pour « équilibrer » le
nombre de threads dans votre programme. Si vous voyez des problèmes de performances dans un
programme multithread vous avez maintenant plusieurs solutions à examiner:
|
 |
 |
 |
- Do you have enough calls
to sleep( ), yield( ), and/or
wait( )?
- Are
calls to sleep( ) long
enough?
- Are you
running too many
threads?
- Have you
tried different platforms and
JVMs?
|
 |
-
Avez vous assez d'appels à sleep(), yield(), et/ou
wait()?
-
Les appels à sleep() sont-ils assez longs?
-
Faites vous tournez trop de threads?
-
Avez vous essayé différentes plates-formes et
JVMs?
|
 |
 |
 |
Issues like this are one
reason that multithreaded programming is often considered an
art.
|
 |
Des questions comme celles-là sont la raison pour laquelle la
programmation multithread est souvent considéré comme un art.
|
 |
 |
 |
Summary
|
 |
Résumé
|
 |
 |
 |
It is vital to learn when to use
multithreading and when to avoid
it. The main reason to use it is to manage a number of tasks whose intermingling
will make more efficient use of the computer (including the ability to
transparently distribute the tasks across multiple CPUs) or be more convenient
for the user. The classic example of resource balancing is using the CPU during
I/O waits. The classic example of user convenience is monitoring a
“stop” button during long downloads.
|
 |
Il est vital d'apprendre quand utiliser le multithreading et quand
l'éviter. La principale raison pour l'utiliser est pour gérer un nombre de taches qui
mélangées rendront plus efficace l'utilisation de l'ordinateur (y compris la
possibilité de distribuer de façon transparente les tâches sur plusieurs CPUs)
ou être plus pratique pour l'utilisateur. L'exemple classique de répartition de
ressources est l'utilisation du CPU pendant les attentes d'entrées/sorties. L'exemple
classique du coté pratique pour l'utilisateur est la surveillance d'un bouton
« stop » pendant un long téléchargement.
|
 |
 |
 |
The main drawbacks to multithreading are:
|
 |
Les principaux inconvénients du multithreading sont:
|
 |
 |
 |
- Slowdown while waiting for
shared
resources
- Additional
CPU overhead required to manage
threads
- Unrewarded
complexity, such as the silly idea of having a separate thread to update each
element of an
array
- Pathologies
including starving, racing, and
deadlock
|
 |
-
Ralentissement en attente de ressources partagées
-
Du temp CPU supplémentaire nécessaire pour gérer les
threads
-
Complexité infructueuse, comme l'idée folle d'avoir un thread
séparé pour mettre à jour chaque élément d'un tableau
-
Pathologies incluant starving [=mourir de faim!?], racing, et inter-blocage
[deadlock]
|
 |
 |
 |
An additional
advantage to threads is that they substitute “light” execution
context switches (of the order of 100 instructions) for “heavy”
process context switches (of the order of 1000s of instructions). Since all
threads in a given process share the same memory space, a light context switch
changes only program execution and local variables. On the other hand, a process
change—the heavy context switch—must exchange the full memory
space.
|
 |
Un avantage supplémentaire des threads est qu'ils substituent des
contextes d'exécution « léger » (de l'ordre de 100
instructions) aux contextes d'exécutions lourds des processus (de l'ordre de 1000
instructions). Comme tous les threads d'un processus donné partage le même espace
mémoire, un contexte léger ne change que l'exécution du programme et les
variables locales. D'un autre coté, un changement de processus — le
changement de contexte lourd — doit échanger l'intégralité de
l'espace mémoire.
|
 |
 |
 |
Threading is like stepping into an
entirely new world and learning a whole new programming language, or at least a
new set of language concepts. With the appearance of thread support in most
microcomputer operating systems, extensions for threads have also been appearing
in programming languages or libraries. In all cases, thread programming (1)
seems mysterious and requires a shift in the way you think about programming;
and (2) looks similar to thread support in other languages, so when you
understand threads, you understand a common tongue. And although support for
threads can make Java seem like a more complicated language, don’t blame
Java. Threads are tricky.
|
 |
Le threading c'est comme marcher dans un monde entièrement nouveau
et apprendre un nouveau langage de programmation entier, ou au moins un nouveau jeu de concepts de
langage. Avec l'apparition du support des threads dans beaucoup de systèmes d'exploitation
de micro-ordinateurs, des extensions pour les threads sont aussi apparues dans les langages de
programmations ou librairies. Dans tous les cas, la programmation de thread (1) semble
mystérieuse et requiert un changement dans votre façon de penser la programmation; et
(2) apparaît comme similaire au support de thread dans les autres langages, donc quand vous
comprenez les threads, vous les comprenez dans une langue commune. Et bien que le support des
threads puisse faire apparaître Java comme un langage plus compliqué, n'accuser pas
Java. Les threads sont difficiles.
|
 |
 |
 |
One of the biggest difficulties with
threads occurs because more than one thread might be sharing a
resource—such as the memory in an object—and you must make sure that
multiple threads don’t try to read and change that resource at the same
time. This requires judicious use of the synchronized keyword, which is a
helpful tool but must be understood thoroughly because it can quietly introduce
deadlock situations.
|
 |
Une des plus grande difficultés des threads se produit lorsque plus
d'un thread partagent une ressource — comme la mémoire dans un
objet — et que vous devez être sûr que plusieurs threads n'essayent pas
de lire et changer cette ressource en même temps. Cela nécessite l'utilisation
judicieuse du mot clé synchronized, qui est un outil bien utile mais qui doit
être bien compris parce qu'il peut introduire silencieusement des situations
d'inter-blocage.
|
 |
 |
 |
In addition, there’s a certain art
to the application of threads. Java is designed to allow you to create as many
objects as you need to solve your problem—at least in theory. (Creating
millions of objects for an engineering finite-element analysis, for example,
might not be practical in Java.) However, it seems that there is an upper bound
to the number of threads you’ll want to create, because at some point a
large number of threads seems to become unwieldy. This critical point is not in
the many thousands as it might be with objects, but rather in the low hundreds,
sometimes less than 100. As you often create only a handful of threads to solve
a problem, this is typically not much of a limit, yet in a more general design
it becomes a constraint.
|
 |
En plus, il y a un certain art dans la mise en application des threads.
Java est conçu pour résoudre vos problèmes — au moins en
théorie. (Créer des millions d'objets pour une analyse d'un ensemble fini
d'élément dans l'ingénierie, par exemple, devrait être faisable en
Java). Toutefois, il semble qu'il y ai une borne haute au nombre de threads que vous voudrez
créer, parce que à un certain point un grand nombre de threads semble devenir lours.
Ce point critique n'est pas dans les milliers comme il devrait être avec les objets, mais
plutôt à quelques centaines, quelquefois moins que 100. Comme vous créez
souvent seulement une poignée de threads pour résoudre un problème, c'est
typiquement pas vraiment une limite, bien que dans un design plus général cela
devient une contrainte.
|
 |
 |
 |
A significant nonintuitive issue in
threading is that, because of thread scheduling, you can typically make your
applications run faster by inserting calls to sleep( ) inside
run( )’s main loop. This definitely makes it feel like an art,
in particular when the longer delays seem to speed up performance. Of course,
the reason this happens is that shorter delays can cause the
end-of-sleep( ) scheduler interrupt to happen before the running
thread is ready to go to sleep, forcing the scheduler to stop it and restart it
later so it can finish what it was doing and then go to sleep. It takes extra
thought to realize how messy things can get.
|
 |
Une importante conséquence non-évidente du threading est que,
en raison de l'ordonnancement des threads, vous pouvez classiquement rendre vos applications plus
rapides en insérant des appels à sleep() dans la boucle principale de
run(). Cela fait définitivement ressembler ça à un art, en particulier
quand la longueur du délai semble augmenter les performances. Bien sûr, la raison pour
laquelle cela arrive est que des délais plus court peuvent causer la fin de sleep()
de l'interruption du scheduler avant que le thread tournant soit prêt à passer au
sleep, forçant le scheduler à l'arrêter et le redémarrer plus tard afin
qu'il puisse finir ce qu'il était en train de faire puis de passer à sleep. Cela
nécessite une réflexion supplémentaire pour réaliser quel
désordre regne dans tout ça.
|
 |
 |
 |
One thing you might notice missing in
this chapter is an animation example, which is one of the most popular things to
do with applets. However, a complete solution (with sound) to this problem comes
with the Java JDK (available at java.sun.com) in the demo section. In
addition, we can expect better animation support to become part of future
versions of Java, while completely different non-Java, non-programming solutions
to animation for the Web are appearing that will probably be superior to
traditional approaches. For explanations about how Java animation works, see
Core Java 2 by Horstmann & Cornell, Prentice-Hall, 1997. For more
advanced discussions of threading, see Concurrent Programming in Java by
Doug Lea, Addison-Wesley, 1997, or Java Threads by Oaks & Wong,
O’Reilly,
1997.
|
 |
Une chose que vous devez notez comme manquant dans ce chapitre est un
exemple d'animation, ce qui est une des chose les plus populaires faite avec des applets.
Toutefois, une solution complète (avec du son) à ce problème vient est
disponible avec le Java JDK (disponible sur java.sun.com) dans la section demo. En plus,
nous pouvons nous attendre à un meilleur support d'animation comme partie des futures
versions de Java, tandis que des solutions très différentes du Java, non
programmables, pour les animations apparaissent sur le Web qui seront probablement
supérieures aux approches traditionnelles. Pour des explications à propos du
fonctionnement des animations Java, voyez Core Java 2 par Horstmann & Cornell,
Prentice-Hall, 1997. Pour des discussions plus avancées sur le threading, voyez
Concurrent Programming in Java de Doug Lea, Addison-Wesley, 1997, ou Java Threads de
Oaks & Wong, O’Reilly, 1997.
|
 |
 |
 |
Exercises
|
 |
Exercices
|
 |
 |
 |
Solutions to selected exercises
can be found in the electronic document The Thinking in Java Annotated
Solution Guide, available for a small fee from
www.BruceEckel.com.
|
 |
Les solutions des exercices sélectionnés
peuvent être trouvées dan le document électronique The Thinking in Java
Annotated Solution Guide, disponible pour une petite contribution sur
www.BruceEckel.com.
|
 |
 |
 |
- Inherit a class from
Thread and override the run( ) method. Inside
run( ), print a message, and then call sleep( ). Repeat
this three times, then return from run( ). Put a start-up message in
the constructor and override finalize( ) to print a shut-down
message. Make a separate thread class that calls System.gc( ) and
System.runFinalization( ) inside run( ), printing a
message as it does so. Make several thread objects of both types and run them to
see what
happens.
- Modify
Sharing2.java to add a synchronized block inside the
run( ) method of TwoCounter instead of synchronizing the
entire run( )
method.
- Create two
Thread subclasses, one with a run( ) that starts up, captures
the reference of the second Thread object and then calls
wait( ). The other class’ run( ) should call
notifyAll( ) for the first thread after some number of seconds have
passed, so the first thread can print a
message.
- In
Counter5.java inside Ticker2, remove the yield( ) and
explain the results. Replace the yield( ) with a
sleep( ) and explain the
results.
- In
ThreadGroup1.java, replace the call to sys.suspend( ) with a
call to wait( ) for the thread group, causing it to wait for two
seconds. For this to work correctly you must acquire the lock for sys
inside a synchronized
block.
- Change
Daemons.java so that main( ) has a sleep( )
instead of a readLine( ). Experiment with different sleep times to
see what happens.
- In
Chapter 8, locate the GreenhouseControls.java example, which consists of
three files. In Event.java, the class Event is based on watching
the time. Change Event so that it is a Thread, and change the rest
of the design so that it works with this new Thread-based
Event.
- Modify
Exercise 7 so that the java.util.Timer class found in JDK 1.3 is used to
run the
system.
- Starting
with SineWave.java from Chapter 13, create a program (an
applet/application using the Console class) that draws an animated sine
wave that appears to scrolls past the viewing window like an oscilloscope,
driving the animation with a Thread. The speed of the animation should be
controlled with a java.swing.JSlider
control.
- Modify
Exercise 9 so that multiple sine wave panels are created within the application.
The number of sine wave panels should be controlled by HTML tags or command-line
parameters.
- Modify
Exercise 9 so that the java.swing.Timer class is used to drive the
animation. Note the difference between this and
java.util.Timer.
|
 |
-
Faites hériter une classe de Thread et redéfinissez la
méthode run(). Dans run(), affichez un message, puis appelez sleep().
Répétez cela trois fois, puis sortez de run(). Placer un message de
démarrage dans le constructeur et redéfinissez finalize() pour afficher un
message d'arrêt. Faites une classe thread séparée qui appelle
System.gc() et System.runFinalization() dans run(), affichant également
un message. Faites plusieurs objets threads des deux types et exécutez les pour voir ce qui
ce passe.
-
Modifiez Sharing2.java pour ajouter un bloc synchronized dans
la méthode run() de TwoCounter à la place de la synchronisation sur
l'intégralité de la méthode run().
-
Créez deux sous-classes de Thread une avec un run()
qui démarre, capture la référence à un second objet Thread et
appelle alors wait(). Le run() de l'autre classe devrait appeler notifyAll()
pour le premier thread après qu'un certain nombre de secondes se soit écoulé,
ainsi le premier thread peut afficher un message.
-
Dans Ticker2 de Counter5.java, supprimez le yield() et
expliquez les résultats. Remplacez le yield() avec un sleep() et expliquez les
résultats.
-
Dans ThreadGroup1.java, remplacez l'appel à
sys.suspend() par un appel à wait() pour le bon groupe de thread,
entraînant une attente de deux secondes. Pour que ça marche correctement vous devez
acquérir le verrou pour sys dans un bloc synchronized.
-
Changez Daemons.java pour que main() ait un sleep()
à la place d'un readLine(). Expérimentez avec différents temps pour le
sleep pour voir ce qui se passe.
-
Dans le chapitre 8, localisez l'exemple GreenhouseControls.java, qui
est constitués de trois fichiers. Dans Event.java, la classe Event est
basée sur l'observation du temps. Changez Event pour qu'il soit un Thread, et
changez le reste du design pour qu'il fonctionne avec le nouvel Event basé sur
Thread.
-
Modifiez l'exercice 7 pour que la classe java.util.Timer
trouvé dans le JDK 1.3 soit utilisée pour faire tourner le système.
-
En commençant avec SineWave.java du Chapitre 13, créez
un programme (une applet/application utilisant la classe Console ) qui dessine un signal
sinus animé qui apparaît en défilant sur la fenêtre comme un
oscilloscope, dirigeant l'animation avec un Thread. La vitesse de l'animation pourra
être controller avec un contrôle java.swing.JSlider.
-
Modifiez l'exercice 9 afin que de multiple signaux sinus puissent
être contrôlés par des tags HTML ou des paramètres de la ligne de
commande.
-
Modifiez l'exercice 9 afin que la classe java.swing.Timer soit
utilisée pour diriger l'animation. Notez la différence entre celle-ci et
java.util.Timer.
|
 |
 |
 |
[70]
Runnable was in Java 1.0, while inner classes were not introduced until
Java 1.1, which may partially account for the existence of Runnable.
Also, traditional multithreading architectures focused on a function to be run
rather than an object. My preference is always to inherit from Thread if
I can; it seems cleaner and more flexible to me.
|
 |
[70] Runnable
était dans Java 1.0, tandis que les classes internes n'ont été introduites que
dans Java 1.1, qui devait partiellement compter pour l'existence de Runnable. De plus, les
architectures multithread traditionelles se basent sur une fonction à exécuter
plutôt qu'un objet. Ma préférence est toujours d'hériter de
Thread si possible; cela parait plus clair et plus flexible pour moi.
|
 |
 |
 |
[71]
The Java Programming Language, by Ken Arnold and James Gosling,
Addison-Wesley 1996 pp 179.
[ Previous Chapter ]
[ Short TOC ]
[ Table of Contents ]
[ Index ]
[ Next Chapter ]
Last Update:04/24/2000
|
 |
[71] The Java
Programming Language, de Ken Arnold et James Gosling, Addison-Wesley 1996 pp
179.
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |