Vous êtes sur la page 1sur 46

Chap5: Les threads

Pr. Lekbir AFRAITES


2020-2021

1
Les Threads
Plan
 Introduction
 Utilisation basique
 Stopper et interrompre un Thread
 Synchronisation et verrou
 Utilisation avancée.

2
Introduction

qu’est ce qu’un thread ?


3
Threads et Processus : généralités

Processus
1. est un programme en cours d’exécution.
2 . Chaque processus dispose d’un espace mémoire :
- pour le code
- pour les données
- pour les piles d’exécution (variables locales et adresses de retour des
fonctions)
3. Chaque processus est composé de plusieurs threads.

Threads
1. partagent le même espace de mémoire (code et données), Cela peut
causer des problèmes.
2. dispose chacun d’une pile d’exécution.
3.sont exécutés à tour de rôle sous le contrôle de système d’exploitation

4
Terminologie

 Thread : fil en anglais, un thread est un fil


d’exécution.
 plusieurs traductions :
- activité,
- fil d’exécution,
- lightweight process (lwp) ou processus leger.

5
Threads et Processus avec Java
 La machine virtuelle correspond à un processus (créé au
lancement de la machine virtuelle).

 Le thread Main correspond à l’exécution de la méthode main


(Thread principal).

 Autres threads :
-Le garbage collector ( ramasse miette)

-Event dispatching thread (EDT).

6
Introduction

 toutes les machines, qu’elles soient monoprocesseur ou multiprocesseur, permettent


d’exécuter plus ou moins simultanément plusieurs programmes (on parle encore de tâches
ou de processus).

 Sur les machines monoprocesseur, la simultanéité, lorsqu’elle se manifeste, n’est en fait


qu’une illusion : à un instant donné, un seul programme utilise les ressources de l’unité
centrale ; mais l’environnement "passe la main" d’un programme à un autre à des intervalles
de temps suffisamment courts pour donner l’impression de la simultanéité ; ou encore,
l’environnement profite de l’attente d’un programme (entrée utilisateur, lecture ou écriture sur
disque, attente de fin de transfert d’un fichier Web...) pour donner la main à un autre.

 Java présente l’originalité d’appliquer cette possibilité de multiprogrammation au sein d’un


même programme dont on dit alors qu’il est formé de plusieurs threads indépendants. Le
contrôle de l’exécution de ces différents threads (c’est-à-dire la façon dont la main passe de
l’un à l’autre) se fera alors, au moins partiellement, au niveau du programme lui-même et ces
threads pourront facilement communiquer entre eux et partager des données.

7
Création

8
Création

 Il existe plusieurs façons pour créer une classe Threads.

 soit : Classe héritant de java.lang.Thread


 soit : Classe implantant l'interface java.lang.Runnable

 Classe anonyme.
 Dans les 3 cas les instructions du thread sont définies dans la méthode
run()

9
Quelques fonction de la classe Thread

/*crée un objet thread*/


public Thread() {…}
public Thread( Runnable runnable) {…} (*)
/*la méthode exécutée par le thread */
public void run(){…}
/*la méthode créant le thread */
public void start(){…}
/*test si ce thread est vivant */
public boolean isActive(){…}
/*waits for this thread to die(attend que ce thread meure )*/
public final void join() throws InterruptedException
/*le thread devient inactif (en sommeil) pendant la durée indiquée*/
public static void sleep(long millisecondes) throws InterruptedException

10
Interface Runnable
 MyRunnable implémente l’interface Runnable

 Redéfinir la méthode run() dans MyRunnable .

11
Interface Runnable
 Code Lanceur ( Main par exemple)

 Simulation

12
Classe Thread
 Classe MyThread héritant de la classe Thread ( Thread implémente
Runnable)

 Redéfinir la méthode run() dans MyRunnable

13
Classe Thread
 Code Lanceur ( Main par exemple)

 Simulation

14
Classe Anonyme
 tout est dans le Code Lanceur

Thread monThread = new Thread() {


public void run () {
...
}
}
monThread.start();

15
++/--
 Thread :
+ : Ecriture simple.

- : pas d’héritage

 Runnable :
+ : Possibilité d’héritage.

 Anonyme :
+ : Pratique si le code comporte peu de lignes.

- : sinon devient illisible.

16
Remarques
 pas de passage de paramètre au thread via start()
 Un thread meurt lorsque sa méthode run() se termine.
 Appel de start() une et une seule fois pour chaque thread,
sinon :

- IllegalThreadStateException si le thread n’est pas mort.

- pas d’exception, mais rien n’est lancé, si le thread est mort

 On appelle jamais directement run() :


- Pas de création d’un nouveau thread

- Le code de la méthode run() est exécuté dans le thread


courant.
17
Exemple introductif

 un programme qui va lancer trois threads simples chacun


d’entre eux se contentant d’afficher un certain nombre de fois
un texte donné, à savoir :

 5 fois "bonjour" pour le premier thread,


 3 fois "bonsoir" pour le deuxième thread,
 2 fois "bonne nuit" pour le troisième thread.

18
Suite 2
La création des objets threads pourra se faire, depuis
class Ecrit extends Thread n’importe quel endroit du programme
{ (par exemple depuis une méthode main) de cette
private String texte; façon :
private int nb; Ecrit e1 = new Ecrit ("bonjour", 10) ;
public Ecrit (String texte, int nb) Ecrit e2 = new Ecrit ("bonsoir", 12) ;
{ this.texte = texte ; Ecrit e3 = new Ecrit ( “bonne nuit", 5) ;
this.nb = nb ;
}
public void run () Ces appels ne font cependant rien d’autre que de
{ for (int i=0 ; i<nb ; i++) créer des objets. Le lancement de l’exécution
System.out.print (texte) ; du thread se fait en appelant la méthode start de la
} classe Thread, par exemple :
} e1.start() ; // lance l’exécution du thread e1

Cet appel réalise les opérations nécessaires pour qu’un nouveau thread soit bien pris en
compte à la fois par la "machine virtuelle Java" et par le système d’exploitation, puis lance
l’exécution de la méthode run de l’objet correspondant.

19
« Endormir » un thread
 Souvent les Threads sont utilisés pour effectuer des taches qui se répètent
sur des intervalles réguliers.

 Nous utiliserons l’appel sleep (ms)

 Cet appel demande que le thread correspondant soit arrêté (on dira "mis en
sommeil“) pour au moins la durée mentionnée. Cette démarche laisse ainsi
la possibilité à d’autres threads de s’exécuter à leur tour.

 La méthode sleep est susceptible de générer une exception de type


InterruptedException dont nous verrons plus tard l’intérêt. Pour l’instant,
nous nous contenterons de l’intercepter en mettant fin à la méthode run.

20
Exemple
class Ecrit extends Thread
{
private String texte; public class TstThr1{
private int nb; public static void main(String args[])
private long attente; {
Ecrit e1 = new Ecrit ("bonjour", 10,5) ;
public Ecrit (String texte, int nb, long attente) Ecrit e2 = new Ecrit ("bonsoir", 12,4) ;
{ this.texte = texte ;this.nb = nb ; Ecrit e3 = new Ecrit ( “bonne nuit",6, 3) ;
this.attente=attente; e1.start();
} e2.start();
e3.start();
public void run () }
{ }
try {
for (int i=0 ; i<nb ; i++)
System.out.print (texte) ;
sleep(attente);
} catch(InterruptedException e){}
}
}

21
Lespublic
threads : un deuxième exemple
class TestThread {
public static void main (String [] args){
Thread ta=new ThreadAfficheLettre(‘a’,100);
Thread tb=new ThreadAfficheLettre(‘b’,100);
System.out.print(« avant »); aaaaaaaaaaab
ta.start(); bbbbbbbbbaaa
tb.start(); aaaaaaaaabbb
System.out.print(« apres »); bbbbbbbbbbaa
} aaaaaaaaabbb
} bbbbbbbbbbaa
class ThreadAfficheLettre extends Thread{ aaaaaaaabbbb
private char lettre; private int time; private cpt=0; bbbbbbbbbbaa
public ThreadAfficheLettre(char lettre,int time) aaaaaaaaabbb
{ this.lettre=lettre; this.time=time;} bbbbbbbbaaaa
/*le travail à effectuer par ce thread*/ aaaaaaabbbbb
Public void run() { bbbbbbbbaaaa
While(cpt<=time) aaaabbbbbbbb
{ bbbbbbaaaaaa
cpt++ aaaaa………...
System.out.print(lettre); }
}
} 22
Les threads : fonctionnement
 La méthode start de la classe Thread est appelée pour
lancer effectivement le thread.

Thread ta= new ThreadAfficheLettre(‘a’,100);


Thread tb= new ThreadAfficheLettre(‘b’,100);

• Le thread n’est effectivement crée qu’à l’appel de la


méthode start.

• La méthode start se traduit par la création d’un thread


par le système d’exploitation et par l’exécution dans ce
thread de la méthode run appliquée à l’objet thread
java.

• La méthode run exécute le travail de thread.


23
Remarques
 Un programme comporte toujours au moins un thread dit "thread principal"
correspondant tout simplement à la méthode main.

 Lorsque la méthode sleep est appelée, elle permet de donner la main à l’un
des autres threads, y compris le thread principal.

 Si l’on appelait directement la méthode run de nos objets threads, le


programme fonctionnerait mais l’on n’aurait plus affaire à trois threads
différents. On exécuterait alors entièrement la méthode run du premier, puis
celle du deuxième et enfin celle du troisième, tout cela dans un seul thread.

 Si nous ne prévoyons pas d’appel de sleep dans notre méthode run, le


programme fonctionnera encore mais son comportement dépendra de
l’environnement. Dans certains cas, on pourra voir chaque thread s’exécuter
intégralement avant que le suivant n’obtienne la main.

24
Exemple : Runnable
Nous créeons des objets de type Ecrit par
class Ecrit implements Runnable :
private String texte; Ecrit e1=new Ecrit(“bonjour “,12,3);
private int nb; Mais ces objets ne sont plus des Threads
private long attente; et ne peuvent plus être lancés par la
méthode start.
public Ecrit (String texte, int nb, long attente)
{ this.texte = texte ;this.nb = nb ;
this.attente=attente;
}
Nous devrons tout d’abord créer des
public void run () objets de type Thread en utilisant une
{ forme particulière de constructeur
try {
for (int i=0 ; i<nb ; i++) Thread t1 = new Thread (e1) ;
System.out.print (texte) ;
Thread.sleep(attente); Nous lancerons ensuite classiquement ce
} catch(InterruptedException e){} thread par start :
} t1.start() ;
}

25
Exemple : Runnable
class Ecrit implements Runnable
private String texte;
private int nb; Public class TestRunnable{
private long attente; Public static void main(String [] args)
{
public Ecrit (String texte, int nb, long attente) Ecrit e1=new Ecrit(“bonjour “,12,3);
{ this.texte = texte ;this.nb = nb ; Ecrit e2=new Ecrit(“bonsoir“,10,4);
this.attente=attente; Ecrit e3=new Ecrit(“bonne nuit “,4, 8);
} Thread t1 = new Thread (e1) ;
Thread t2=new Thread(e2);
public void run () Thread t3=new Thread(e3);
{ t1.start() ;
try { t2.start();
for (int i=0 ; i<nb ; i++) t3.start();
System.out.print (texte) ; }
Thread.sleep(attente); }
} catch(InterruptedException e){}
}
}

26
Remarques
 vous pouvez doter votre classe Ecrit d’une méthode nommée start
jouant alors un double rôle : création de l’objet de type Thread et
lancement du thread :

public class Ecrit implements Runnable


{ public Ecrit (String texte, int nb, long attente)
public class TstThr2
{ // constructeur inchangé
{ public static void main (String args[])
}
{ Ecrit e1 = new Ecrit ("bonjour ", 10, 5) ;
public void start ()
Ecrit e2 = new Ecrit ("bonsoir ", 12, 10) ;
{ Thread t = new Thread (this) ;
Ecrit e3 = new Ecrit ( “bonne nuit", 5, 15) ;
t.start() ;
e1.start() ;
}
e2.start() ;
public void run ()
e3.start() ;
{ // méthode run inchangée
}
}
}
.....
}

27
Runnable: un deuxième exemple
Public class TestRunnable{
public static void main (String [] args){
Runnable ra=new RunnableAfficheLettre(‘a’,100);
Runnable rb=new RunnableAfficheLettre(‘b’,100);
Thread ta=new Thread(ra);
Thread tb=new Thread(rb);
ta.start();
tb.start();
System.out.println("fin du programme");
}
}
Class RunnableAfficheLettre implements Runnable{
private char lettre; private int time; private cpt=0;
public RunnableAfficheLettre (char lettre,int time)
{ this.lettre=lettre; this.time=time;}
/*le travail à effectuer par ce thread*/
Public void run() {
While(cpt<=time) {
cpt++
System.out.println(lettre); }
}
} 28
La méthode join
Public final void join() throws InterruptedException {…}

public static void main (String [] args) throws Exception


{
Thread ta =new ThreadAfficheLettre(‘a’,100);
Thread tb=new ThreadAfficheLettre(‘b’,100);
ta.start();
tb.start();
//elle bloque l’éxcécution jusqu’à que ta et tb termine le
travail
ta.join();
tb.join();
System.out.println(“fin“);

}
29
Problématique
EX: Réservoir-Banque

30
Exemple
 Exemple d’une banque possédant plusieurs comptes.
 Nous générons au hasard des transactions qui déplacent de l’argent
entre ces comptes.

 Chaque compte possède un thread.


 quantités aléatoires d’argent de chaque transaction.

31
Exemple

32
Coordination de threads
 L’avantage des threads sur les processus est qu’ils appartiennent à un
même programme.

 Ils peuvent donc éventuellement partager les mêmes objets. Cet


avantage s’accompagne parfois de contraintes

 En effet, dans certains cas, il faudra éviter que deux threads puissent
accéder (presque) en même temps au même objet.

 Ou encore, un thread devra attendre qu’un autre ait achevé un certain


travail sur un objet avant de pouvoir lui-même poursuivre son exécution.

 Problème est réglé par l’emploi de "méthodes synchronisées", avec le


mot-clé synchronized.

 Problème sera réglé par des mécanismes d’attente et de notification mis


en œuvre à l’aide des méthodes wait et notify.
33
Synchronisation

 mot clé synchronized


 méthodes : wait(), notify, notifiyAll() de Object.

Elles permettent la gestion des threads c.à.d de s’assurer


qu’un bloc de code ne sera pas utilisé à n’importe comment
par plusieurs threads.

34
Synchronized
• Synchronizd (list) {

list.add(“ok“);

• 2 blocs synchronisés sur même objet ne peuvent être exécutés


simultanément par 2 threads différents

Synchronized (obj){ //Pose d’un verrou sur l’objet

} retrait du verrou

• Le thread devant exécuter un bloc synchronisé reste en attente tant qu’il


existe déjà un verrou sur l’objet concerné.

• Appel méthode synchronized ≈ 4 fois plus long qu'appel « normal »


35
Synchonized sur les méthodes
 //class vector
public synchronized int size(){…}

Équivalent à :

public int size() {

synchronized (this) {…}

Aucun bloc synchronisé portant sur le même instance ne peut


s’exécuter dans un autre thread tant que la méthode
synchronisée n’est pas déterminée.
36
37
Attente et notification
 il arrive que l’on ait besoin de coordonner l’exécution de threads, un thread doit
attendre qu’un autre effectue une certaine tâche pour continuer son exécution.

 Java offre un mécanisme basé sur l’objet et sur les méthodes synchronisées

 une méthode synchronisée peut appeler la méthode wait de l’objet dont elle
possède le verrou, ce qui a pour effet :

– de rendre le verrou à l’environnement qui pourra, le cas échéant, l’attribuer à une


autre méthode synchronisée,

– de mettre "en attente" le thread correspondant ; plusieurs threads peuvent être en


attente sur un même objet ; tant qu’un thread est en attente, l’environnement ne lui
donne pas la main ;

 une méthode synchronisée peut appeler la méthode notifyAll d’un objet pour
prévenir tous les threads en attente sur cet objet et leur donner la possibilité de
s’exécuter (le mécanisme utilisé et le thread effectivement sélectionné pourront
dépendre de l’environnement).
38
Synchronisation de threads
Coordination entre threads
 Coordination entre threads est géré au sein d’un objet à l'aide de ses méthodes

wait() , notify() , notifyAll()

 wait() : le thread est mis en attente infinie jusqu'à ce qu’il soit notifié

 notify() : un thread notifie un seul autre thread afin de mettre fin à son attente

 notifyAll() : la notification s’adresse à tous les threads en attente

 wait() , notify() , notifyAll() doivent être utilisées dans des méthodes


synchronized (sinon exception IllegalMonitorStateException)

39
Exemple
 Gestion d’une "réserve “. Il comporte :

• un thread qui ajoute une quantité donnée,

• deux threads qui puisent chacun une quantité donnée.

 Manifestement, un thread ne peut puiser dans la réserve que si elle contient


une quantité suffisante.

 La réserve est représentée par un objet r, de type Reserve. Cette classe


dispose de deux

- méthodes synchronisées puise et ajoute. Lorsque la méthode puise


s’aperçoit que la réserve est insuffisante, il appelle wait pour mettre le thread
correspondant en attente. Parallèlement, la méthode ajoute appelle notifyAll
après chaque ajout.

 Les trois threads sont lancés par main et interrompus lorsque l’utilisateur le
souhaite (en frappant un texte quelconque).
40
public class Synchro3 class ThrAjout extends Thread
{ public static void main (String args[]) {private int vol;
{ Reserve r = new Reserve () ; private Reserve r;
private int delai;
ThrAjout ta1 = new ThrAjout (r, 100, 15) ;
public ThrAjout (Reserve r, int vol, int delai)
ThrAjout ta2 = new ThrAjout (r, 50, 20) ; { this.vol = vol ; this.r = r ; this.delai = delai ;
ThrPuise tp = new ThrPuise (r, 300, 10) ; }
System.out.println ("Suivi de stock --- faire entree pour public void run ()
arreter ") ; { try
ta1.start () ; ta2.start () ; tp.start () ; { while (!interrupted())
Clavier.lireString() ; { r.ajoute (vol) ; sleep (delai) ;
ta1.interrupt () ; ta2.interrupt () ; tp.interrupt () ; }
} }
} catch (InterruptedException e) {}
class Reserve }
}
{private int stock=500;// stock initial=500
public synchronized void puise (int v) throws
InterruptedException
{ if (v <= stock) { System.out.print ("-- on puise " + v) ;
stock -= v ; class ThrPuise extends Thread
System.out.println (" et il reste " + stock ) ; {private int vol;
} private Reserve r;
else { System.out.println ("** stock de " + stock private int delai;
+ " insuffisant pour puiser " + v ) ; public ThrPuise (Reserve r, int vol, int delai)
{ this.vol = vol ; this.r = r ; this.delai = delai ;
wait() ;
}
}
public void run ()
} { try
public synchronized void ajoute (int v) { while (!interrupted())
{ stock += v ; { r.puise (vol) ;
System.out.println ("++ on ajoute " + v sleep (delai) ;
+ " et il y a maintenant " + stock) ; }
notifyAll() ; }
} catch (InterruptedException e) {}
} }
} 41
Résultat :
-- on puise 300 et il reste 0

** stock de 0 insuffisant pour puiser 300

++ on ajoute 50 et il y a maintenant 50

++ on ajoute 100 et il y a maintenant 150

** stock de 150 insuffisant pour puiser 300

++ on ajoute 50 et il y a maintenant 200

** stock de 200 insuffisant pour puiser 300

++ on ajoute 100 et il y a maintenant 300

-- on puise 300 et il reste 0

** stock de 0 insuffisant pour puiser 300

++ on ajoute 50 et il y a maintenant 50
42
États d’un thread
 Au départ, on crée un objet thread. Tant que l’on ne fait rien, il n’a aucune chance d’être
exécuté. L’appel de start rend le thread disponible pour l’exécution. Il est alors considéré
comme prêt.

 Il peut être interrompu par l’environnement qui le ramène à l’état prêt ; c’est ce qui se produit
lorsque l’on doit donner la main à un autre thread (sur le même processeur). Cette transition
peut être "programmée" en appelant la méthode yield de la classe Thread.

 Il peut être mis "en sommeil" par appel de la méthode sleep. Cet état est différent de prêt car
un thread en sommeil ne peut pas être lancé par l’environnement. Lorsque le temps de
sommeil est écoulé, l’environnement replace le thread dans l’état prêt (il ne sera relancé que
lorsque les circonstances le permettront).

 Il peut être mis dans une liste d’attente associée à un objet (appel de wait). Dans ce cas, c’est
l’appel de notifyAll qui le ramènera à l’état prêt.

 Il peut lancer une opération d’entrée-sortie et il se trouve alors bloqué tant que l’opération n’est
pas terminée.

 • Il peut s’achever.

43
Etats d’un thread

44
Etats d’un thread

45
Etats d’un thread

46

Vous aimerez peut-être aussi