Threads

Java est un langage dit multithreads, ce qui signifie qu'a un instant donné une application peut avoir plusieurs threads qui s'exécutent. Un thread est un système d'exécution séparé dans un même programme. Les threads fournissent les moyens à une application d'effectuer beaucoup de tâches différentes en même temps. Si une application effectue n'importe quel type de traitement important, comme une animation, un calcul long, il sera bien de créer des threads distincts pour chacun de ces traitement.

Conceptuellement les threads ressemblent à des processus, à la différence que plusieurs threads partagent le même espace d'adressage, se qui signifie qu'ils peuvent partager des variables et des méthodes (en possédant en même temps ses propres variables locales).

Les threads sont plus économes en ressources si on les compare au processus, donc il est concevable pour une seule application de lancer des centaines de threads en même temps

 

Creation d'une Thread

            Dériver la classe Thread
La programmation avec les threads a des particularités surtout avec la synchronisation des threads. Java a des utiles simples pour résoudre bon nombre de problèmes.

Les threads sont créés et contrôlé par les objets de la classe Thread.

Chaque objet de ce classe correspond à un seul thread. Il contient des méthodes pour lancer, contrôler et arrêter l'exécution du thread. Pour lancer l'exécution  d'un thread on doit appeler la méthode start() de la classe Thread. Une fois que le thread est lancé, il continue à s'exécuter jusqu'à la fin du traitement (indiqué par la méthode run() ) ou jusqu'à ce que la méthode stop() le termine.

Le traitement qui doit être effectué par le thread est indiqué par une méthode appelée run(). On a deux possibilités pour spécifier la méthode run(). Premièrement, la classe Thread elle-même possède une méthode run(). On peut dériver la classe Thread et de redéfinir sa méthode run() pour faire ce que l'on veut. Dans ce cas on crée une instance de la classe dérivée et on lance sa méthode start().

        Implémenter Runnable
Il n'est pas toujours possible de créer une classe dérivée de la classe Thread. Par exemple une applet doit être dérivée de la classe Applet, donc elle ne peut pas être dérivée en même temps de la classe Thread. Dans ce cas on a besoin d'indiquer à la classe Thread quel objet possède la méthode run() qu'il doit exécuter. La classe Thread possède un constructeur qui prend comme argument une référence sur un objet. Si on crée un objet thread en utilisant ce constructeur et si on appelle sa méthode start() le thread va exécuter la méthode run() de l'objet passé en paramètre plutôt que la sienne. Pour effectuer cette opération, Java a besoin d'avoir garantie que l'objet que nous passons en paramètre contient une méthode run().

 
Création

o La classe java. lang. Thread permet de créer de nouveaux threads
o
un thread doit implémenter obligatoirement l’interface
Runnable
        - le code exécuté se situe dans sa méthode
run()
o
2 méthodes pour créer un Thread :
    - 1) une classe qui dérive de
java. lang. Thread
            --
java. lang. Thread implémente Runnable
            -- il faut redéfinir la méthode
run()
    - 2) une classe qui implémente l’interface
Runnable
            -- il faut implémenter la méthode
run()

Sous- classer Thread

o Méthode 1 : une sous- classe de Thread

class Proc1 extends Thread {
Proc1() {...} // Le constructeur
...

public void
run() {

... // Ici ce que fait le processus : boucle infinie
}
}
...

Proc1 p1 = new Proc1();
// Création du processus p1
p1
.start(); // Demarre le processus et execute p1. run()

Implémenter Runnable

o Méthode 2 : une classe qui implémente Runnable

class Proc2 implements Runnable {
Proc2() { ...} // Constructeur
...

public void
run() {

... // Ici ce que fait le processus
}
}
...

Proc2 p = new Proc2();
Thread p2 = new Thread( p);

...

p2
.start(); // Démarre un processus qui execute p. run()

Quelle solution choisir ?

o Méthode 1 : sous- classer Thread
   
- lorsqu’on désire paralélliser une classe qui n’hérite pas déjà
   
   d’une autre classe (attention : héritage simple)
   
- cas des applications autonomes
o
Méthode 2 : implémenter Runnable

   
- lorsqu’une super- classe est imposée
   
- cas des applets

public class MyThreadApplet extends Applet implements Runnable {}

 

Etats d'un thread

 4 états:  New , Runnable, Blocked, Dead .

  

 
 

 

Pour chaque passage dans Blocked il existe une récuperation  spécifique: suspend() - resume,  wait()-notify()

Méthode isAlive()

La classe Thread possede un méthode isAlive()

Méthode getState()

Après release 5.0 existe la méthode Thread.getState() qui a pour résultat une de  cettes Thread.State valeurs:

Une exemple simple
 

//: SimpleThread.java
public class SimpleThread extends Thread {
    private int countDown = 5;
    private String name;
    private static int threadCount =  0;
    public SimpleThread(String nameP) {
         name = nameP;
         System.out.println("Making " + name);
    }
    public void run() {
          for( ;countDown>0; countDown--) {
                System.out.println("Thread " + name + "(" + countDown + ")");
          }
          System.out.println("Thread " + name + " end");
    }
    public static void main(String[] args) {
         String nameA[]={"Nick", "Marie", "George", "Isabelle", "Pierre"};
         for(int i = 0; i < 5; i++)
                new SimpleThread(nameA[i]).start();
          System.out.println("All Threads Started");
    }
}

 

Making Nick
Making Marie
Making George
Making Isabelle
Making Pierre
All Threads Started
Thread Nick(5)
Thread Nick(4)
Thread Nick(3)
Thread Nick(2)
Thread Nick(1)
Thread Nick end
Thread Marie(5)
Thread Marie(4)
Thread Marie(3)
Thread Marie(2)
Thread Marie(1)
Thread Marie end
Thread George(5)
Thread George(4)
Thread George(3)
Thread George(2)
Thread George(1)
Thread George end
Thread Isabelle(5)
Thread Isabelle(4)
Thread Isabelle(3)
Thread Isabelle(2)
Thread Isabelle(1)
Thread Isabelle end
Thread Pierre(5)
Thread Pierre(4)
Thread Pierre(3)
Thread Pierre(2)
Thread Pierre(1)
Thread Pierre end

//: SimpleThread.java
public class ST extends Thread {
    private int countDown = 5;
    public String name;
    private static int threadCount =  0;
    public ST(String nameP) {
         name = nameP;
         System.out.println("Making " + name);
    }
    public void run() {
          for( ;countDown>0; countDown--) {
                System.out.println("Thread " + name + "(" + countDown + ")");
          }
          System.out.println("Thread " + name + " end");
    }
    public static void main(String[] args) {
         String nameA[]={"Nick", "Marie", "George", "Isabelle", "Pierre"};
         Thread t[] = new Thread[nameA.length];
         for(int i = 0; i < 5; i++){
                t[i]=new ST(nameA[i]);
                for(int j=0;j<=i;j++)
                    System.out.println("\t\tThread " + j+":"+t[j].getState());
                t[i].start();
         }
          System.out.println("All Threads Started");
    }

 

Making Nick
        Thread 0:NEW
Making Marie
        Thread 0:RUNNABLE
        Thread 1:NEW
Thread Nick(5)
Thread Nick(4)
Thread Marie(5)
Thread Marie(4)
Thread Marie(3)
Thread Marie(2)
Thread Marie(1)
Thread Marie end
Thread Nick(3)
Thread Nick(2)
Thread Nick(1)
Thread Nick end
Making George
        Thread 0:TERMINATED
        Thread 1:TERMINATED
        Thread 2:NEW
Making Isabelle
        Thread 0:TERMINATED
        Thread 1:TERMINATED
        Thread 2:RUNNABLE
        Thread 3:NEW
Thread George(5)
Thread Isabelle(5)
Thread Isabelle(4)
Thread Isabelle(3)
Thread Isabelle(2)
Thread Isabelle(1)
Thread Isabelle end
Making Pierre
        Thread 0:TERMINATED
        Thread 1:TERMINATED
        Thread 2:BLOCKED
        Thread 3:TERMINATED
        Thread 4:NEW
Thread George(4)
Thread George(3)
Thread George(2)
Thread George(1)
Thread George end
All Threads Started
Thread Pierre(5)
Thread Pierre(4)
Thread Pierre(3)
Thread Pierre(2)
Thread Pierre(1)
Thread Pierre end

Avec des priorités:

//: SimpleThread.java
public class St extends Thread {
    private int countDown = 5;
    private String name;
    public St(String nameP) {
         name = nameP;
         System.out.println("Making " + name);
    }
    public void run() {
          for( ;countDown>0; countDown--) {
                System.out.println("Thread " + name + "(" + countDown + ")"+  
                           " priority -> " + getPriority() );
          }
          System.out.println("Thread " + name + " end");
    }
    public static void main(String[] args) {
         String nameA[]={"Nick", "Marie", "George", "Isabelle", "Pierre"};
         for(int i = 0; i < 5; i++)
                new St(nameA[i]).start();
          System.out.println("All Threads Started");
    }

 

Making Nick
Making Marie
Making George
Making Isabelle
Making Pierre
All Threads Started
Thread Marie(5) priority -> 5
Thread Marie(4) priority -> 5
Thread Marie(3) priority -> 5
Thread Marie(2) priority -> 5
Thread Marie(1) priority -> 5
Thread Marie end
Thread Isabelle(5) priority -> 5
Thread Isabelle(4) priority -> 5
Thread Pierre(5) priority -> 5
Thread Nick(5) priority -> 5
Thread George(5) priority -> 5
Thread Isabelle(3) priority -> 5
Thread Pierre(4) priority -> 5
Thread Nick(4) priority -> 5
Thread George(4) priority -> 5
Thread Isabelle(2) priority -> 5
Thread Pierre(3) priority -> 5
Thread Nick(3) priority -> 5
Thread George(3) priority -> 5
Thread Isabelle(1) priority -> 5
Thread Pierre(2) priority -> 5
Thread Nick(2) priority -> 5
Thread George(2) priority -> 5
Thread Isabelle end
Thread Pierre(1) priority -> 5
Thread Nick(1) priority -> 5
Thread George(1) priority -> 5
Thread Pierre end
Thread Nick end
Thread George end

 On peut utiliser un Thread qui travail tous le temps:
 
 
 //: CompteurA.java
// Using the Runnable interface to turn the
// main class into a thread.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

 public class CompteurA extends Applet implements Runnable {
        private int count = 0;
        private boolean runFlag = true;
        private Thread selfThread = null;
        private Button
                  onOff = new Button("Stop"),
                  start = new Button("Start");
        private TextField t = new TextField(10);
        public void init() {
              add(t);
              start.addActionListener(new StartL());
              add(start);
              onOff.addActionListener(new OnOffL());
              add(onOff);
         }
         public void run() {
                 while (true) {
                 try {
                     Thread.sleep(100);
                 } catch (InterruptedException e){}
                  if(runFlag)
                  t.setText(Integer.toString(count++));
          }
  }
  class StartL implements ActionListener {
        public void actionPerformed(ActionEvent e) {
                if(selfThread == null){
                        selfThread = new Thread(CompteurA.this);
                        selfThread.start();
                }
                runFlag = true;
        }
  }
  class OnOffL implements ActionListener {
          public void actionPerformed(ActionEvent e) {
                  runFlag = false;
          }
  }
  public static void main(String[] args) {
          CompteurA applet = new CompteurA();
          Frame aFrame = new Frame("CompteurA");
          aFrame.addWindowListener(
                  new WindowAdapter() {
                            public void windowClosing(WindowEvent e) {
                                    System.exit(0);
                            }
                  }
           );
          aFrame.add(applet, BorderLayout.CENTER);
          aFrame.setSize(300,200);
          applet.init();
          applet.start();
          aFrame.setVisible(true);
    }
}

CompteurA:
 
 


 
 

Ou bien créer et tuer le Thread chaque fois:

//: CompteurA1.java
// Using the Runnable interface to turn the
// main class into a thread.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class CompteurA1 extends Applet implements Runnable {
    private int count = 0;
    private Thread selfThread = null;
    private Button    onOff = new Button("Stop"),
    start = new Button("Start");
    private TextField t = new TextField(10);
    private boolean runFlag=true;
    public void init() {
        add(t);
        start.addActionListener(new StartL());
        add(start);
        onOff.addActionListener(new OnOffL());
        add(onOff);
    }
    public void run() {
        while (runFlag) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e){}
            t.setText(Integer.toString(count++));
        }
    }
    class StartL implements ActionListener {
         public void actionPerformed(ActionEvent e) {
             if(selfThread == null){
                 selfThread = new Thread(CompteurA1.this);
                 runFlag=true;
                 selfThread.start();
             }
         }
    }
    class OnOffL implements ActionListener {
        public void actionPerformed(ActionEvent e) {
             if(selfThread != null) {
                //  selfThread.stop();     deprecated
                 runFlag =false;
                 selfThread = null;
             }
       }
    }
    public static void main(String[] args) {
        CompteurA1 applet = new CompteurA1();
        Frame aFrame = new Frame("CompteurA1");
        aFrame.addWindowListener(  
            new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                     System.exit(0);
               }
            }
        );
        aFrame.add(applet, BorderLayout.CENTER);
        aFrame.setSize(300,200);
        applet.init();
        applet.start();
        aFrame.setVisible(true);
    }
}
 

 

CompteurA1:
 
 

 


 

 
 

Lancement de plusieurs threads
 
 
 //: CompteurPT.java
// If you separate your thread from the main
// class, you can have as many threads as you want.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

class Ticker extends Thread {
  private Button b = new Button("Toggle");
  private TextField t = new TextField(10);
  private int count = 0;
  private boolean runFlag = true;
  public Ticker(Container c) {
     b.addActionListener(new ToggleL());
     Panel p = new Panel();
     p.add(t); p.add(b); c.add(p);
  }
  class ToggleL implements ActionListener {
     public void actionPerformed(ActionEvent e) {
        runFlag = !runFlag;
     }
  }
  public void run() {
     while (true) {
        if(runFlag)
            t.setText(Integer.toString(count++));
        try {
            sleep(100);
         } catch (InterruptedException e){}
    }
  }
  public void stp() {
    runFlag = false;
  }
  public void restart() {
     runFlag = true;
  }
}

public class CompteurPT extends Applet {
  private Button start = new Button("Start");
  private Button stop = new Button("Stop");
  private Button restart = new Button("Restart");
  private boolean started = false;
  private Ticker[] s;
  private boolean isApplet = true;
  private int size;
  public void init() {
     // Get parameter "size" from Web page:
     if(isApplet)
         size = Integer.parseInt(getParameter("size"));
     s = new Ticker[size];
     for(int i = 0; i < s.length; i++)
          s[i] = new Ticker(this);
     start.addActionListener(new StartL());
     add(start);
     stop.addActionListener(new StopL());
     add(stop);
     restart.addActionListener(new RestartL());
     add(restart);
  }
  class StartL implements ActionListener {
      public void actionPerformed(ActionEvent e) {
         if(!started) {
               started = true;
               for(int i = 0; i < s.length; i++) s[i].start();
         }
      }
  }
  class StopL implements ActionListener{
       public void actionPerformed(ActionEvent e) {
            for(int i=0;i<s.length;i++) s[i].stp();
       }
  }
  class RestartL implements ActionListener{
       public void actionPerformed(ActionEvent e){
            for(int i=0; i< s.length; i++) s[i].restart();
      }
  }
  public static void main(String[] args) {
      CompteurPT applet = new CompteurPT();
      // This isn't an applet, so set the flag and
      // produce the parameter values from args:
      applet.isApplet = false;
      applet.size = (args.length == 0 ? 5 : Integer.parseInt(args[0]));
      Frame aFrame = new Frame("CompteurPT");
      aFrame.addWindowListener(
          new WindowAdapter() {
                 public void windowClosing(WindowEvent e) {
                        System.exit(0);
                }
          }
      );
     aFrame.add(applet, BorderLayout.CENTER);
     aFrame.setSize(200, applet.size * 50);
     applet.init();
     applet.start();
     aFrame.setVisible(true);
  }
}

CompteurPT: