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
t
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
t t t
14: Multiple Threads t 14) Les Threads multiples
t t t
Objects provide a way to divide a program into independent sections. Often, you also need to turn a program into separate, independently running subtasks.
t 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.
t t t
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.
t 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.
t t t
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.
t 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.
t t t
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.
t 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]
t t t

Responsive user interfaces

t

Interfaces utilisateurs dynamiques [Responsive user interfaces]

t t t
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:
t 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] :
t t t
//: 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); } } ///:~ t
//: 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);
  }
} ///:~
t t t
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.
t À 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.
t t t
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.
t 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()
t t t
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.
t 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.
t t t
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.)
t 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.)
t t t
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.
t 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.
t t t
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.
t 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.
t t t
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.
t 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.
t t t

Inheriting from Thread

t

Héritage de Thread

t t t
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.
t 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.
t t t
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).
t 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).
t t t
//: 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"); } } ///:~ t
//: 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");
  }
} ///:~
t t t
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.
t 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.
t t t
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.
t 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é.
t t t
The output for one run of this program (it will be different from one run to another) is:
t La sortie d'une exécution de ce programme (qui peut être différente d'un exécution à l'autre) est:
t t t
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) t
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)
t t t
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.
t 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].
t t t
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.
t 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.
t t t
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.
t 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.
t t t

Threading for a responsive interface

t

Threading pour une une interface réactive

t t t
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:
t 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:
t t t
//: 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); } } ///:~ t
//: 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);
  }
} ///:~
t t t
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.
t 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.
t t t
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.
t 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.
t t t
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.
t 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.
t t t
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.
t 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é.
t t t
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.
t 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.
t t t
t t t
t t
    
///
t t t
t
     
Sommaire Le site de Bruce Eckel