Note : on pourra se référer à l'API JavaSE 7 pour plus de détails.
En informatique, un thread d'exécution (ou plus simplement thread) est la plus petite partie qui peut être traitée par un système d'exploitation lors de l'exécution. L'une des fonctionnalités de l'OS (Operating System) consiste à réaliser l'ordonnancement des processus à exécuter.
On qualifie généralement un thread de processus léger car il est contenu dans un processus.
Un processus (ou plus simplement programme en exécution) peut également être composé de plusieurs threads qui s'exécutent de manière concurrente. Les threads d'un même processus partagent des ressources (comme de la mémoire ou des canaux d'entrée / sortie).
Lorsque l'on programme avec Java, la gestion des threads est simplifiée : un programme, c'est à dire une classe dotée de la méthode main, possède un thread d'exécution.
La commande top indique bien deux processus.
3129 richer 20 0 1988 60 0 R 22 0.0 0:01.58 forkit.exe
3128 richer 20 0 1988 284 228 R 21 0.0 0:01.54 forkit.exe
Pour créer d'autres threads au sein du programme principal, on définit des classes qui vont :
Dans les deux cas, il faudra définir la méthode run qui contient les instructions exécutées par le thread.
class MyRunnable extends MyClass implements Runnable {
public void run() {
// code to execute
}
}
class MyThread extends Thread {
public void run() {
// code to execute
}
}
Un thread peut être doté d'un nom (constructeur ou setName()) et/ou rattaché à une groupe de threads (ThreadGroup - cf plus loin dans le cours).
class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
public void run() {
while (true) {
System.err.println("hello world !");
}
}
}
On lance l'exécution du thread par appel de ma méthode start
class MyProgram {
public static void main(String args[]) {
MyThread thread = new MyThread("a thread");
thread.start();
}
}
ou encore :
class MyProgram {
public static void main(String args[]) {
new Thread(new MyRunnable()).start();
}
}
Le programme ne terminera que lorsque le thread aura terminé son exécution.
Voici un exemple :
La commande top indique un seul processus.
3843 richer 20 0 1223m 31m 7336 S 59 0.4 0:04.51 java
On peut connaître l'état d'un thread en appelant la méthode Thread.State getState().
Il existe plusieurs moyens pour suspendre temporairement l'exécution d'un thread.
Il existe plusieurs niveaux de synchronisation pour les threads.
Le résultat de l'exécution est par exemple :
thread0:1
thread1:1
thread2:1
thread1:2
thread0:2
thread2:2
thread2:3
thread1:3
thread2:4
thread0:3
thread1:4
thread0:4
JOIN HERE
Chaque thread possède une priorité d'exécution, héritée par défaut de son thread parent. On peut cependant modifier la priorité d'un thread grâce à void setPriority(int newPriority). Celle-ci varie entre :
On obtient la priorité d'un thread grâce à int getPriority().
Il est parfois intéressant de regrouper les threads par groupes. Sur l'exemple suivant on crée deux groupes de threads, le groupe g1 continuera à s'exécuter alors que g2 sera interrompu par le thread t3. Une exception InterruptedException est levée car les threads de g2 sont interrompus alors qu'ils sont dans l'état waiting engendré par l'instruction sleep
Le résultat de l'exécution est par exemple :
g1.t1-1 running
g1.t1-4 running
g2.t2-1 running
g1.t1-3 running
g2.t2-2 running
g2.t2-4 running
g1.t1-2 running
g2.t2-3 running
g1.t1-1 running
g1.t1-4 running
g1.t1-3 running
g2.t2-1 running
g2.t2-4 running
g1.t1-2 running
g2.t2-2 running
g2.t3:active = 0
g2.t2-3 running
g2.t2-1:java.lang.InterruptedException: sleep interrupted
g2.t2-4:java.lang.InterruptedException: sleep interrupted
g2.t2-3:java.lang.InterruptedException: sleep interrupted
g2.t2-2:java.lang.InterruptedException: sleep interrupted
g1.t1-1 running
g1.t1-4 running
g1.t1-3 running
g1.t1-2 running
Le principe de la programmation concurrente consiste à faire interagir plusieurs threads pour accomplir une tâche.
On peut distinguer deux approches :
Exemple 1 : on désire réaliser le calcul de la somme des évaluations d'une fonction f(x), pour différentes valeurs de x, sur l'intervalle [x1, x2] mais le calcul de f(x) prend du temps. Les calculs étant indépendants on décide de multithreader l'application. On utilisera par exemple k threads qui se répartiront la tâche : feront les calculs en parallèle et reporteront leurs résultats à l'application principale.
Exemple 2 : on souhaite résoudre un problème d'optimisation combinatoire et on crée plusieurs threads qui recherchent la meilleure solution possible et échangent de l'information : exploration, intensification.