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

 Мultiprocessing et Multithreading

Мultiprocessing - deux ou plusieurs programmes qui s'exécutent "visiblement" de manière compétitive sous le contrôle du système d'exploitation. Les programmes n'ont aucun lien entre eux, sauf le fait qu'ils sont démarrés et exécutés simultanément. Il est mis en œuvre par le système d'exploitation et aucune mesure ne doit être prise au sein du programme.

Multithreading - deux ou plusieurs tâches qui sont effectuées "visiblement" en parallèle dans le même programme. Parfois appelés processus « légers »
Bien qu'il nécessite une maintenance du système d'exploitation, il est implémenté et exécuté par le programme lui-même. la conception et la planification de programmes spéciaux sont nécessaires

Caractéristiques

Constructeurs

Thread()
          Allocates a new Thread object.
Thread(String name)
          Allocates a new Thread object.
Thread(Runnable target)
          Allocates a new Thread object.
Thread(Runnable target, String name)
          Allocates a new Thread object.

Quelques méthodes  utilisées souvent

public static Thread   currentThread()
          Returns a reference to the currently executing thread object.
public final String     getName()
          Returns this thread's name.
public final void setName(String name)
          Changes the name of this thread to be equal to the argument name.

3.1 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()

Dériver la  classe 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 {}

 

3.2 Etats d'un thread (Cycle de vie)

 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
 

Dériver la classe Thread

    3.3.1

//: SimpleThread.java
public class SimpleThread extends Thread {
    private int countDown = 5;
    private String name,indent;
    private static String s="";
    public SimpleThread(String nameP) {
         name = nameP;
         indent = (s+="       ");
         System.out.println("Making " + name);
    }
    public void run() {
          for( ;countDown>0; countDown--) {
                System.out.println(indent+"Thread " + name + "(" + countDown + ")");
          }
          System.out.println(indent+"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
       Thread Nick(5)
       Thread Nick(4)
Making George
       Thread Nick(3)
       Thread Nick(2)
       Thread Nick(1)
              Thread Marie(5)
              Thread Marie(4)
Making Isabelle
              Thread Marie(3)
              Thread Marie(2)
                     Thread George(5)
       Thread Nick end
                     Thread George(4)
              Thread Marie(1)
              Thread Marie end
Making Pierre
                     Thread George(3)
                            Thread Isabelle(5)
All Threads Started
                     Thread George(2)
                                   Thread Pierre(5)
                            Thread Isabelle(4)
                                   Thread Pierre(4)
                     Thread George(1)
                                   Thread Pierre(3)
                            Thread Isabelle(3)
                                   Thread Pierre(2)
                                   Thread Pierre(1)
                     Thread George end
                                   Thread Pierre end
                            Thread Isabelle(2)
                            Thread Isabelle(1)
                            Thread Isabelle end


    3.3.2 Dans la fonction main(), on ajoute dans une boucle l'état de chaque  thread créé:




//: SimpleThread.java
public class ST extends Thread {
    private int countDown = 5;
    private String name,indent;
    static private String s="";
    public ST(String nameP) {
        super(nameP);
        name = nameP;
        indent= (s+="        ");
        System.out.println("Making " + name);
    }
    public void run() {
        for( ;countDown>0; countDown--) {
            System.out.println(indent +"Thread " + name + "(" + countDown + ")");
        }
        System.out.println(indent + "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 " + t[j].getName()+":"+t[j].getState());
            t[i].start();
        }
        System.out.println("All Threads Started");
    }
}




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





Le choix de thread à exécuter se fait par la méthode round - robin :

    3.3.3 Avec des priorités

Chaque thread a la priorité (le droit de commencer avant les autres). Les priorités sont représentées par des entiers allant de Thread.MAX_PRIORITY (10 - le plus élevé) à Thread.MIN_PRIORITY (1 - le plus bas). Par défaut, chaque thread a la même priorité que celui qui l'a créé. Une fois la priorité du thread créée, vous pouvez la modifier à l'aide de la méthode setPriority ().

//: 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

    3.3.4 Exemple avec modification des priorités:
import java.text.*;
import java.util.*;

//: SimpleThreadPr.java
public class SimpleThreadPr extends Thread {
    private int countDown = 5;
    private String name;
    private static Date dt = new Date( );
    private static DateFormat df = new SimpleDateFormat("HH:mm:ss:SSS");
    private volatile double d=0; // no optimization
    public SimpleThreadPr(String name, int prior) {
        this.name = name;
        setPriority(prior);
        System.out.println("\t\tMaking " + name);
    }
    public void run() {
        dt.setTime(System.currentTimeMillis( ));
        System.out.println(name+"start at "+df.format(dt)+"pr -> "+getPriority());
        for( ;countDown>0; countDown--) {

            // An expensive, interruptible operation:
            for(int i = 1; i < 100000; i++)
                d = d + (Math.PI + Math.E) / (double)i;

            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","Rose","Salome"};
        SimpleThreadPr st[] = new SimpleThreadPr[nameA.length];
        for(int i = 0; i < nameA.length; i++)
            st[i] = new SimpleThreadPr(nameA[i],i<3?Thread.MAX_PRIORITY:
                Thread.MIN_PRIORITY);
        for(int i = 3; i < nameA.length; i++)
            st[i] .start();
        System.out.println("\t\tThe Threads with low priority started");
        for(int i = 0; i < 3; i++)
            st[i] .start();
        System.out.println("\t\tAll Threads Started");
    }

 

        Making Nick
        Making Marie
        Making George
        Making Isabelle
        Making Pierre
        Making Rose
        Making Salome
        The Threads with low priority started
        All Threads Started
Nickstart at 09:49:37:705pr -> 10
Rosestart at 09:49:37:705pr -> 1
Isabellestart at 09:49:37:705pr -> 1
Pierrestart at 09:49:37:705pr -> 1
Salomestart at 09:49:37:705pr -> 1
Mariestart at 09:49:37:705pr -> 10
Georgestart at 09:49:37:705pr -> 10
Thread Nick(5) priority -> 10
Thread Marie(5) priority -> 10
Thread George(5) priority -> 10
Thread Isabelle(5) priority -> 1
Thread Marie(4) priority -> 10
Thread Pierre(5) priority -> 1
Thread Rose(5) priority -> 1
Thread Salome(5) priority -> 1
Thread George(4) priority -> 10
Thread Nick(4) priority -> 10
Thread Marie(3) priority -> 10
Thread Nick(3) priority -> 10
Thread Rose(4) priority -> 1
Thread Isabelle(4) priority -> 1
Thread Pierre(4) priority -> 1
Thread Marie(2) priority -> 10
Thread George(3) priority -> 10
Thread Nick(2) priority -> 10
Thread Rose(3) priority -> 1
Thread Salome(4) priority -> 1
Thread Isabelle(3) priority -> 1
Thread Marie(1) priority -> 10
Thread Marie end
Thread George(2) priority -> 10
Thread Rose(2) priority -> 1
Thread Nick(1) priority -> 10
Thread Nick end
Thread Pierre(3) priority -> 1
Thread George(1) priority -> 10
Thread George end
Thread Salome(3) priority -> 1
Thread Isabelle(2) priority -> 1
Thread Pierre(2) priority -> 1
Thread Rose(1) priority -> 1
Thread Rose end
Thread Pierre(1) priority -> 1
Thread Pierre end
Thread Salome(2) priority -> 1
Thread Isabelle(1) priority -> 1
Thread Isabelle end
Thread Salome(1) priority -> 1
Thread Salome end

 
Remarque: En principe il y a des différentes approches de combiner  "round robin" avec des priorités dans les différents JVM pour éviter le risque de "starving".   D'une manière générale, il n'est guère conseillé d'agir sur les priorités des threads dans les programmes qui se veulent portables!

    Implémenter Runnable

    3.3.5 On peut utiliser un Thread qui travail tous le temps: 

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


 public class CounterA extends JPanel 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);
        private Label  l = new Label("Thread: no Thread counter yet");
       public void init() {
              add(t);
              start.addActionListener(new StartL());
              add(start);
              onOff.addActionListener(new OnOffL());
              add(onOff);
              add(l);
         }
         public void run() {
                 while (true) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e){}
                     if(runFlag) {
                          t.setText(Integer.toString(count++));
                          l.setText("Thread: "+selfThread.getName());
                     }
                 }
          }
 
    class StartL implements ActionListener {
        public void actionPerformed(ActionEvent e) {
                if(selfThread == null){
                        selfThread = new Thread(CounterA.this);
                        selfThread.start();
                }
                runFlag = true;
        }
  }
  class OnOffL implements ActionListener {
          public void actionPerformed(ActionEvent e) {
                  runFlag = false;
          }
  }
  public static void main(String[] args) {
          CounterA cnt = new CounterA();
          JFrame frame = new JFrame("CounterA");
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.add(cnt);
          frame.setSize(300,200);
          cnt.init();
          frame.setVisible(true);
    }
}


 
 
 
 

    3.3.6. 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 javax.swing.*;

public class CounterA1 extends JPanel implements Runnable {
    private int count = 0;
    private Thread selfThread = null;
    private Button    stop = new Button("Stop"),
    start = new Button("Start");
    private TextField t = new TextField(10);
    private Label  l = new Label("Thread: no Thread counter yet");
    private boolean runFlag=true;
    public void init() {
        add(t);
        add(l);
        start.addActionListener(new StartL());
        add(start);
        stop.addActionListener(new StopL());
        add(stop);
    }
    public void run() {
        while (runFlag) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e){}
            t.setText(Integer.toString(count++));
            l.setText("Thread: "+selfThread.getName());
        }  
        selfThread = null;
    }
    class StartL implements ActionListener {
         public void actionPerformed(ActionEvent e) {
             if(selfThread == null){
                 selfThread = new Thread(CounterA1.this);
                 runFlag=true;
                 selfThread.start();
             }
         }
    }
    class StopL implements ActionListener {
        public void actionPerformed(ActionEvent e) {
             if(selfThread != null) {
                //  selfThread.stop();     deprecated
                 runFlag =false;                
             }
       }
    }
    public static void main(String[] args) {
        CounterA1 cnt = new CounterA1();
        JFrame aFrame = new JFrame("CounterA1");
        aFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        aFrame.add(cnt);
        aFrame.setSize(300,200);
        cnt.init();
        aFrame.setVisible(true);
    }
}


 

 
 

3.3.7 Lancement de plusieurs threads 

 //: CounterPT.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 javax.swing.*;

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());
     JPanel p = new JPanel();
     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 CounterPT extends JPanel {
  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 int size;
  public void init() {     
     this.setLayout(new FlowLayout()); 
     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) {
      CounterPT cnt = new CounterPT();

      cnt.size = (args.length == 0 ? 5 : Integer.parseInt(args[0]));
      JFrame aFrame = new JFrame("CounterPT");
      aFrame.add(cnt);
      aFrame.setSize(200*(1+cnt.size/10), 500);
      aFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      cnt.init();
      aFrame.setVisible(true);
  }
}