Vous êtes sur la page 1sur 40

E1: Programmation Réseau et

Distribuée
 Programmation concurrente
 Programmation réseaux
 Programmation distribuée

Part 1
PROGRAMMATION CONCURRENTE
THREADS

1
Introduction

Programme vs Processus

 Un programme est une entité statique (fichier situé sur un


disque)

 Un processus est une entité dynamique (un programme en


cours d'exécution)

 Un programme peut engendrer lors de son exécution plusieurs


processus

Prof. Asmaa El Hannani ISIC/S3 11

2
Parallélisme

 Parallélisme ou le multitâches: pouvoir faire exécuter plusieurs


tâches (processus) en parallèle à un ordinateur.
 On dit que deux processus s'exécutent en parallèle lorsqu'ils
s'exécutent sur des CPU différents.
 Ils sont concurrents lorsqu'ils concourent pour l'obtention d'une
même ressource CPU.
 S’il y a par ex. moins de processeurs que de processus à exécuter :
 Division du temps d’utilisation du processeur en tranches de
temps (time slice en anglais)
 Attribution des tranches de temps à chacune des tâches de
façon telle qu’on ait l’impression que les tâches se déroulent
en parallèle.

Prof. Asmaa El Hannani ISIC/S3 12

Programmation concurrente

 La programmation concurrente est un paradigme de


programmation où le programme consiste en plusieurs flots
d'exécution parallèles.
 A l’opposé de la programmation séquentiel où il ya un seul flot
d’exécution; les instructions s‘exécutant d'une façon séquentiel.

 Comment implémenter la concurrence?


 Grâce à des appels systèmes
 Le système d’exploitation offre une bibliothèque appelée système
multitâche
 Désavantage: La portabilité (dépendance au système cible)
 Grâce à des mécanismes du langage de programmation
 L’objet de ce cours !

Prof. Asmaa El Hannani ISIC/S3 13

3
Exemples d’applications

 Tous les systèmes d'exploitation courants sont hautement


concurrents (Microsoft Windows, Linux, Mac OS X)

 Une interface graphique peut charger une image pendant


qu’elle continue de traiter les événements générés par des
actions de l’utilisateur.

 Une application serveur qui attend les demandes de


connexions venant des clients peut traiter les demandes de
plusieurs clients simultanément.

Prof. Asmaa El Hannani ISIC/S3 14

Utilités / Problèmes

 Utilités :
 Optimiser l’utilisation des ressources (CPU ...)
 Faciliter l’équité
 Serveur multi-utilisateurs
 Station mono-utilisateur : système plus réactif
 Satisfaire des contraintes temporelles
 Programmation Temps Réel
 Problèmes :
 Difficulté à écrire un programme concurrent et à le déboguer
 Problème de prédictibilité et de reproductibilité
 Dans un programme concurrent, l'ordre d'exécution des instructions
varie d'une exécution à l'autre pour les mêmes paramètres en entrée

Prof. Asmaa El Hannani ISIC/S3 15

4
Processus (Rappel)

Processus

 Un processus est un programme en execution

 Chaque processus a un espace d’adressage (espace mémoire


où sont rangées les variables et les objets) distinct

 La communication entre processus peut être réalisée par :


 des signaux
 des tubes (pipes)
 de la mémoire partagée
 des sockets
 ...

Prof. Asmaa El Hannani ISIC/S3 17

5
Systèmes Multi-processeurs
 Système avec plusieurs processeurs
 parallèle
 vrai multitache
 doit assurer qu'il y a l'éxecution d'autant de processus que
processeurs en meme temps

 Contrairement: système avec un seul processeur


 quasi-parallèle
 arêter et reprendre les différentes processus
 Gestion avec le « scheduler » (ordonnancement des processus)

Prof. Asmaa El Hannani ISIC/S3 18

Etats d’un processus

 Un processus peut être dans différents états.


 En exécution (running) : il utilise le processeur
 Prêt (ready): le processus est prêt à s'exécuter, mais n'a pas le
processeur (occupé par un autre processus en exécution)
 Bloqué (blocked): il manque une ressource (en plus du
processeur) au processus pour qu'il puisse s'exécuter

1. Le processus manque de ressource


2. Le scheduler choisi un autre processus
3. Le shceduler choisi ce processus
4. La ressource devient disponible

Prof. Asmaa El Hannani ISIC/S3 19

6
Notion de préemption

 Définition : la préemption est la mise en attente forcée d’un


processus.

 Un processus spécial s’occupe de faire « tourner » les


processus qui sont en running : c’est l'ordonnanceur ou
scheduler en anglais. La gestion de la préemption est appelé
l’ordonnancement ou le scheduling.

 Les critères de choix appliqué par le OS déterminent les


performances du système.

Prof. Asmaa El Hannani ISIC/S3 20

Ordonnanceur et répartiteur

Nouveaux

Prof. Asmaa El Hannani ISIC/S3 21

7
Politiques d'ordonnancement

 Premier arrivé, premier servi


 FIFO

 Par priorités constantes

 Par tourniquet (round robin)

Prof. Asmaa El Hannani ISIC/S3 22

Algorithme : Premier Arrivé Premier

Prof. Asmaa El Hannani ISIC/S3 23

8
Algorithme : Premier Arrivé Premier

Prof. Asmaa El Hannani ISIC/S3 24

Algorithme : Le plus court d'abord

Prof. Asmaa El Hannani ISIC/S3 25

9
Politiques d'ordonnancement
 Premier arrivé, premier servi

 Par priorités constantes


 Chaque processus reçoit une priorité
 Le processus de plus forte priorité est élu
 Avec ou sans préemption

 Par tourniquet (round robin)

Prof. Asmaa El Hannani ISIC/S3 26

Algorithme : avec priorités

pr tp

Prof. Asmaa El Hannani ISIC/S3 27

10
Algorithme : avec priorités

Prof. Asmaa El Hannani ISIC/S3 28

Politiques d'ordonnancement

 Premier arrivé, premier servi

 Par priorités constantes

 Par tourniquet (round robin)


 Définition d'un quantum = tranche de temps
 Un processus élu s'exécute au plus durant un quantum; à la fin du
quantum, préemption et réinsertion en fin de file d'attente des
processus prêts

Prof. Asmaa El Hannani ISIC/S3 29

11
Algorithme : tourniquet

Prof. Asmaa El Hannani ISIC/S3 30

Gestion des processus en Java

 Avec Java, on peut gérer les processus :


 création et exécution de processus,
 arrêt de processus

 Deux classes importantes sont à utiliser :


 java.lang.Runtime
 java.lang.Process

Prof. Asmaa El Hannani ISIC/S3 31

12
Classe Runtime

 Permet de contrôler le processus courant.


 On récupère le Runtime via l'opération statique getRuntime de
l'interface Runtime :
 static public Runtime getRuntime();

 Méthodes:
 Connaître l’environnement d’exécution :
total/free/maxMemory(), availableProcessors()
 Contrôle du processus : gc(), exit(), halt()
 Création d’autres processus : exec()

Prof. Asmaa El Hannani ISIC/S3 32

Classe Process

 Permet de contrôler un processus fils


Process fils = Runtime.getRuntime().exec(″commande″);

 Méthodes :
 waitFor() : attends la fin de l’exécution du fils
 exitValue() : valeur renvoyée lors de la fin du fils (par exit(n))
 destroy() : tue le fils
 getInputStream(), getOutputStream(), getErrorStream()
permettent de contrôler les E/S standard du processus

Prof. Asmaa El Hannani ISIC/S3 33

13
Classe Process (suite)

 Il existe plusieurs méthodes exec qui permettent de préciser :


 L’application à démarrer
 Des paramètres
 Des variables d’environnement
 Le répertoire courant

 Exemples :
 Runtime.getRuntime().exec(″javac Toto.java″);
 Runtime.getRuntime().exec(new String[]{″javac″,″Toto.java″});
utilise un tableau pour la commande et ces arguments
 Runtime.getRuntime().exec(″prog″, new String[] {″var=val″}, new
File(″/tmp/″));
exécute prog avec pour répertoire courant /tmp en ayant positionné
auparavant la variable d’environnement var à la valeur val
Prof. Asmaa El Hannani ISIC/S3 34

Thread

14
Qu’est-ce qu’un Thread ?

 Un Thread, dit aussi processus léger (lightweight process) peut se traduire


par tâche ou fil d’exécution.
 Un thread est un flot de contrôle séquentiel à l'intérieur d'un processus.
 Un processus peut contenir plusieurs threads s'exécutant en
concurrence.
 Un processus possède une copie unique de ses propres variables, alors

que les threads partagent les mêmes données.


 Léger : car il s’exécute dans le contexte d’un programme et utilise les
ressources allouées pour ce programme et son environnement.
 Généralement, une application (java par exemple) correspond à un
processus qui lui peut contenir un ou plusieurs threads.
 Les programmes qui peuvent exécuter plusieurs threads en même temps
sont appelés des programmes multithreads.
Prof. Asmaa El Hannani ISIC/S3 36

Exemples d’utilisation des threads


 Animation dans une interface graphique
 Un thread dédié à l’affichage de l’image. Il réaffiche (rafraîchit) l’image
à intervalles de temps réguliers

affichage affichage affichage

 Faire un traitement en tâche de fond


 Thread lancé avec une priorité inférieure qui exécute un traitement
devant s’exécuter en permanence mais sans pertuber le traitement
principal. Exemple : le « garbage collector » pour un programme Java
Thread principal
attente attente
Thread secondaire
bloqué actif bloqué actif bloqué

Prof. Asmaa El Hannani ISIC/S3 37

15
Avantages / Inconvénients des threads

 Avantages:
 Programmer facilement des applications où des traitements se
résolvent de façon concurrente (applications réseaux, par
exemple)
 Améliorer les performances en optimisant l'utilisation des
ressources
 Légèreté grâce au partage des données

 Inconvénients:
 Code plus difficile à comprendre et à débuguer

Prof. Asmaa El Hannani ISIC/S3 38

Thread-processus: en commun

 Possèdent un ID, un ensemble de registres, un état, et une


priorité
 Possèdent un bloc d’information
 Partagent des ressources avec les processus parents
 Sont des entités indépendantes, une fois créés
 Les créateurs de processus et thread ont contrôle sur eux
 Peuvent changer leurs attributs après création, et créer de
nouvelles ressources
 Ne peuvent accéder aux ressources d’autres threads et
processus non reliés

Prof. Asmaa El Hannani ISIC/S3 39

16
Thread-processus: non commun
 Les processus ont un espace d’adressage; les threads non
 Les processus parents et enfants doivent utiliser les
mécanismes de communication inter-processus; les threads
d’un même processus communiquent en lisant et modifiant les
variables de leur processus
 Les processus enfants n’ont aucun contrôle sur les autres
processus enfants; les threads d’un processus sont considérés
comme des pairs, et peuvent exercer un contrôle sur les autres
threads du processus
 Les processus enfants ne peuvent pas exercer de contrôle sur le
processus parent; n’importe quel thread peut exercer un
contrôle sur le thread principal, et donc sur le processus entier
Prof. Asmaa El Hannani ISIC/S3 40

Les threads en Java

 Dans JAVA, la gestion des threads est intégrée au langage


 Simplicité d’utilisation
 Les threads sont des objets: Classes définies dans package
java.lang
 Primitives basiques et de « bas niveau » pour écrire des
programmes concurrents et synchronisés -> programmation pas
toujours aisée
 Une bibliothèque étendue d’utilitaires de plus haut niveau
pour la gestion de la concurrence : java.util.concurrent
 Pools de threads, queues, collections synchronisées, verrous
spéciaux…

Prof. Asmaa El Hannani ISIC/S3 41

17
Le cycle de vie d’un Thread (1/3)

 Après sa création, un thread est dans l'état initial.


 Il reste dans cet état jusqu'à l'appel de la méthode start(). Il passe
alors dans l'état prêt (Il est placé dans une file d'attente
correspondant à sa priorité).
 Lorsque l’ordonnanceur assigne le processeur au thread, il
passe dans l'état actif. Le thread exécute la méthode run().

Prof. Asmaa El Hannani ISIC/S3 42

Le cycle de vie d’un Thread (2/3)


 Un thread en cours d’exécution peut subir différentes actions :
 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).
 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 peut être mis dans une liste d’attente associée à un objet (appel
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 (mort).
Prof. Asmaa El Hannani ISIC/S3 43

18
Le cycle de vie d’un Thread (3/3)

Prof. Asmaa El Hannani ISIC/S3 44

Création de Threads en Java

 Deux manières de faire :


 Par héritage : une classe qui dérive de la classe java.lang.Thread
 méthode run() : code de l'activité
 méthode start() : lancement de l'activité
 méthodes de contrôle ...

 Par implementation: une classe qui implémente l’interface


Runnable
 une unique méhode (abstraite) public void run() : code de l'activité
de Runnable

Prof. Asmaa El Hannani ISIC/S3 45

19
Pourquoi deux manières de faire ?

 Rappel : Java n'autorise pas l'héritage multiple !


 Une classe ne peut hériter que d'une seule classe.
 Par contre elle peut implémenter plusieurs interfaces.

 Comment choisir entre les deux solutions ?


 Hériter de Thread : quand on n'a pas besoin d'hériter d'une autre
classe.

 Implémenter Runnable : quand on souhaite hériter d'une autre


classe.

Prof. Asmaa El Hannani ISIC/S3 46

Définir et lancer un thread


en sous classant Thread
1. Sous classer la classe Thread
2. Redéfinir la méthode run()
class Tache extends Thread {
...
public void run() {
// Code qui sera exécuté par le thread
...
}
}

3. Instancier votre sous classe de Thread


4. Lancer l’exécution de ce thread par appel de sa méthode start() qui
exécute la méthode run() du thread puis poursuit l’exécution.
Tache tache = new Tache(...);
tache.start();

Prof. Asmaa El Hannani ISIC/S3 47

20
Définir et lancer un thread
en implémentant l’interface Runnable
1. Implémenter l’interface Runnable
2. Définir la méthode run()
class Tache implements Runnable {
...
public void run() {
// Code qui sera exécuté par le thread
...
}
}

3. Créer un objet Thread en passant en paramètre du constructeur une


instance de votre implémentation de Runnable.
4. Lancer l’exécution de ce thread par appel de sa méthode start()
Thread tache = new Thread(new Tache(...));
tache.start();

Prof. Asmaa El Hannani ISIC/S3 48

Lancer l’exécution d’un thread

 On appelle la méthode start() du contrôleur de thread.

 Le code du Runnable s’exécute en parallèle du code qui a lancé le


thread.

 La méthode start ne peut être appelée qu’une seule fois pour un


objet thread donné. Dans le cas contraire, on obtiendra une
exception IllegalThreadStateException.

 Attention, une erreur serait d’appeler directement la méthode run() :


la méthode serait alors exécutée par le thread qui l’a appelée et pas
par un nouveau thread !

Prof. Asmaa El Hannani ISIC/S3 49

21
Exemple

 Voici 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 :
 10 fois "bonjour" pour le premier thread,
 12 fois "bonsoir" pour le deuxième thread,
 5 fois un changement de ligne pour le troisième thread.

Prof. Asmaa El Hannani ISIC/S3 50

Exemple (1/3)

Prof. Asmaa El Hannani ISIC/S3 51

22
Exemple (2/3)

 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.

Prof. Asmaa El Hannani ISIC/S3 52

Exemple (3/3)

 Ce programme affiche par exemple le résultat suivant :


bonjour
bonsoir Je suis dans le main aprés l'appel des threads !
bonjour bonsoir bonjour bonjour
bonjour bonsoir bonjour bonjour
bonsoir bonjour bonjour bonsoir
bonjour bonsoir bonsoir
bonsoir bonsoir bonsoir bonsoir bonsoir

Prof. Asmaa El Hannani ISIC/S3 53

23
La classe Thread
 Chaque instance de la classe Thread possède:
 Un nom, [get/set]Name(), un identifiant

 Une priorité, [get/set]Priority(),

 Un statut daemon(booléen), [is/set]Daemon()

 Un groupe, de classe ThreadGroup, getThreadGroup()

 Une cible, représentant le code que doit exécuter ce processus


léger. Ce code est décrit par la méthode
public void run(){...}
qui par défaut ne fait rien (return;) dans a classeThread

Prof. Asmaa El Hannani ISIC/S3 54

Méthodes principales de la classe Thread


 La classe Thread possède plusieurs méthodes pour gérer le cycle de vie du thread.
Méthode Rôle
void destroy() met fin brutalement au thread : à n'utiliser qu'en dernier recours.
int getPriority() renvoie la priorité du thread
ThreadGroup
renvoie un objet qui encapsule le groupe auquel appartient le thread
getThreadGroup()
void interrupt() un thread "interrompt" (envoie une InterruptedException ) un autre.
boolean isAlive() renvoie un booléen qui indique si le thread est actif ou non
boolean isInterrupted() renvoie un booléen qui indique si le thread a été interrompu
void join() Attente de l'arrêt d'un thread donne
void run() méthode contenant le code qui sera exécuté par le thread
void setPriority(int) fixe la priorité du thread
mettre le thread en attente durant le temps exprimé en milli-secondes
void sleep(long) fourni en paramètre. Cette méthode peut lever une exception de type
InterruptedException si le thread est réactivé avant la fin du temps.
void start() démarrer le thread et exécuter la méthode run()
indique à l'interpréteur que le thread peut être suspendu pour
void yield()
permettre à d'autres threads de s'exécuter.
Prof. Asmaa El Hannani ISIC/S3 55

24
Le Thread courant

Prof. Asmaa El Hannani ISIC/S3 56

Les priorités

 Principes :
 Java permet de modifier les priorités (niveaux absolus) des
Threads par la méthode setPriority()
 Par défaut, chaque nouveau Thread a la même priorité que le
Thread qui l’a crée
 Rappel: seuls les Threads actifs peuvent être exécutés et donc
accéder au CPU
 La JVM choisit d’exécuter le Thread actif qui a la plus haute
priorité : priority-based scheduling
 Si plusieurs Threads ont la même priorité, la JVM répartit
équitablement le temps CPU (time slicing) entre tous : round-
robin scheduling

Prof. Asmaa El Hannani ISIC/S3 57

25
Les priorités (suite)

 Les méthodes :
 setPriority(int) : fixe la priorité du receveur.
 le paramètre doit appartenir à :
[MIN_PRIORITY, MAX_PRIORITY]
 sinon IllegalArgumentException est levée
 NORM_PRIORITY : donne le niveau de priorité "normal"

 int getPriority() : pour connaître la priorité d’un Thread

Prof. Asmaa El Hannani ISIC/S3 58

« Endormir » un thread

 Souvent les threads sont utilisés pour effectuer des tâches répétitives
à intervalles réguliers (Exemple : animations dans une interface
graphique)
 Donc besoin de suspendre l’exécution du thread pour une durée donnée

 La méthode statique de la classe Thread


public static void sleep(long x) throws InterruptedException
permet d’endormir le thread COURANT pendant x millisecondes

 Attention : si elle est exécutée dans du code synchronisé (voir plus


loin), le thread garde le moniteur (au contraire de wait())

Prof. Asmaa El Hannani ISIC/S3 59

26
Attente de la fin d’un thread

 Parfois un thread doit s’interrompre et attendre que l’exécution


d’un autre thread soit terminée

 Cela est effectué à l’aide la méthode join()

Prof. Asmaa El Hannani ISIC/S3 60

Exemple (1/2)

Prof. Asmaa El Hannani ISIC/S3 61

27
Exemple (2/2)

 Ce programme affiche par exemple le résultat suivant :


bonjour bonjour
bonsoir bonsoir bonjour
bonsoir bonjour
bonsoir
bonjour bonsoir bonjour bonsoir bonjour
bonjour bonjour bonsoir bonjour bonsoir bonsoir bonsoir bonsoir
bonsoir
FIN des instructions du main!

 Le thread principal du main est interrompu jusqu’à ce que e1,


e2 et e3 se terminent !

Prof. Asmaa El Hannani ISIC/S3 62

Synchronisation entre Threads

 L’utilisation de threads peut entraîner des besoins de


synchronisation pour éviter les problèmes liés aux accès
simultanés aux variables

 En programmation concurrente (parallèle), on appelle section


critique, une partie du code qui ne peut être exécutée en même
temps par plusieurs threads sans risquer de provoquer des
anomalies de fonctionnement

Prof. Asmaa El Hannani ISIC/S3 63

28
Exemple de problème

 Prenons un exemple simple d'une imprimante partagée.

 Si deux utilisateurs peuvent y accéder en même temps, il est


tout de même préférable que le premier arrivé puisse la
verrouiller le temps que son impression soit terminée.

 Dans le cas contraire, l'imprimante risquerait d'imprimer


alternativement une page d'un utilisateur, puis une page d'un
autre, et ainsi de suite. S'il s'agit d'imprimer deux documents
de cent pages chacun, il n'y a plus qu'à trier !!!

Prof. Asmaa El Hannani ISIC/S3 64

Exemple
public class Impression1 extends Thread {
static Imprimante imprimante;
String doc; int nbPage;
public Impression1(String doc, int nbPage) {
this.doc = doc; this.nbPage = nbPage;
}
public void run() {
imprimante.imprime(doc,nbPage);
}
static public void main(String args[]) {
imprimante = new Imprimante();
Impression1 a = new Impression1("Premier document", 5);
Impression1 b = new Impression1("Deuxieme Document", 10);
a.start(); b.start();
}
}
public class Imprimante {
public void imprime(String doc, int np) {
for (int i=1; i<=np; i++) {
System.out.println("Page "+i+" du "+doc);
}
}
}
Prof. Asmaa El Hannani ISIC/S3 65

29
Exemple

 Ce programme affiche le résultat suivant :


Page 1 du Premier document
Page 1 du Deuxieme Document
Page 2 du Premier document
Page 2 du Deuxieme Document
Page 3 du Premier document
Page 3 du Deuxieme Document
Page 4 du Premier document
Page 4 du Deuxieme Document
Page 5 du Premier document
Page 5 du Deuxieme Document
Page 6 du Deuxieme Document
Page 7 du Deuxieme Document
Page 8 du Deuxieme Document
Page 9 du Deuxieme Document
Page 10 du Deuxieme Document

Prof. Asmaa El Hannani ISIC/S3 66

Nécessité de synchroniser !

 Il faut donc éviter l’exécution simultanée de sections critiques


par plusieurs threads.

 En Java le mot clé synchronized est utilisé pour synchroniser


les threads et les empêcher d’exécuter en même temps des
portions de code.
 La synchronisation consiste à obtenir un verrou sur un objet
utilisé pour le traitement (ce qui empêche un thread concurrent de
s'exécuter).

 Plusieurs threads ne peuvent exécuter en même temps du code


synchronisé sur un même objet.
Prof. Asmaa El Hannani ISIC/S3 67

30
Définir une section critique

 Il y a deux façons de définir une section critique :

 Soit on synchronise un ensemble d'instructions sur un objet


synchronized(object){
// Instructions de manipulation d'une
// ressource partagée.
}

 Soit on synchronise directement l'exécution d'une méthode


public synchronized void meth(int param) {
// Le code de la méthode synchronizée.
}

Prof. Asmaa El Hannani ISIC/S3 68

Exemple (synchronized méthode )


public class Impression2 extends Thread {
static Imprimante imprimante;
String doc; int nbPage;
public Impression2(String doc, int nbPage) {
this.doc = doc; this.nbPage = nbPage;
}
public void run() {
imprimante.imprime(doc,nbPage);
}
static public void main(String args[]) {
imprimante = new Imprimante();
Impression2 a = new Impression2("Premier document", 5);
Impression2 b = new Impression2("Deuxieme Document", 10);
a.start(); b.start();
}
}
public class Imprimante {
public synchronized void imprime(String doc, int np) {
for (int i=1; i<=np; i++) {
System.out.println("Page "+i+" du "+doc);
}
}
}
Prof. Asmaa El Hannani ISIC/S3 69

31
Exemple (synchronized instructions)
public class Impression3 extends Thread {
static Imprimante imprimante;
String doc; int nbPage;
public Impression3(String doc, int nbPage) {
this.doc = doc; this.nbPage = nbPage;
}
public void run() {
synchronized (imprimante) {
imprimante.imprime(doc,nbPage);
}
}
static public void main(String args[]) {
imprimante = new Imprimante();
Impression3 a = new Impression3("Premier document", 5);
Impression3 b = new Impression3("Deuxieme Document", 10);
a.start(); b.start();
}
}
public class Imprimante {
public void imprime(String doc, int np) {
for (int i=1; i<=np; i++) {
System.out.println("Page "+i+" du "+doc);
}
}
}
Prof. Asmaa El Hannani ISIC/S3 70

Interblocage

 L’utilisation des verrous sur des objets peut conduire à un interblocage


connue sous le nom de "Deadlock" qui peut se définir ainsi :
 le thread t1 possède le verrou de l’objet o1 et il attend le verrou de

l’objet o2,
 le thread t2 possède le verrou de l’objet o2 et il attend le verrou de

l’objet o1.

 Java n’est pas en mesure de détecter ce genre de situation et c’est au


programmeur qu’il incombe de gérer cette tâche !
Prof. Asmaa El Hannani ISIC/S3 71

32
Coordination entre threads
 On a parfois besoin de coordonner l’exécution de threads
 un thread devant attendre qu’un autre ait effectué 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 ; 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.
 Il existe une méthode notify qui se contente de prévenir un seul des threads
Prof. Asmaa El Hannani ISIC/S3 72

Exemple (1/6)

 Le problème traité ici est de type producteur-consommateur.

 Le stock des produit est partagé par deux threads:


 Un producteur qui rempli le stock
 Un consommateur qui vide le stock

 Manifestement, un thread consommateur ne peut puiser dans


le stock que s’il contient une quantité suffisante.
 Le consommateur doit donc attendre que le producteur place de
nouveaux produit dans le stock

Prof. Asmaa El Hannani ISIC/S3 73

33
Exemple (2/6)

Prof. Asmaa El Hannani ISIC/S3 74

Exemple (3/6)

Prof. Asmaa El Hannani ISIC/S3 75

34
Exemple (4/6)

Prof. Asmaa El Hannani ISIC/S3 76

Exemple (5/6)

Prof. Asmaa El Hannani ISIC/S3 77

35
Exemple (6/6)

 Ce programme affiche le résultat suivant :


--- Suivi de stock ---
++ on ajoute 100 et il y a maintenant 600
++ on ajoute 150 et il y a maintenant 750
-- on puise 400 et il reste 350
-- on puise 200 et il reste 150
** stock de 150 insuffisant pour puiser 400
** stock de 150 insuffisant pour puiser 200
++ on ajoute 100 et il y a maintenant 250
++ on ajoute 150 et il y a maintenant 400
-- on puise 200 et il reste 200
** stock de 200 insuffisant pour puiser 400
++ on ajoute 100 et il y a maintenant 300
++ on ajoute 150 et il y a maintenant 450

Prof. Asmaa El Hannani ISIC/S3 78

Terminer un Thread

 Un thread se termine lorsque sa méthode run() a fini de s’exécuter


 On peut forcer la terminaison d’un thread à l’aide des méthodes
stop(), destroy(), resume()
 Ces méthodes sont deprecated

 Leur utilisation est déconseillée car ils peuvent laisser des objets
dans un état inconsistant et favorisent les interblocages

 Il faut plutôt avertir un thread qu’il doit s’arrêter en utilisant la


méthode interrupt()
 Cette méthode n’arrête pas le thread, il change la valeur d’un champ
booléen dans la structure de données du thread
 C’est au thread de déterminer lorsqu’il est interrompu
 Interrompre est un mécanisme pour attirer l’attention du thread
Prof. Asmaa El Hannani ISIC/S3 79

36
Interrompre un thread

 La méthode run doit vérifier périodiquement si elle a été


interrompue
 Utiliser la méthode interrupted()
 Un thread interrompue devrait relâcher les ressources qu’elle
détient, faire le ménage, and quitter

public void run()


{
for (int i=1; i<=REPETITIONS && !Thread.interrupted(); i++)
{
// Effectue un traitement
}
}

Prof. Asmaa El Hannani ISIC/S3 80

Interrompre un thread en attente


 Si le thread est en attente avec la méthode sleep() , ou par wait() ou
join(), la méthode interrupt() provoque la levée d’une
java.lang.InterruptedException
 Le thread « interrompu » gère cette interruption dans un bloc catch
qui traite cette exception
public void run() {
try {
Thread.sleep (1000);
...
}
catch (InterruptedException exception) {
...
return;
}
// Effectue un traitement
}

Prof. Asmaa El Hannani ISIC/S3 81

37
Les groupes de threads

 Plusieurs Threads peuvent s’exécuter en même temps, il serait


utile de pouvoir les manipuler comme une seule entité
 On peut ainsi jouer en une seule instruction sur la priorité des
threads du groupe ou sur le fait que les thread soient des démons
ou non

 Java offre cette possibilité via l’utilisation des groupes de


threads : java.lang.ThreadGroup

Prof. Asmaa El Hannani ISIC/S3 82

Les groupes de threads

 Une arborescence :
 La classe ThreadGroup permet de constituer une arborescence de
Threads et de ThreadGroups

 Fonctionnement :
 La JVM crée au minimum un groupe de threads nommé main
 Par défaut, un thread appartient au même groupe que celui qui l’a
crée (son père)
 getThreadGroup() : pour connaitre son groupe

Prof. Asmaa El Hannani ISIC/S3 83

38
Création de groupe de threads

 ThreadGroup groupe1 = new ThreadGroup("GP1");


 Thread p1 = new Thread(groupe1, "P1");
 Thread p2 = new Thread(groupe1, "P2");
 Thread p3 = new Thread(groupe1, "P3");
 ThreadGroup groupe11 = new ThreadGroup(groupe1, "GP11");
 Thread p4 = new Thread(groupe11, "P4");
 Thread p5 = new Thread(groupe11, "P5");

Prof. Asmaa El Hannani ISIC/S3 84

Contrôler les ThreadGroup

 Le contrôle des ThreadGroup passe par l’utilisation des


méthodes standards qui sont partagées avec Thread :
 Par exemple : appliquer la méthode interrupt() à un ThreadGroup
revient à invoquer pour chaque Thread du groupe cette même
méthode
 ce sont des méthodes de manipulation récursive

Prof. Asmaa El Hannani ISIC/S3 85

39
Types de threads
 2 types de threads
 les threads utilisateur
 les démons
 La différence :
 la JVM fonctionne tant qu’il reste des threads utilisateurs en exécution
 la JVM s’arrête s’il ne reste plus que des démons
 Les démons sont là seulement pour rendre service aux threads
utilisateur. Exemple : ramasse-miettes
 La méthode void setDaemon(true) de la classe Thread permet
d’indiquer que le thread sera un démon (utilisateur par défaut)
 Elle doit être appelée avant le démarrage du thread par l’appel de
start()
Prof. Asmaa El Hannani ISIC/S3 86

Résumé

 Nous avons vu l'essentiel de ce qui concerne l'utilisation des


threads en Java.
 Les threads sont d'une utilisation très simple, à condition de
bien maîtriser les problèmes de synchronisation.
 Ne supposez jamais qu'un thread s'exécutera avant un autre
simplement parce que c'est logique ou parce que vous l'avez
observé dans un environnement donné.
 Si plusieurs threads doivent être synchronisés, vous devez
toujours le faire explicitement.

 Pour les situations plus délicates, il faut passer par des


ouvrages/référence plus spécialisés!

Prof. Asmaa El Hannani ISIC/S3 87

40