t
t
t
t
t t 14) Les Threads multiples
tttt
14) Les « Threads » multiples
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
///:Continuing
/////////// Blocking via wait() ///////////
class WaitNotify1 extends Blockable {
  public WaitNotify1(Container c) { super(c); }
  public synchronized void run() {
    while(true) {
      i++;
      update();
       try {
        wait(1000);
      } catch(InterruptedException e) {
        System.err.println("Interrupted");
      }
    }
  }
}

class WaitNotify2 extends Blockable {
  public WaitNotify2(Container c) {
    super(c);
    new Notifier(this);
  }
  public synchronized void run() {
    while(true) {
      i++;
      update();
       try {
        wait();
      } catch(InterruptedException e) {
        System.err.println("Interrupted");
      }
    }
  }
}

class Notifier extends Thread {
  private WaitNotify2 wn2;
  public Notifier(WaitNotify2 wn2) {
    this.wn2 = wn2;
    start();
  }
  public void run() {
    while(true) {
       try {
        sleep(2000);
      } catch(InterruptedException e) {
        System.err.println("Interrupted");
      }
      synchronized(wn2) {
        wn2.notify();
      }
    }
  }
} ///:Continued
t
///:Continuing
/////////// Blocqué via wait() ///////////
class WaitNotify1 extends Blockable {
  public WaitNotify1(Container c) { super(c); }
  public synchronized void run() {
    while(true) {
      i++;
      update();
       try {
        wait(1000);
      } catch(InterruptedException e) {
        System.err.println("Interrupted");
      }
    }
  }
}

class WaitNotify2 extends Blockable {
  public WaitNotify2(Container c) {
    super(c);
    new Notifier(this);
  }
  public synchronized void run() {
    while(true) {
      i++;
      update();
       try {
        wait();
      } catch(InterruptedException e) {
        System.err.println("Interrupted");
      }
    }
  }
}

class Notifier extends Thread {
  private WaitNotify2 wn2;
  public Notifier(WaitNotify2 wn2) {
    this.wn2 = wn2;
    start();
  }
  public void run() {
    while(true) {
       try {
        sleep(2000);
      } catch(InterruptedException e) {
        System.err.println("Interrupted");
      }
      synchronized(wn2) {
        wn2.notify();
      }
    }
  }
}  ///:Continued
t t t
wait( ) is typically used when you’ve gotten to the point where you’re waiting for some other condition, under the control of forces outside your thread, to change and you don’t want to idly wait by inside the thread. So wait( ) allows you to put the thread to sleep while waiting for the world to change, and only when a notify( ) or notifyAll( ) occurs does the thread wake up and check for changes. Thus, it provides a way to synchronize between threads. t wait() est typiquement utilisé quand vous êtes arrivé à un point où vous attendez qu'une autre condition, sous le contrôle de forces extérieures à votre thread, change et que vous ne voulez pas attendre activement à l'intérieur du thread. Donc wait() vous autorise à mettre votre thread en sommeil en attendant que le monde change, et c'est seulement quand un notify() ou notifyAll() arrive que le thread se réveille et controle les changements. Ainsi, on dispose d'un moyen de synchronization entre les threads.
t t t

Blocking on I/O

t

Bloqué sur I/O

t t t
If a stream is waiting for some I/O activity, it will automatically block. In the following portion of the example, the two classes work with generic Reader and Writer objects, but in the test framework a piped stream will be set up to allow the two threads to safely pass data to each other (which is the purpose of piped streams). t Si un flux est en attente de l'activité d'une I/O, il se bloquera automatiquement. Dans la portion suivante de l'exemple, les deux classes travaillent avec les objets génériques Reader et Writer, mais dans le framework de test un piped stream sera créé afin de permettre au deux threads de se passer des données de façon sûre (ce qui est le but des piped streams).
t t t
The Sender puts data into the Writer and sleeps for a random amount of time. However, Receiver has no sleep( ), suspend( ), or wait( ). But when it does a read( ) it automatically blocks when there is no more data. t Le Sender place des données dans le Writer et s'endort pour un temps tiré au hasard. Cependant Receiver n'a pas de sleep(), suspend(), ou wait(). Mais quand il appelle read() il se bloque automatiquement quand il n'y a pas d'autre données.
t t t
///:Continuing
class Sender extends Blockable { // send
  private Writer out;
  public Sender(Container c, Writer out) {
    super(c);
    this.out = out;
  }
  public void run() {
    while(true) {
      for(char c = 'A'; c <= 'z'; c++) {
        try {
          i++;
          out.write(c);
          state.setText("Sender sent: "
            + (char)c);
          sleep((int)(3000 * Math.random()));
        } catch(InterruptedException e) {
          System.err.println("Interrupted");
        } catch(IOException e) {
          System.err.println("IO problem");
        }
      }
    }
  }
}

class Receiver extends Blockable {
  private Reader in;
  public Receiver(Container c, Reader in) {
    super(c);
    this.in = in;
  }
  public void run() {
    try {
      while(true) {
        i++; // Show peeker it's alive
        // Blocks until characters are there:
        state.setText("Receiver read: "
          + (char)in.read());
      }
    } catch(IOException e) {
      System.err.println("IO problem");
    }
  }
} ///:Continued
t
///:Continuing
class Sender extends Blockable { color="#009900">// envoie
  private Writer out;
  public Sender(Container c, Writer out) {
    super(c);
    this.out = out;
  }
  public void run() {
    while(true) {
      for(char c = 'A'; c <= 'z'; c++) {
        try {
          i++;
          out.write(c);
          state.setText("Sender sent: "
            + (char)c);
          sleep((int)(3000 * Math.random()));
        } catch(InterruptedException e) {
          System.err.println("Interrupted");
        } catch(IOException e) {
          System.err.println("IO problem");
        }
      }
    }
  }
}

class Receiver extends Blockable {
  private Reader in;
  public Receiver(Container c, Reader in) {
    super(c);
    this.in = in;
  }
  public void run() {
    try {
      while(true) {
        i++; // Montre que peeker est en vie
        // Bloque jusqu'à ce que les caractères soient là:
        state.setText("Receiver read: "
          + (char)in.read());
      }
    } catch(IOException e) {
      System.err.println("IO problem");
    }
  }
} ///:Continued
t t t
Both classes also put information into their state fields and change i so the Peeker can see that the thread is running. t Les deux classes placent également des informations dans leurs champs state et change i afin que le Peeker puisse voir que le thread tourne.
t t t

Testing

t

Tester

t t t
The main applet class is surprisingly simple because most of the work has been put into the Blockable framework. Basically, an array of Blockable objects is created, and since each one is a thread, they perform their own activities when you press the “start” button. There’s also a button and actionPerformed( ) clause to stop all of the Peeker objects, which provides a demonstration of the alternative to the deprecated (in Java 2) stop( ) method of Thread. t La classe principale de l'applet est étonnamment simple parce la majorité du travail a été mis dans le framework Blockable. En fait, un tableau d'objets Blockable est créé, et puisque chacun est un thread, il réalise leur propre activité quand vous pressez le bouton « start ». Il y a aussi un bouton et une clause actionPerformed() pour stopper tout les objets Peeker, qui donne une démonstration de de l'alternative à la méthode dépréciée stop() de Thread.
t t t
To set up a connection between the Sender and Receiver objects, a PipedWriter and PipedReader are created. Note that the PipedReader in must be connected to the PipedWriter out via a constructor argument. After that, anything that’s placed in out can later be extracted from in, as if it passed through a pipe (hence the name). The in and out objects are then passed to the Receiver and Sender constructors, respectively, which treat them as Reader and Writer objects of any type (that is, they are upcast). t Pour établir la connexion entre les objets Sender et Receiver, un PipedWriter et un PipedReader sont créés. Notez que le PipedReader in doit être connecté au PipedWriter out via un argument du constructeur. Après ça, les données placées dans out peuvent être extraites de in, comme si elles passaient dans un tube (d'où le nom) ([NDT: un pipe est un tube en anglais]). Les objets in et out sont alors passés respectivement aux constructeurs de Receiver et Sender, qui les traitent comme des objets Reader et Writer (ils sont upcast).
t t t
The array of Blockable references b is not initialized at its point of definition because the piped streams cannot be set up before that definition takes place (the need for the try block prevents this). t Le tableau de références Blockable b n'est pas initialisé à son point de définition parce que les piped streams ne peuvent pas être établis avant cette définition (l'utilisation du bloc try évite cela).
t t t
///:Continuing
/////////// Testing Everything ///////////
public class Blocking extends JApplet {
  private JButton
    start = new JButton("Start"),
    stopPeekers = new JButton("Stop Peekers");
  private boolean started = false;
  private Blockable[] b;
  private PipedWriter out;
  private PipedReader in;
  class StartL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(!started) {
        started = true;
        for(int i = 0; i < b.length; i++)
          b[i].start();
      }
    }
  }
  class StopPeekersL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      // Demonstration of the preferred
      // alternative to Thread.stop():
      for(int i = 0; i < b.length; i++)
        b[i].stopPeeker();
    }
  }
  public void init() {
     Container cp = getContentPane();
     cp.setLayout(new FlowLayout());
     out = new PipedWriter();
    try {
      in = new PipedReader(out);
    } catch(IOException e) {
      System.err.println("PipedReader problem");
    }
    b = new Blockable[] {
      new Sleeper1(cp),
      new Sleeper2(cp),
      new SuspendResume1(cp),
      new SuspendResume2(cp),
      new WaitNotify1(cp),
      new WaitNotify2(cp),
      new Sender(cp, out),
      new Receiver(cp, in)
    };
    start.addActionListener(new StartL());
    cp.add(start);
    stopPeekers.addActionListener(
      new StopPeekersL());
    cp.add(stopPeekers);
  }
  public static void main(String[] args) {
    Console.run(new Blocking(), 350, 550);
  }
} ///:~
t
///:Continuing
/////////// Test de tout ///////////
public class Blocking extends JApplet {
  private JButton
    start = new JButton("Start"),
    stopPeekers = new JButton("#004488">"Stop Peekers");
  private boolean started = false;
  private Blockable[] b;
  private PipedWriter out;
  private PipedReader in;
  class StartL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(!started) {
        started = true;
        for(int i = 0; i < b.length; i++)
          b[i].start();
      }
    }
  }
  class StopPeekersL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      // Demonstration de la meilleure
      // alternative à Thread.stop():
      for(int i = 0; i < b.length; i++)
        b[i].stopPeeker();
    }
  }
  public void init() {
     Container cp = getContentPane();
     cp.setLayout(new FlowLayout());
     out = new PipedWriter();
    try {
      in = new PipedReader(out);
    } catch(IOException e) {
      System.err.println("PipedReader problem");
    }
    b = new Blockable[] {
      new Sleeper1(cp),
      new Sleeper2(cp),
      new SuspendResume1(cp),
      new SuspendResume2(cp),
      new WaitNotify1(cp),
      new WaitNotify2(cp),
      new Sender(cp, out),
      new Receiver(cp, in)
    };
    start.addActionListener(new StartL());
    cp.add(start);
    stopPeekers.addActionListener(
      new StopPeekersL());
    cp.add(stopPeekers);
  }
  public static void main(String[] args) {
    Console.run(new Blocking(), 350, 550);
  }
} ///:~
t t t
In init( ), notice the loop that moves through the entire array and adds the state and peeker.status text fields to the page. t Dans init(), notez la boucle qui parcourt la totalité du tableau et ajoute les champs state et peeker.status à la page.
t t t
When the Blockable threads are initially created, each one automatically creates and starts its own Peeker. So you’ll see the Peekers running before the Blockable threads are started. This is important, as some of the Peekers will get blocked and stop when the Blockable threads start, and it’s essential to see this to understand that particular aspect of blocking. t Quand les threads Blockable sont initialement créés, chacune crée et démarre automatiquement son propre Peeker. Donc vous verrez les Peekers tournés avant que les threads Blockable ne démarrent. C'est important, puisque certains des Peekers seront bloqués et stoperont quand les threads Blockable démarreront, et c'est essentiel de voir et de comprendre cet aspect particulier du blocage.
t t t

Deadlock

t

Interblocage [Deadlock]

t t t
Because threads can become blocked and because objects can have synchronized methods that prevent threads from accessing that object until the synchronization lock is released, it’s possible for one thread to get stuck waiting for another thread, which in turn waits for another thread, etc., until the chain leads back to a thread waiting on the first one. You get a continuous loop of threads waiting on each other and no one can move. This is called deadlock. The claim is that it doesn’t happen that often, but when it happens to you it’s frustrating to debug. t Puisse que les threads peuvent être bloqués et puisse que les objets peuvent avoir des méthodes synchronized qui empêchent les threads d'accéder à cet objet jusqu'à ce que le verrou de synchronisation soit libéré, il est possible pour un thread de rester coincé attendant un autre thread, qui à son tour attend un autre thread, etc., jusqu'à ce que la chaîne ramène à un thread en attente sur le premier. Vous obtenez une boucle continue de threads s'attendant les uns les autres et aucun ne peut bouger. C'est ce qu'on appelle un interblocage (ou deadlock). Le pire c'est que cela n'arrive pas souvent, mais quand cela vous arrive c'est frustrant à déboguer.
t t t
There is no language support to help prevent deadlock; it’s up to you to avoid it by careful design. These are not comforting words to the person who’s trying to debug a deadlocking program. t Il n'y a pas de support du langage pour aider à éviter les interblocages; c'est à vous de les éviter en faisant attention à la conception. Ce ne sont pas des mots pour rassurer la personne qui essaie de déboguer un programme générant des inter-blocages.
t t t

The deprecation of stop( ), suspend( ), resume( ), and destroy( ) in Java 2

t

La dépréciation de stop(), suspend(), resume(), et destroy() en Java 2

t t t
One change that has been made in Java 2 to reduce the possibility of deadlock is the deprecation of Thread’s stop( ), suspend( ), resume( ), and destroy( ) methods. t Un changement qui a été fait dans Java 2 pour réduire les possibilités d'inter-blocage est la dépréciation des méthodes de Thread&rsquo; stop(), suspend(), resume(), et destroy().
t t t
The reason that the stop( ) method is deprecated is because it doesn’t release the locks that the thread has acquired, and if the objects are in an inconsistent state (“damaged”) other threads can view and modify them in that state. The resulting problems can be subtle and difficult to detect. Instead of using stop( ), you should follow the example in Blocking.java and use a flag to tell the thread when to terminate itself by exiting its run( ) method. t La méthode stop() est dépréciée parce qu'elle ne libère pas les verrous que le thread a acquis, et si les objets sont dans un état inconsistent (« damaged ») les autres threads peuvent les voir et les modifiés dans cet état. Le problème résultant peut être subtil et difficile à détecter. Plutôt que d'utiliser stop(), vous devriez suivre l'exemple de Blocking.java et utiliser un drapeau pour dire au thread quand se terminer en sortant de sa méthode run().
t t t
There are times when a thread blocks—such as when it is waiting for input—and it cannot poll a flag as it does in Blocking.java. In these cases, you still shouldn’t use stop( ), but instead you can use the interrupt( ) method in Thread to break out of the blocked code: t Il existe des situations où un thread se bloque — comme quand il attend une entrée — mais il ne peut pas positionner un drapeau comme il le fait dans Blocking.java. Dans ces cas, vous ne devriez pas utiliser stop(), mais plutôt la méthode interrupt() de Thread pour écrire le code bloquant:
t t t
//: c14:Interrupt.java
// The alternative approach to using
// stop() when a thread is blocked.
// <applet code=Interrupt width=200 height=100>
// </applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.bruceeckel.swing.*;

class Blocked extends Thread {
  public synchronized void run() {
    try {
      wait(); // Blocks
    } catch(InterruptedException e) {
      System.err.println("Interrupted");
    }
    System.out.println("Exiting run()");
  }
}

public class Interrupt extends JApplet {
  private JButton
    interrupt = new JButton("Interrupt");
  private Blocked blocked = new Blocked();
  public void init() {
    Container cp = getContentPane();
    cp.setLayout(new FlowLayout());
    cp.add(interrupt);
    interrupt.addActionListener(
      new ActionListener() {
        public
        void actionPerformed(ActionEvent e) {
          System.out.println("Button pressed");
          if(blocked == null) return;
          Thread remove = blocked;
          blocked = null; // to release it
          remove.interrupt();
        }
      });
    blocked.start();
  }
  public static void main(String[] args) {
    Console.run(new Interrupt(), 200, 100);
  }
} ///:~
t
//: c14:Interrupt.java
// L'approche alternative pour utiliser
// stop() quand un thread est bloqué.
// <applet code=Interrupt width=200 height=100>
// </applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.bruceeckel.swing.*;

class Blocked extends Thread {
  public synchronized void run() {
    try {
      wait(); // Bloque
    } catch(InterruptedException e) {
      System.err.println("Interrupted");
    }
    System.out.println("Exiting run()");
  }
}

public class Interrupt extends JApplet {
  private JButton
    interrupt = new JButton("Interrupt");
  private Blocked blocked = new Blocked();
  public void init() {
    Container cp = getContentPane();
    cp.setLayout(new FlowLayout());
    cp.add(interrupt);
    interrupt.addActionListener(
      new ActionListener() {
        public
        void actionPerformed(ActionEvent e) {
          System.out.println("Button pressed");
          if(blocked == null) color="#0000ff">return;
          Thread remove = blocked;
          blocked = null; // pour le libérer
          remove.interrupt();
        }
      });
    blocked.start();
  }
  public static void main(String[] args) {
    Console.run(new Interrupt(), 200, 100);
  }
} ///:~
t t t
The wait( ) inside Blocked.run( ) produces the blocked thread. When you press the button, the blocked reference is set to null so the garbage collector will clean it up, and then the object’s interrupt( ) method is called. The first time you press the button you’ll see that the thread quits, but after that there’s no thread to kill so you just see that the button has been pressed. t Le wait() dans Blocked.run() produit le thread bloqué. Quand vous pressez le bouton, la référence blocked est placée à null donc le garbage collector le nettoiera, la méthode interrupt() de l'objet est alors appelée. La première fois que vous pressez le bouton vous verrez le thread sortir, mais ensuite il n'y a plus de thread à tuer donc vous voyez juste que le bouton a été pressé.
t t t
The suspend( ) and resume( ) methods turn out to be inherently deadlock-prone. When you call suspend( ), the target thread stops but it still holds any locks that it has acquired up to that point. So no other thread can access the locked resources until the thread is resumed. Any thread that wants to resume the target thread and also tries to use any of the locked resources produces deadlock. You should not use suspend( ) and resume( ), but instead put a flag in your Thread class to indicate whether the thread should be active or suspended. If the flag indicates that the thread is suspended, the thread goes into a wait using wait( ). When the flag indicates that the thread should be resumed the thread is restarted with notify( ). An example can be produced by modifying Counter2.java. Although the effect is similar, you’ll notice that the code organization is quite different—anonymous inner classes are used for all of the listeners and the Thread is an inner class, which makes programming slightly more convenient since it eliminates some of the extra bookkeeping necessary in Counter2.java: t Les méthodes suspend() et resume() finissent par être sujettes aux inter-blocages. Quand vous appelez suspend(), le thread cible s'arrête mais il conserve les verrous qu'il a acquis à ce point. Ainsi aucuns autres threads ne peut accéder aux ressources verrouillées jusqu'à ce que le thread soit redémarré. Un thread qui veut redémarrer le thread cible et aussi essaye d'utiliser une des ressources verrouillées produit un inter-blocage. Vous ne devriez pas utiliser suspend() et resume(), mais plutôt mettre un drapeau dans votre classe Thread pour indiqué si le thread devrait être actif ou suspendu. Si le drapeau indique que le thread est suspendu, le thread rentre dans un wait(). Quand le drapeau indique que le thread devrait être redémarré le thread est réactivé avec notify(). Un exemple peut être produit en modifiant Counter2.java. Alors que l'effet est similaire, vous remarquerez que l'organisation du code est assez différente —  des classes internes anonymes sont utilisées pour tous les listeners et le Thread est une classe interne, ce qui rend la programmation légèrement plus convenable puisqu'on élimine certaines complexités nécessaires dans Counter2.java:
t t t
//: c14:Suspend.java
// The alternative approach to using suspend()
// and resume(), which are deprecated in Java 2.
// <applet code=Suspend width=300 height=100>
// </applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.bruceeckel.swing.*;

public class Suspend extends JApplet {
  private JTextField t = new JTextField(10);
  private JButton
    suspend = new JButton("Suspend"),
    resume = new JButton("Resume");
  private Suspendable ss = new Suspendable();
  class Suspendable extends Thread {
    private int count = 0;
    private boolean suspended = false;
    public Suspendable() { start(); }
    public void fauxSuspend() {
      suspended = true;
    }
    public synchronized void fauxResume() {
      suspended = false;
      notify();
    }
    public void run() {
      while (true) {
        try {
          sleep(100);
          synchronized(this) {
            while(suspended)
              wait();
          }
        } catch(InterruptedException e) {
          System.err.println("Interrupted");
        }
        t.setText(Integer.toString(count++));
      }
    }
  }
  public void init() {
    Container cp = getContentPane();
    cp.setLayout(new FlowLayout());
    cp.add(t);
    suspend.addActionListener(
      new ActionListener() {
        public
        void actionPerformed(ActionEvent e) {
          ss.fauxSuspend();
        }
      });
    cp.add(suspend);
    resume.addActionListener(
      new ActionListener() {
        public
        void actionPerformed(ActionEvent e) {
          ss.fauxResume();
        }
      });
    cp.add(resume);
  }
  public static void main(String[] args) {
    Console.run(new Suspend(), 300, 100);
  }
} ///:~
t
//: c14:Suspend.java
// L'approche alternative à l'utilisation de suspend()
// et resume(), qui sont déprecié dans Java 2.
// <applet code=Suspend width=300 height=100>
// </applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import com.bruceeckel.swing.*;

public class Suspend extends JApplet {
  private JTextField t = new JTextField(10);
  private JButton
    suspend = new JButton("Suspend"),
    resume = new JButton("Resume");
  private Suspendable ss = new Suspendable();
  class Suspendable extends Thread {
    private int count = 0;
    private boolean suspended = color="#0000ff">false;
    public Suspendable() { start(); }
    public void fauxSuspend() {
      suspended = true;
    }
    public synchronized void fauxResume() {
      suspended = false;
      notify();
    }
    public void run() {
      while (true) {
        try {
          sleep(100);
          synchronized(this) {
            while(suspended)
              wait();
          }
        } catch(InterruptedException e) {
          System.err.println("Interrupted");
        }
        t.setText(Integer.toString(count++));
      }
    }
  }
  public void init() {
    Container cp = getContentPane();
    cp.setLayout(new FlowLayout());
    cp.add(t);
    suspend.addActionListener(
      new ActionListener() {
        public
        void actionPerformed(ActionEvent e) {
          ss.fauxSuspend();
        }
      });
    cp.add(suspend);
    resume.addActionListener(
      new ActionListener() {
        public
        void actionPerformed(ActionEvent e) {
          ss.fauxResume();
        }
      });
    cp.add(resume);
  }
  public static void main(String[] args) {
    Console.run(new Suspend(), 300, 100);
  }
} ///:~
t t t
The flag suspended inside Suspendable is used to turn suspension on and off. To suspend, the flag is set to true by calling fauxSuspend( ) and this is detected inside run( ). The wait( ), as described earlier in this chapter, must be synchronized so that it has the object lock. In fauxResume( ), the suspended flag is set to false and notify( ) is called—since this wakes up wait( ) inside a synchronized clause the fauxResume( ) method must also be synchronized so that it acquires the lock before calling notify( ) (thus the lock is available for the wait( ) to wake up with). If you follow the style shown in this program you can avoid using suspend( ) and resume( ). t Le drapeau suspended dans Suspendable est utilisé pour activer ou désactiver la suspension. Pour suspendre, le drapeau est placé à true en appelant fauxSuspend() et ceci est détecté dans run(). Le wait(), comme décrit plus haut dans ce chapitre, doit être synchronized afin qu'il ait le verrou de l'objet. Dans fauxResume(), le drapeau suspended est placé à false et notify() est appelé — puisque ceci reveille wait() dans une clause synchronized la méthode fauxResume() doit aussi être synchronized afin qu'elle acquiert le verrou avant d'appeler notify() (ainsi le verrou est libre pour que le wait() se réveille avec). Si vous suivez le style montré dans ce programme vous pouvez éviter d'utiliser suspend() et resume().
t t t
The destroy( ) method of Thread has never been implemented; it’s like a suspend( ) that cannot resume, so it has the same deadlock issues as suspend( ). However, this is not a deprecated method and it might be implemented in a future version of Java (after 2) for special situations in which the risk of a deadlock is acceptable. t La méthode destroy() de Thread n'a jamais été implémenter; c'est comme un suspend() qui ne peut pas être réactivé, donc elle a les mêmes problèmes d'inter-blocage que suspend(). Toutefois, ce n'est pas une méthode dépréciée et elle devrait être implémentée dans une future version de Java (après la 2) pour des situations spéciales où le risque d'inter-blocage est acceptable.
t t t
You might wonder why these methods, now deprecated, were included in Java in the first place. It seems an admission of a rather significant mistake to simply remove them outright (and pokes yet another hole in the arguments for Java’s exceptional design and infallibility touted by Sun marketing people). The heartening part about the change is that it clearly indicates that the technical people and not the marketing people are running the show—they discovered a problem and they are fixing it. I find this much more promising and hopeful than leaving the problem in because “fixing it would admit an error.” It means that Java will continue to improve, even if it means a little discomfort on the part of Java programmers. I’d rather deal with the discomfort than watch the language stagnate. t Vous devez vous demander pourquoi ces méthodes, maintenant dépréciées, étaient incluses dans Java dans un premier temps. Il semblerait admissible qu'une erreur significative soit simplement supprimée (et donne encore un autre coup aux arguments pour l'exceptionnel conception et l'infaillibilité claironné par les commerciaux de Sun). La partie réconfortante à propos des changements est que cela indique clairement que ce sont les techniciens et non les commerciaux qui dirige le show — ils découvrent un problème et ils le fixent. Je trouve cela beaucoup plus promettant et encourageant que de laisser le problème parce que « fixer le problème serait admettre une erreur. » Cela signifie que Java continuera à évoluer, même si cela signifie une petite perte de confort pour les programmeurs Java. Je préfère accepter cet inconvénient plutôt que de voir le langage stagné.
t t t
t t t
t t
\\\
///
t t t
t
     
Sommaire Le site de Bruce Eckel