 |
 |
14) Les Threads multiples |
|
 |
|
Texte original |
 |
Traducteur : Cédric BABAULT |
|
 |
|
Ce chapitre contient 9 pages
1
2
3
4
5
6
7
8
9
|
|
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
|
 |
29.11.2001 - version 1.2 [Armel] : - Corrections dans le code html. 29.10.2001 - version 1.1 [Armel] : - Remise en forme du code html ; - Premier passage au correcteur d'orthographe de Staroffice. 29.10.2001 - version 1.0 [Cédric BABAULT] : - Première publication sur Yahoogroupes. Traducteur : - Cédric BABAULT Texte original : - Thinking in Java, 2nd edition, Revision 10 © 2000 by Bruce ECKEL
|
 |
 |
 |
14: Multiple Threads
|
 |
14) Les Threads multiples
|
 |
 |
 |
Objects provide a way to divide a
program into independent sections. Often, you also need to turn a program into
separate, independently running subtasks.
|
 |
Les objets permettent une division d'un programme en
sections indépendantes. Souvent, nous avons aussi besoin de découper un programme en
unités d'exécution indépendantes.
|
 |
 |
 |
Each of these independent subtasks is
called a thread, and you
program as if each thread runs by itself and has the CPU to itself. Some
underlying mechanism is actually dividing up the CPU time for you, but in
general, you don’t have to think about it, which makes programming with
multiple threads a much easier task.
|
 |
Chacune de ces unités d'exécution est appelé un
thread, et vous programmez comme si chaque thread s'exécutait par elle même et
avait son propre CPU. Un mécanisme sous-jacent s'occupe de diviser le temp CPU pour vous,
mais en général vous n'avez pas besoin d'y penser, c'est ce qui fait de la
programmation multi-threads une tâche beaucoup plus facile.
|
 |
 |
 |
A process
is a self-contained running program with its own address space. A
multitasking operating system is capable of
running more than one process (program) at a time, while making it look like
each one is chugging along on its own, by periodically providing CPU cycles to
each process. A thread is a single sequential flow of control within a process.
A single process can thus have multiple concurrently executing
threads.
|
 |
Un processus est un programme s'exécutant dans son propre
espace d'adressage. Un système d'exploitation multitâche est capable d'exécuter
plusieurs processus (programme) en même temps, en faisant comme si chacun d'eux
s'exécutait de façon indépendante, en accordant périodiquement des
cycles CPU à chaque processus. Un thread est un flot de contrôle séquentiel
à l'intérieur d'un processus. Un processus peut contenir plusieurs threads
s'exécutant en concurrence.
|
 |
 |
 |
There are many possible uses for
multithreading, but in general, you’ll have some part of your program tied
to a particular event or resource, and you don’t want to hang up the rest
of your program because of that. So you create a thread associated with that
event or resource and let it run independently of the main program. A good
example is a “quit” button—you don’t want to be forced
to poll the quit button in every piece of code you write in your program and yet
you want the quit button to be responsive, as if you were checking it
regularly. In fact, one of the most immediately compelling reasons for
multithreading is to produce a responsive user
interface.
|
 |
Il existe plusieurs utilisations possibles du multithreading, mais en
général, vous aurez une partie de votre programme attachée à un
événement ou une ressource particulière, et vous ne voulez pas que le reste de
votre programme dépende de ça. Vous créez donc un thread associé
à cet événement ou ressource et laissez celui-ci s'exécuter
indépendamment du programme principal. Un bon exemple est celui d'un bouton
« quitter » - vous ne voulez pas être obliger de vérifier le
bouton quitter dans chaque partie de code que vous écrivez dans votre programme, mais vous
voulez que le bouton quitter réponde comme si vous le vérifiez
régulièrement. En fait, une des plus immédiatement indiscutables raisons
d'être du multithreading est de produire des interfaces utilisateurs dynamiques.
[responsive user interface]
|
 |
 |
 |
Responsive user interfaces
|
 |
Interfaces utilisateurs dynamiques [Responsive user
interfaces]
|
 |
 |
 |
As a starting point, consider a program
that performs some CPU-intensive operation and thus ends up ignoring user input
and being unresponsive. This one, a combined applet/application, will simply
display the result of a running counter:
|
 |
Comme point de départ, considérons un programme qui contient
des opérations nécessitant beaucoup de temps CPU finissant ainsi par ignorer les
entrées de l'utilisateur et répondrait mal. Celle-ci, une combinaison
applet/application, affichera simplement le résultat d'un compteur actif [running
counter] :
|
 |
 |
 |
//: c14:Counter1.java // A non-responsive user interface. // <applet code=Counter1 width=300 height=100> // </applet> import javax.swing.*; import java.awt.event.*; import java.awt.*; import com.bruceeckel.swing.*;
public class Counter1 extends JApplet { private int count = 0; private JButton start = new JButton("Start"), onOff = new JButton("Toggle"); private JTextField t = new JTextField(10); private boolean runFlag = true; public void init() { Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(t); start.addActionListener(new StartL()); cp.add(start); onOff.addActionListener(new OnOffL()); cp.add(onOff); } public void go() { while (true) { try { Thread.sleep(100); } catch(InterruptedException e) { System.err.println("Interrupted"); } if (runFlag) t.setText(Integer.toString(count++)); } } class StartL implements ActionListener { public void actionPerformed(ActionEvent e) { go(); } } class OnOffL implements ActionListener { public void actionPerformed(ActionEvent e) { runFlag = !runFlag; } } public static void main(String[] args) { Console.run(new Counter1(), 300, 100); } } ///:~
|
 |
//: c14:Counter1.java // Une interface utilisateur sans répondant. // <applet code=Counter1 width=300 height=100> // </applet> import javax.swing.*; import java.awt.event.*; import java.awt.*; import com.bruceeckel.swing.*;
public class Counter1 extends JApplet { private int count = 0; private JButton start = new JButton("Start"), onOff = new JButton("Toggle"); private JTextField t = new JTextField(10); private boolean runFlag = true; public void init() { Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(t); start.addActionListener(new StartL()); cp.add(start); onOff.addActionListener(new OnOffL()); cp.add(onOff); } public void go() { while (true) { try { Thread.sleep(100); } catch(InterruptedException e) { System.err.println("Interrupted"); } if (runFlag) t.setText(Integer.toString(count++)); } } class StartL implements ActionListener { public void actionPerformed(ActionEvent e) { go(); } } class OnOffL implements ActionListener { public void actionPerformed(ActionEvent e) { runFlag = !runFlag; } } public static void main(String[] args) { Console.run(new Counter1(), 300, 100); } } ///:~
|
 |
 |
 |
At this point, the Swing and applet code
should be reasonably familiar from Chapter 13. The go( ) method is
where the program stays busy: it puts the current value of count into
the JTextField t, then increments count.
|
 |
À ce niveau, le code Swing et de l'applet devrait être
raisonnablement familier depuis le chapitre 13. La méthode go() est celle dans
laquelle le programme reste occupé: il met le valeur courante de count dans le
JTextField t, avant d'incrémenter count.
|
 |
 |
 |
Part of the infinite loop inside
go( ) is to call sleep( ).
sleep( ) must be associated with a
Thread object, and it turns out that every
application has some thread associated with it. (Indeed, Java is based on
threads and there are always some running along with your application.) So
regardless of whether you’re explicitly using threads, you can produce the
current thread used by your program with Thread and the static
sleep( ) method.
|
 |
La boucle infinie à l'intérieur de go() appelle
sleep(). sleep() doit être associé avec un objet Thread, ce qui
montre que toute application a un thread qui lui est associé. (En effet, Java est
basé sur des threads et il y en a toujours qui tourne avec votre application.) Donc, que
vous utilisiez explicitement les threads ou non, vous pouvez accéder au thread courant
utilisé par votre programme avec Thread et la méthode static
sleep()
|
 |
 |
 |
Note that sleep( ) can throw
an InterruptedException, although throwing such
an exception is considered a hostile way to break from a thread and should be
discouraged. (Once again, exceptions are for exceptional conditions, not normal
flow of control.) Interrupting a sleeping thread is included to support a future
language feature.
|
 |
Notez que sleep() peut déclencher une
InterruptedException, alors que le déclenchement d'une telle exception est
considéré comme un moyen hostile d'arrêter un thread et devrait être
découragé. (Encore une fois les exceptions sont destinées aux conditions
exceptionnelles et non pour le controle du flot normal.) L'interruption d'un thread en sommeil sera
incluse dans une future fonctionnalité du langage.
|
 |
 |
 |
When the start button is pressed,
go( ) is invoked. On examining go( ), you might naively
think (as I did) that it should allow multithreading because it goes to sleep.
That is, while the method is asleep, it seems like the CPU could be busy
monitoring other button presses. But it turns out that the real problem is that
go( ) never returns, since it’s in an infinite loop, and this
means that actionPerformed( ) never returns. Since you’re
stuck inside actionPerformed( ) for the first keypress, the program
can’t handle any other events. (To get out, you must somehow kill the
process; the easiest way to do this is to press Control-C in the console window,
if you started it from the console. If you start it via the browser, you have to
kill the browser window.)
|
 |
Quand le bouton start est pressé, go() est
appelé. En examinant go(), vous pouvez naïvement pensé (comme je le fais)
que cela autorisera le multithreading parce que elle se met en sommeil [it goes to sleep].
C'est le cas, tant que la méthode n'est pas en sommeil, tout ce passe comme si le CPU
pouvait être occupé à gérer les autres boutons. [=That is, while the
method is asleep, it seems like the CPU could be busy monitoring other button presses. ] Mais
il s'avère que le vrai problème est que go() ne s'achève jamais,
puisqu'il s'agit d'une boucle infinie, ce qui signifie que actionPerformed() ne
s'achève jamais. Comme vous êtes bloqué dans actionPerformed() par la
première touche appuyée, le programme ne peut pas capturer les autres
événements. (Pour sortir vous devez tué le processus; le moyen le plus facile
de le faire est de presser les touches Control-C dans la console, si vous avez lancé le
programme depuis la console. Si vous l'avez démarrer dans un navigateur, vous devez fermer
la fenêtre du navigateur.)
|
 |
 |
 |
The basic problem here is that
go( ) needs to continue performing its operations, and at the same
time it needs to return so that actionPerformed( ) can complete and
the user interface can continue responding to the user. But in a conventional
method like go( ) it cannot continue and at the same time
return control to the rest of the program. This sounds like an impossible thing
to accomplish, as if the CPU must be in two places at once, but this is
precisely the illusion that threading provides.
|
 |
Le problème de base ici est que go() doit continuer de
réaliser ses opérations, et dans le même temps elle doit retourner à la
fonction appelante de façon à ce que actionPerformed() puisse se terminer et
que l'interface utilisateur puisse continuer à répondre à l'utilisateur. Mais
une méthode conventionnelle comme go() ne peut pas continuer et dans le
même temps rendre le contrôle au reste du programme. Cela à l'air d'une chose
impossible à accomplir, comme si le CPU devait être à deux endroits à la
fois, mais c'est précisément l'illusion que les threads permettent.
|
 |
 |
 |
The thread model (and its programming
support in Java) is a programming convenience to simplify juggling several
operations at the same time within a single program. With threads, the CPU will
pop around and give each thread some of its time. Each thread has the
consciousness of constantly having the CPU to itself, but the CPU’s time
is actually sliced between all the threads. The exception to this is if your
program is running on multiple CPUs. But one of the great things about threading
is that you are abstracted away from this layer, so your code does not need to
know whether it is actually running on a single CPU or many. Thus, threads are a
way to create transparently scalable programs.
|
 |
Le modèle des threads (et son support de programmation Java) est une
commodité de programmation pour simplifier le jonglage entre plusieurs opérations
simultanées dans un même programme. Avec les threads, le temp CPU sera
éclaté et distribué entre les différentes threads. Chaque thread a
l'impression d'avoir constamment le CPU pour elle toute seule, mais le temps CPU est
distribué entre les différentes threads. L'exception à cela est si votre
programme tourne sur plusieurs CPUs. Mais ce qui est intéressant dans le threading c'est de
permettre une abstraction par rapport à cette couche, votre code n'a pas besoin de savoir si
il tournera sur un ou plusieurs CPUs. Ainsi, les threads sont un moyen de créer de
façon transparente des programmes portables.
|
 |
 |
 |
Threading
reduces computing efficiency somewhat, but the net improvement in program
design, resource balancing, and user convenience is often quite valuable. Of
course, if you have more than one CPU, then the operating system can dedicate
each CPU to a set of threads or even a single thread and the whole program can
run much faster. Multitasking and multithreading tend to be the most reasonable
ways to utilize multiprocessor
systems.
|
 |
Le threading réduit l'efficacité informatique, mais la net
amélioration dans la conception des programmes, la gestion de ressources [resource
balancing] , et le confort d'utilisation est souvent valable. Bien sûr, si vous avez plus
d'un CPU, alors le système d'exploitation pourra décider quel CPU attribué
à quel jeux de threads ou attribué une seule thread et la totalité du
programme pourra s'exécuter beaucoup plus vite. Le multi-tâche et le multithreading
tendent à être l'approche la plus raisonnable pour utiliser les systèmes
multi-processeurs.
|
 |
 |
 |
Inheriting from Thread
|
 |
Héritage de Thread
|
 |
 |
 |
The simplest way to create a thread is to
inherit from class Thread, which has all the wiring necessary to create
and run threads. The most important method for Thread is
run( ), which you must override to make the thread do your bidding.
Thus, run( ) is the code that will be executed
“simultaneously” with the other threads in a
program.
|
 |
Le moyen le plus simple de créer une thread est d'hériter de
la classe Thread, qui a toutes les connexions nécessaires pour créer et faire
tourner les threads. La plus importante méthode de Thread est run(), qui doit
être redéfinie pour que le thread fasse ce que vous lui demander. Ainsi, run(),
contient le code qui sera exécuté « simultanément » avec
les autres threads du programme.
|
 |
 |
 |
The following example creates any number
of threads that it keeps track of by assigning each thread a unique number,
generated with a static variable. The
Thread’s run( ) method is
overridden to count down each time it passes through its loop and to finish when
the count is zero (at the point when run( ) returns, the thread is
terminated).
|
 |
L'exemple suivant créée un certain nombre de threads dont il
garde la trace en attribuant à chaque thread un numéro unique,
généré à l'aide d'une variable static. La méthode
run() du Thread est redéfinie pour décrémenter le compteur
à chaque fois qu'elle passe dans sa boucle et se termine quand le compteur est à
zéro (au moment où run() retourne, le thread se termine).
|
 |
 |
 |
//: c14:SimpleThread.java // Very simple Threading example.
public class SimpleThread extends Thread { private int countDown = 5; private static int threadCount = 0; private int threadNumber = ++threadCount; public SimpleThread() { System.out.println("Making " + threadNumber); } public void run() { while(true) { System.out.println("Thread " + threadNumber + "(" + countDown + ")"); if(--countDown == 0) return; } } public static void main(String[] args) { for(int i = 0; i < 5; i++) new SimpleThread().start(); System.out.println("All Threads Started"); } } ///:~
|
 |
//: c14:SimpleThread.java // Un example très simple de Threading.
public class SimpleThread extends Thread { private int countDown = 5; private static int threadCount = 0; private int threadNumber = ++threadCount; public SimpleThread() { System.out.println("Making " + threadNumber); } public void run() { while(true) { System.out.println("Thread " + threadNumber + "(" + countDown + "#004488">")"); if(--countDown == 0) return; } } public static void main(String[] args) { for(int i = 0; i < 5; i++) new SimpleThread().start(); System.out.println("All Threads Started"); } } ///:~
|
 |
 |
 |
A run( ) method virtually
always has some kind of loop that continues until the thread is no longer
necessary, so you must establish the condition on which to break out of this
loop (or, in the case above, simply return from run( )).
Often, run( ) is cast in the form of an infinite loop, which means
that, barring some external factor that causes run( ) to terminate,
it will continue forever.
|
 |
Une méthode run() a toujours virtuellement une sorte de
boucle qui continue jusqu'à ce que le thread ne soit plus nécessaire, ainsi vous
pouvez établir la condition sur laquelle arrêter cette boucle (ou, comme dans le cas
précédent, simplement retourner de run()). Souvent, run() est
transformé en une boucle infinie, ce qui signifie que à moins qu'un facteur
extérieur ne cause la terminaison de run(), elle continuera pour toujours.
|
 |
 |
 |
In main( ) you can see a
number of threads being created and run. The start( ) method in the
Thread class performs special initialization for
the thread and then calls run( ). So the steps are: the constructor
is called to build the object, then start( ) configures the thread
and calls run( ). If you don’t call start( )
(which you can do in the constructor, if that’s appropriate) the thread
will never be started.
|
 |
Dans main() vous pouvez voir un certain nombre de threads
créé et lancée. La méthode start() de la classe Thread
procède à une initialisation spéciale du thread et appelle ensuite
run(). Les étapes sont donc: le constructeur est appelé pour construire
l'objet, puis start() configure le thread et appelle run(). Si vous n'appelez pas
start() (ce que vous pouvez faire dans le constructeur, si c'est approprié) le thread
ne sera jamais démarré.
|
 |
 |
 |
The output for one run of this program
(it will be different from one run to another) is:
|
 |
La sortie d'une exécution de ce programme (qui peut être
différente d'un exécution à l'autre) est:
|
 |
 |
 |
Making 1 Making 2 Making 3 Making 4 Making 5 Thread 1(5) Thread 1(4) Thread 1(3) Thread 1(2) Thread 2(5) Thread 2(4) Thread 2(3) Thread 2(2) Thread 2(1) Thread 1(1) All Threads Started Thread 3(5) Thread 4(5) Thread 4(4) Thread 4(3) Thread 4(2) Thread 4(1) Thread 5(5) Thread 5(4) Thread 5(3) Thread 5(2) Thread 5(1) Thread 3(4) Thread 3(3) Thread 3(2) Thread 3(1)
|
 |
Making 1 Making 2 Making 3 Making 4 Making 5 Thread 1(5) Thread 1(4) Thread 1(3) Thread 1(2) Thread 2(5) Thread 2(4) Thread 2(3) Thread 2(2) Thread 2(1) Thread 1(1) All Threads Started Thread 3(5) Thread 4(5) Thread 4(4) Thread 4(3) Thread 4(2) Thread 4(1) Thread 5(5) Thread 5(4) Thread 5(3) Thread 5(2) Thread 5(1) Thread 3(4) Thread 3(3) Thread 3(2) Thread 3(1)
|
 |
 |
 |
You’ll notice that nowhere in this
example is sleep( ) called, and yet the output indicates that each
thread gets a portion of the CPU’s time in which to execute. This shows
that sleep( ), while it relies on the existence of a thread in order
to execute, is not involved with either enabling or disabling threading.
It’s simply another method.
|
 |
Vous aurez remarqué que nulle part dans cet exemple sleep()
n'est appelé, mais malgré cela la sortie indique que chaque thread a eu une portion
de temps CPU dans lequel s'exécuter. Cela montre que sleep(), bien que relié
à l'existence d'un thread pour pouvoir s'exécuter, n'est pas impliqué dans
l'activation et la désactivation du threading. C'est simplement une autre méthode
[NDT: de la classe Thread].
|
 |
 |
 |
You can also see that the
threads are not run in the order that they’re
created. In fact, the order that the CPU attends to an existing set of threads
is indeterminate, unless you go in and adjust the priorities using
Thread’s setPriority( ) method.
|
 |
Vous pouvez aussi voir que les threads ne sont pas exécutés
dans l'ordre où ils sont créés. En fait, l'ordre dans lequel le CPU s'occupe
d'un ensemble de threads donné est indéterminé, à moins que vous ne
rentriez dans l'ajustement des priorités en utilisant la méthode setPriority()
de Thread.
|
 |
 |
 |
When main( ) creates the
Thread objects it isn’t capturing the references for any of them.
An ordinary object would be fair game for garbage collection, but not a
Thread. Each Thread “registers” itself so there is
actually a reference to it someplace and the garbage collector can’t clean
it up.
|
 |
Quand main() créée les objets Thread elle le
capture aucune des références sur ces objets. Un objet ordinaire serait la proie
rêvée pour le garbage collector, mais pas un Thread. Chaque Thread
« s'enregistre » lui-même, il y a alors quelque part une
référence sur celui-ci donc le garbage collector ne peut pas le
détruire.
|
 |
 |
 |
Threading for a responsive interface
|
 |
Threading pour une une interface réactive
|
 |
 |
 |
Now it’s possible to solve the
problem in Counter1.java with a thread. The trick is to place the
subtask—that is, the loop that’s inside
go( )—inside the run( ) method of a thread. When
the user presses the start button, the thread is started, but then the
creation of the thread completes, so even though the thread is running,
the main job of the program (watching for and responding to user-interface
events) can continue. Here’s the solution:
|
 |
Il est maintenant possible de résoudre le problème de
Counter1.java avec un thread. Le truc est de placer une sous-tâche —
est la boucle de la méthode go() — dans la méthode
run() du thread. Quand l'utilisateur presse le bouton start, le thread est
démarré, mais quand la creation du thread est terminé, donc que le
thread tourne, le principal travail du programme (chercher et répondre aux
événements de l'interface utilisateur) peut continuer. Voici la solution:
|
 |
 |
 |
//: c14:Counter2.java // A responsive user interface with threads. // <applet code=Counter2 width=300 height=100> // </applet> import javax.swing.*; import java.awt.*; import java.awt.event.*; import com.bruceeckel.swing.*;
public class Counter2 extends JApplet { private class SeparateSubTask extends Thread { private int count = 0; private boolean runFlag = true; SeparateSubTask() { start(); } void invertFlag() { runFlag = !runFlag; } public void run() { while (true) { try { sleep(100); } catch(InterruptedException e) { System.err.println("Interrupted"); } if(runFlag) t.setText(Integer.toString(count++)); } } } private SeparateSubTask sp = null; private JTextField t = new JTextField(10); private JButton start = new JButton("Start"), onOff = new JButton("Toggle"); class StartL implements ActionListener { public void actionPerformed(ActionEvent e) { if(sp == null) sp = new SeparateSubTask(); } } class OnOffL implements ActionListener { public void actionPerformed(ActionEvent e) { if(sp != null) sp.invertFlag(); } } public void init() { Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(t); start.addActionListener(new StartL()); cp.add(start); onOff.addActionListener(new OnOffL()); cp.add(onOff); } public static void main(String[] args) { Console.run(new Counter2 (), 300, 100); } } ///:~
|
 |
//: c14:Counter2.java // Une interface utilisateur réactive grace aux threads. // <applet code=Counter2 width=300 height=100> // </applet> import javax.swing.*; import java.awt.*; import java.awt.event.*; import com.bruceeckel.swing.*;
public class Counter2 extends JApplet { private class SeparateSubTask color="#0000ff">extends Thread { private int count = 0; private boolean runFlag = color="#0000ff">true; SeparateSubTask() { start(); } void invertFlag() { runFlag = !runFlag; } public void run() { while (true) { try { sleep(100); } catch(InterruptedException e) { System.err.println("Interrupted"); } if(runFlag) t.setText(Integer.toString(count++)); } } } private SeparateSubTask sp = null; private JTextField t = new JTextField(10); private JButton start = new JButton("Start"), onOff = new JButton("Toggle"); class StartL implements ActionListener { public void actionPerformed(ActionEvent e) { if(sp == null) sp = new SeparateSubTask(); } } class OnOffL implements ActionListener { public void actionPerformed(ActionEvent e) { if(sp != null) sp.invertFlag(); } } public void init() { Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(t); start.addActionListener(new StartL()); cp.add(start); onOff.addActionListener(new OnOffL()); cp.add(onOff); } public static void main(String[] args) { Console.run(new Counter2 (), 300, 100); } } ///:~
|
 |
 |
 |
Counter2 is a straightforward
program, whose only job is to set up and maintain the user interface. But now,
when the user presses the start button, the event-handling code does not
call a method. Instead a thread of class SeparateSubTask is created, and
then the Counter2 event loop continues.
|
 |
Counter2 est un programme simple, son seul travail est de
préparer et maintenir l'interface utilisateur. Mais maintenant, quand l'utilisateur presse
le bouton start, le code gérant l'événement n'appelle plus de
méthode. Au lieu de ça un thread de la classe SeparateSubTask est créer
et la boucle d'événements de Counter2 continue.
|
 |
 |
 |
The class SeparateSubTask is a
simple extension of Thread with a constructor that runs the thread by
calling start( ), and a run( ) that essentially contains
the “go( )” code from
Counter1.java.
|
 |
La classe SeparateSubTask est une simple extension de Thread
avec un constructeur qui lance le thread en appelant start(), et un run() qui
contient essentiellement le code de « go() » de
Counter1.java.
|
 |
 |
 |
Because SeparateSubTask is an
inner class, it can directly access the JTextField t in Counter2;
you can see this happening inside run( ). The t field in the
outer class is private since SeparateSubTask can access it without
getting any special permission—and it’s always good to make fields
“as private as possible” so they cannot be accidentally
changed by forces outside your class.
|
 |
La classe SeparateSubTask étant une classe interne, elle peut
accéder directement au JTextField t dans Counter2; comme vous pouvez le voir
dans run(). SeparateSubTask peut accéder au champ t sans permission
spéciale malgré que ce champ soit déclaré private dans la classe
externe — il est toujours bon de rendre les champs « aussi
privé que possible » de façon à ce qu'ils ne soient pas
accidentellement changés à l'extérieur de votre classe.
|
 |
 |
 |
When you press the onOff button it
toggles the runFlag inside the SeparateSubTask object. That thread
(when it looks at the flag) can then start and stop itself. Pressing the
onOff button produces an apparently instant response. Of course, the
response isn’t really instant, not like that of a system that’s
driven by interrupts. The counter stops only when the thread has the CPU and
notices that the flag has changed.
|
 |
Quand vous pressez le bouton onOff, runFlag est
inversé dans l'objet SeparateSubTask. Ce thread (quand il regarde le flag) peut se
démarrer ou s'arrêter tout seul. Presser le bouton onOff produit un temps de
réponse apparent. Bien sûr, la réponse n'est pas vraiment instantanée
contrairement à ce qui se passe sur un système fonctionnant par interruptions. Le
compteur ne s'arrête que lorsque le thread a le CPU et s'aperçoit que le flag a
changé.
|
 |
 |
 |
You can see that the
inner class
SeparateSubTask is private, which means that its fields and
methods can be given default access (except for run( ), which must
be public since it is public in the base class). The private
inner class is not accessible to anyone but Counter2, and the two
classes are tightly coupled. Anytime you notice classes that appear to have high
coupling with each other, consider the coding and maintenance improvements you
might get by using inner
classes.
|
 |
Vous pouvez voir que la classe interne SeparateSubTaskest
private, ce qui signifie que l'on peut donner à ces champs et méthodes
l'accès par défaut (excepté pour run() qui doit être
public puisque elle est public dans la classe de base). La classe interne
private ne peut être accéder par personne sauf Counter2, les deux
classes sont ainsi fortement couplés. Chaque fois que vous constatez que deux classes
apparaissent comme fortement couplées, vous remarquerez le gain en codage et en maintenance
que les classes internes peuvent apportées.
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |