Nous allons commencer par le plus simple à comprendre.
Comme je vous le disais, nous allons créer un classe héritée, et tout ce que nous avons à faire, c'est redéfinir la méthode
run() de notre objet afin qu'il sache quoi faire... Vu que nous allons en utiliser plusieurs, autant pouvoir les différencier par un nom...
Créez la classe correspondant à ce diagramme :
On crée ici un constructeur avec un String en paramètre pour spécifier le nom du thread... Cette classe a une méthode getName() afin de retourner celui-ci. La classe Thread se trouve dans la package java.lang, aucune instruction import n'est nécessaire !
Voilà le code de cette classe :
Code : Java 1
2
3
4
5
6
7
8
9
10
11
12
13
14 | public class TestThread extends Thread {
public TestThread(String name){
super(name);
}
public void run(){
for(int i = 0; i < 10; i++)
System.out.println(this.getName());
}
}
|
Et maintenant, testez ce code plusieurs fois :
Code : Java | public class Test {
public static void main(String[] args) {
TestThread t = new TestThread("A");
TestThread t2 = new TestThread(" B");
t.start();
t2.start();
}
}
|
Voici quelques screenshots de mes tests consécutifs :
Vous pouvez voir que l'ordre d'exécution est totalement aléatoire !
Ceci car java utilise un
ordonnanceur.
Vous devez savoir que si vous utilisez plusieurs threads dans une application, ceux-ci
ne s'exécutent pas en même temps !
En fait, l'ordonnanceur gère les différents thread de façon aléatoire : il va en utiliser un, pendant un certain laps de temps, puis un autre, puis revenir au premier... Jusqu'à ce que les threads soit terminés ! Et, lorsque l'ordonnanceur passe d'un thread à un autre, le thread interrompu est mis en
sommeil pendant que l'autre est en
éveil !
Un thread peut avoir plusieurs états :
- NEW : lors de sa création.
- RUNNABLE : lorsque vous invoquez la méthode start(), celui-ci est prêt à travailler.
- TERMINATED : lorsque celui-ci a terminé toutes ses tâches, on dit aussi que le thread est mort. Une fois un thread mort, vous ne pouvez plus le relancer avec la méthode start() !
- TIMED_WAITING : lorsque celui-ci est en pause, quand vous utilisez la méthode sleep() par exemple.
- WAITING : en attente indéfinie...
- BLOCKED : lorsque l'ordonnanceur met un thread en sommeil pour en utiliser un autre... Le statut de celui en sommeil est celui-ci.
Un thread est considéré comme terminé lorsque la méthode run() est dépilée de sa pile d'exécution !
En effet, une nouvelle pile d'exécution a, à sa base, la méthode
run() de notre thread... Une fois celle-ci dépilée, notre nouvelle pile est détruite !
Notre thread principal crée un second thread, celui-ci se lance et crée une pile avec comme base sa méthode
run() ; celle-ci appelle
methode, l'empile, fait tous les traitements, et, une fois terminé, dépile cette dernière. La méthode
run() prend fin, la pile est détruite !
Nous allons un peu modifier notre classe
TestThread afin de voir les états de nos threads que nous pouvons récupérer grâce à la méthode
getState().
Voici notre classe
TestThread modifiée :
Code : Java 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 | public class TestThread extends Thread {
Thread t;
public TestThread(String name){
super(name);
System.out.println("statut du thread " + name + " = " +this.getState());
this.start();
System.out.println("statut du thread " + name + " = " +this.getState());
}
public TestThread(String name, Thread t){
super(name);
this.t = t;
System.out.println("statut du thread " + name + " = " +this.getState());
this.start();
System.out.println("statut du thread " + name + " = " +this.getState());
}
public void run(){
for(int i = 0; i < 10; i++){
System.out.println("statut " + this.getName() + " = " +this.getState());
if(t != null)System.out.println("statut de " + t.getName() + " pendant le thread " + this.getName() +" = " +t.getState());
}
}
public void setThread(Thread t){
this.t = t;
}
}
|
Ainsi que notre
main :
Code : Java 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | public class Test {
public static void main(String[] args) {
TestThread t = new TestThread("A");
TestThread t2 = new TestThread(" B", t);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("statut du thread " + t.getName() + " = " + t.getState());
System.out.println("statut du thread " + t2.getName() + " = " +t2.getState());
}
}
|
Et un jeu d'essai représentatif :
Alors, dans notre classe
TestThread, nous avons ajouté quelques instructions d'affichage afin de voir l'état en cours de nos objets, mais nous avons aussi ajouté un constructeur avec un
Thread en paramètre supplémentaire, ceci afin de voir l'état de notre premier thread lors de l'exécution du second !
Dans notre jeu d'essai vous pouvez voir les différents statuts qu'ont pris nos threads... Et vous pouvez voir que le premier est
BLOCKED lorsque le second est en cours de traitement, ce qui justifie ce dont je vous parlais :
les threads ne s'exécutent pas en même temps !
Vous pouvez voir aussi que les traitements effectués par nos threads sont en fait codés dans la méthode
run(). Reprenez l'image que j'ai utilisée :
"
un thread est une machine bien huilée capable d'effectuer les tâches que vous lui spécifierez".
Le fait de faire un objet hérité de
Thread permet de créer un nouveau thread très facilement. Cependant, vous pouvez procéder autrement, en redéfinissant uniquement ce que doit faire le nouveau thread, ceci grâce à l'interface
Runnable. Et dans ce cas, ma métaphore prend tout son sens :
vous ne redéfinissez que ce que doit faire la machine et non pas la machine tout entière !