Vous êtes sur la page 1sur 12

Threads

Programmation concurrente

© Sofia ZAIDENBERG CNRS Novembre 2007 1

Qu’est-ce
Qu’est-ce qu’un
qu’un thread
thread ??
« MultiThread » :
possibilité d’exécuter
simultanément plusieurs
Programme threads dans un même
programme

« Thread » : flot de
contrôle dans un
programme  séquence
d’instructions

 Thread : processus léger (« lightweight process »)


 Similaire à un processus dans le sens où un thread et un programme s’exécutant
représentent tous deux un flot de contrôle séquentiel
 Un thread possède sa propre pile d’exécution et compteur de programme
(contexte d’exécution)
 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
 La machine virtuelle java permet à une application java de définir plusieurs flots
(threads) d’exécution s’exécutant de manière concurrente

© Sofia ZAIDENBERG CNRS Novembre 2007 2


Exemples
Exemples d’utilisation
d’utilisation des
des threads
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 perturber le traitement principal.
Exemple : le « garbage collector » pour un programme Java
Thread principal
attente attente
Thread secondaire
bloqué actif bloqué actif bloqué

 Partage d’un serveur par plusieurs clients


 Pour optimiser le temps d’attente des clients, les services
proposés aux clients sont traités en parallèle
Thread principal
Thread client 1
Thread client 2
Thread client 3

© Sofia ZAIDENBERG CNRS Novembre 2007 3

Les
Les threads
threads en
en Java
Java
 Utilité des threads
 Puissance de modélisation : un monde multithread

 Puissance d’exécution : parallélisme

 Légèreté grâce au partage des données


 Meilleures performances au lancement et en exécution

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


 Simplicité d’utilisation
 Les threads sont des objets
 Classes définies dans le package java.lang

 Primitives basiques et de « bas niveau » pour écrire des programmes


concurrents et synchronisés  programmation pas toujours aisée

 J2SE 5.0 intègre 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…

Dans ce cours, seuls seront abordés les éléments de base de gestion de la concurrence

© Sofia ZAIDENBERG CNRS Novembre 2007 4


Définition
Définition de
de threads
threads
 La classe java.lang.Thread : la classe au centre de la gestion des
threads dans l’API Java

Les threads sont


des objets Object

Thread
Permet de créer de nouveaux
threads Thread()
Thread(Runnable target)

Le « corps » du thread,
code qui sera exécuté …
void run()
void start() Interrompt l’exécution
void interrupt() du thread
Lance l’exécution du thread static void sleep(long millis)
(de sa méthode run). Suspend l’exécution

du thread
Le thread s’exécute jusqu’à
ce que sa méthode run se
termine

© Sofia ZAIDENBERG CNRS Novembre 2007 5

Définition
Définition de
de threads
threads

 Deux manières de définir des threads

Object
<interface> Runnable
void run()
Thread
Thread()
MonThread Thread(Runnable target)

...

void run()
... void run()
void start()
Deuxième solution : void interrupt()
static void sleep(long millis)
Passer en paramètre …
du constructeur un
objet implémentant
l’interface Runnable MonThread Première solution :
...
Sous classer Thread
void run()
...
et redéfinir run()

Mais dans ce cas MonThread ne peut hériter d’une autre classe (héritage simple en Java)
© Sofia ZAIDENBERG CNRS Novembre 2007 6
Définir
Définir et
et lancer
lancer un
un thread
thread
en
en sous-classant
sous-classant java.lang.Thread
java.lang.Thread

class BackgroundSorter extends Thread


• Sous-classer la classe Thread
{
• Redéfinir la méthode run() List l;
public BackgroundSorter(List l)
{
this.l = l;
}
public void run()
{
Collections.sort(l);
}
}

• Instancier votre sous-classe de Thread ...


• start() lance l’exécution de la méthode List liste1 = new ArrayList();
run() du thread et poursuit l’exécution ...
List liste2 = new ArrayList();
...

Thread sorter1 = new BackgrounderSorter(liste1);


sorter1 sorter1.start();
...
Thread sorter2 = new BackgrounderSorter(liste2);
sorter2 sorter2.start();
...
...

© Sofia ZAIDENBERG CNRS Novembre 2007 7

Définir
Définir et
et lancer
lancer un
un thread
thread
en
en implémentant
implémentant l’interface
l’interface java.lang.Runnable
java.lang.Runnable

class SorterApplet extends JApplet implements Runnable {


...
public void init() {
• Implémenter l’interface Runnable
...
(définir une méthode run()) }
public void run() {
... // tri et mise à jour de l’affichage
Si lors de l’exécution on
a besoin de l’objet
Thread t = Thread.currentTread();
Thread qui la contrôle
t.sleep(…);
} méthode statique,
} retourne le Thread
qui exécute cette
instruction
• Créer un objet Thread en
passant en paramètre du
constructeur une instance
de votre implémentation ...
de Runnable Thread t1 = new Thread(new SorterApplet());
t1.start();
• Lancer l’exécution t1 ...
de ce thread par Thread t2 = new Thread(new SorterApplet());
appel de sa méthode t2.start();
t2
start() ...
...
 Exécute la ...
méthode run() de
l’objet Runnable
associé
© Sofia ZAIDENBERG CNRS Novembre 2007 8
«« Endormir
Endormir »» un
un thread
thread
 Souvent les threads sont utilisés pour effectuer des tâches répétitives à intervalles
réguliers (Exemple : animations dans une interface graphique)
public class Clock extends Thread {
boolean keepRunning = true;

public Clock() {
setDaemon(true); // thread de type "démon" : il est possible de
// quitter l’interpréteur même si ce thread est
// en cours d’exécution
start(); // ce thread se lance lui-même
}

public void run() { // le "corps" de ce thread


while (keepRunning) { Suspendre l’exécution
System.out.println(System.currentTimeMillis()); du thread pour
try {
Thread.sleep(1000); // attend 1000 millisecondes
une durée donnée
}
catch (InterruptedException e) {
// ignorer cette exception, lancée si un autre thread
// a interrompu ce thread
}
} java.util.Timer et
} java.util.TimerTask
peuvent être utilisées
public pleaseStop() { depuis version 1.3 de
keepRunning = false;
} Java
}

© Sofia ZAIDENBERG CNRS Novembre 2007 9

Synchronisation
Synchronisation de
de threads
threads
Attendre
Attendre la
la fin
fin d’un
d’un autre
autre thread
thread
 Parfois un thread doit s’interrompre et attendre que l’exécution d’un autre thread
soit terminée
 Effectué à l’aide la méthode join()

class BackgroundSorter extends Thread {


List l;
public BackgroundSorter(List l) {
this.l = l;
}
public void run() {
Collections.sort(l);
}
}
t1
Liste maListe;
...
Thread sorter = new BackgrounderSorter(maListe); sorter
sorter.start(); start
...
byte[] data = readData();
// avant de pouvoir continuer, la liste doit être complètement triée
// il faut donc attendre la fin du thread sorter si il n’a pas déjà terminé
try {
sorter.join(); join t1 se met
} en attente
catch(InterruptedException e) {
// ignorer cette exception, lancée si un autre thread Fin de
// a interrompu ce thread l’exécution
} de sorter

© Sofia ZAIDENBERG CNRS Novembre 2007 10


Synchronisation
Synchronisation de
de threads
threads
sections
sections critiques
critiques

 Lorsque l’on utilise des threads multiples, il faut être très prudent si on permet
à des threads différents d’accéder aux mêmes structures de données.
une liste
d’objets
thread 1 thread 2
Parcours des éléments de la liste Tri de la liste

 Section critique : lorsque deux « morceaux » de code à l’intérieur d’un


programme accèdent au même objet à partir de threads concurrents.
 L’une des principales difficultés avec les programmes multithread
 « Verrouillage » : Technique de base pour éviter que deux threads accèdent au
même moment à un même objet
thread 1 thread 2
Une liste Tri de la liste
Parcours des éléments de la liste d’objets 1
Verrouille la liste avant de la 1
La liste est verrouillée 2
modifier.
2 thread 1 ne peut y accéder tant que ce 3
verrou n’est pas levé. 4 Verrou est relâché quand
4
thread 1 se met en attende jusqu’à ce les modifications sont
3
que thread 2 relâche son verrou terminées

© Sofia ZAIDENBERG CNRS Novembre 2007 11

Synchronisation
Synchronisation de
de threads
threads
sections
sections critiques
critiques
 Le moyen le plus simple de garder les objets « sûrs » vis-à-vis des threads
(« thread-safe ») est de déclarer toutes leurs méthodes sensibles comme
étant synchronized.
class X { Un thread devra obtenir un verrou
... sur l’objet avant de pouvoir appeler
public synchronized void m1(...) { n’importe laquelle de ses méthodes
... synchronisées.
}
 Aucun autre thread ne pourra exécuter
... une méthode synchronisée sur l’objet
} au même moment

 Il est possible d’effectuer une synchronisation plus fine en spécifiant des blocs
de code synchronisés qui détiennent momentanément un verrou sur un objet
spécifié
...
public static void swap(Object[] array, int index1, index2) {
synchronized(array) {
Object tmp = array[index1]; Bloc
array[index1] = array[index2];
array[index2] = tmp;
synchronisé.
}
}

© Sofia ZAIDENBERG CNRS Novembre 2007 12


Synchronisation
Synchronisation de
de threads
threads
sections
sections critiques
critiques
 « Deadlock » : quand deux threads s’interrompent, chacun attendant que
l’autre relâche un verrou qu’il attend
thread1 thread2

Utilise ressource1,
demande un verrou ressource1
sur celle-ci

Utilise ressource2,
demande un verrou
Utilise ressource2, sur celle-ci
demande un verrou
sur celle-ci
ressource2
Utilise ressource1,
demande un verrou
thread1 et thread2 sont sur celle-ci
tous deux interrompus et
s’attendent mutuellement

 Il faut être très prudent lors d’une programmation multithread pour éviter
ce type de situation

© Sofia ZAIDENBERG CNRS Novembre 2007 13

Synchronisation
Synchronisation de
de threads
threads
Coordination
Coordination entre
entre threads
threads

 File d’attente FIFO (« First In First Out »)


ajouter(on)

oo1
1
oo2
2
oo3
3
oo4
4
… oon
n

retirer() oo2
2
oo3
3
oo4
4
… ooi
i

oo1
1

 File partagée par deux threads : un producteur (remplit la file), un


consommateur (vide la file)
f.ajouter(oi) f.ajouter(oj)

Producteur

oo1
1
oo2
2
oo3
3
oo4
4
… oon
n
Si la file est vide,
le consommateur doit
attendre que le producteur
Consommateur place de nouveaux objets
f.retirer() f.retirer()
dans la file

© Sofia ZAIDENBERG CNRS Novembre 2007 14


Synchronisation
Synchronisation de
de threads
threads
Coordination
Coordination entre
entre threads
threads

import java.util.LinkedList; while (q.size() == 0) {


; // ne rien faire
public class FileFIFO { }
LinkedList q = new LinkedList();
Incorrect car inter blocage quand
public synchronized Object retirer() { un consommateur est en attente
// Si la liste q est vide,
Nécessaire si // attendre qu’un objet
Thread Thread
plusieurs
// soit mis dans la file Consommateur Producteur
consommateurs
return q.remove(0);
} Demande
retirer() de verrou
Liste Demande
FIFO de verrou
public synchronized void ajouter(Object o) { Boucle ajouter(obj)
d'attente

q.add(o);

} le producteur est
interrompu. Il doit
attendre que le
} consommateur
relâche son verrou
 Interrompre le thread consommateur pour pouvoir faire un
ajout
 Permettre au thread producteur d’accéder à la file
 Relancer l’exécution du thread consommateur

© Sofia ZAIDENBERG CNRS Novembre 2007 15

Synchronisation
Synchronisation de
de threads
threads
Coordination
Coordination entre
entre threads
threads -- Moniteur
Moniteur

 Rendez-vous entre threads est géré au sein d’un objet moniteur à l'aide de ses
méthodes wait(), notify(), notifyAll()

 C’est un objet quelconque (les méthodes wait(), notify(), notifyAll()


sont héritées de Object)

 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)
 Plusieurs threads peuvent utiliser le même moniteur. Si plusieurs threads
exécutent un wait() dans un même moniteur, ils sont placés dans une
file d’attente.

© Sofia ZAIDENBERG CNRS Novembre 2007 16


Synchronisation
Synchronisation de
de threads
threads
Coordination
Coordination entre
entre threads
threads -- Moniteur
Moniteur
 Utilisation des méthodes wait(), notify(), notifyAll() de la
classe Object
Tout objet, de la même manière qu’il gère un
verrou, maintient une liste de threads en attente.
t1 t2 t3 t4
Threads en attente sur moniteur
Demande t1 t2 t3 t4
de verrou
Un thread appelle la méthode wait() de l'objet :
3
o1 • Il possède nécessairement un verrou sur 1
1 o1 le moniteur (car wait() dans une méthode ou
wait() un bloc synchronized)
• Le thread interrompt son exécution 2
et est ajouté à la liste des threads en
2 attente sur cet objet 3
notify()
• Le verrou qu'il possède sur le moniteur
4
5 est libéré pour rendre possible l’utilisation
6
du moniteur par d'autres threads 4
7
7

Un autre thread appelle la méthode notify() du moniteur 5


•Le moniteur « réveille » l’un de threads en attente et autorise la 6
poursuite de son exécution
• Le thread réactivé ne dispose plus du verrou sur le moniteur, avant de
pouvoir s’exécuter il doit attendre la disponibilité de ce verrou 7
© Sofia ZAIDENBERG CNRS Novembre 2007 17

Synchronisation
Synchronisation de
de threads
threads
Coordination
Coordination entre
entre threads
threads
if (q.size() == 0) {
try {
this.wait();
}
catch (InterruptedException e) {
// ignorer cette exception
}
import java.util.LinkedList; }

public class FileFIFO


{
Attention : quand le thread en
LinkedList q = new LinkedList(); attente est réveillé, il est en
concurrence avec d'autres
public synchronized Object retirer() threads
{ IL FAUT vérifier à nouveau la
condition qui l'a mis en attente
// Si la liste est vide Mise en attente du thread avant de poursuivre son
// attendre qu’un objet du consommateur
// soit mis dans la file
exécution (si elle est vraie,
il doit de nouveau se mettre
return q.remove(0); en attente)
}
while (q.size() == 0) {
public synchronized void ajouter(Object o)
try {
{
Réveille l’un des this.wait();
q.add(o);
}
this.notify(); thread en attente
catch (InterruptedException e) {
}
// ignorer cette exception
}
}
}

© Sofia ZAIDENBERG CNRS Novembre 2007 18


Collections
Collections et
et accès
accès concurrents
concurrents
 Dans java.util les collections ne sont pas « thread-safe »
 L'utilisateur doit gérer lui-même les synchronisations
java.util

AbstractCollection<E>

<<interface>> AbstractList<E>
AbstractSet<E>
Collection<E>

HashSet<E> TreeSet<E> <<interface>> <<interface>> <<interface>> ArrayList<E>


Set <E> Queue<E> List<E>

AbstractSequentialList<E>
LinkedHashSet<E>
<<interface>>
SortedSet<E> AbstractQueue<E> LinkedList<E> Vector<E>

PriorityQueue<E>

Synchronisation brutale :
Toutes les méthodes sont synchronisées !
 lors d'accès concurrents, toute
la structure est verrouillée
 Dans java 5, introduction d'un nouveau package pour offrir un meilleur
support pour la concurrence
 java.util.concurrent

© Sofia ZAIDENBERG CNRS Novembre 2007 19

Collections
Collections et
et accès
accès concurrents
concurrents
 java.util.concurrent propose plusieurs implémentations
des collections offrant un support pour la concurrence
java.util

AbstractCollection<E>

<<interface>> AbstractList<E>
AbstractSet<E>
Collection<E>

HashSet<E> TreeSet<E> <<interface>> <<interface>> <<interface>> ArrayList<E>


Set <E> Queue<E> List<E>

AbstractSequentialList<E>
LinkedHashSet<E>
<<interface>>
SortedSet<E> AbstractQueue<E> LinkedList<E> Vector<E>

PriorityQueue<E>

java.util.concurrent

CopyOnWriteArraySet<E> <<interface>> ConcurentLinkedQueue<E> CopyOnWriteArrayList<E>


BlockingQueue<E>

Synchronisation
uniquement à Synchronisation
ArrayBlockingQueue<E> DelayQueue<E> LinkedBlockingQueue<E>
l'écriture uniquement à
l'écriture
PriorityBlockingQueue<E> SynchronousQueue<E>

© Sofia ZAIDENBERG CNRS Novembre 2007 20


Collections
Collections et
et accès
accès concurrents
concurrents
 CopyOnWriteArrayList<E>
 Un nombre quelconque de lectures peuvent être effectuées
de manière concurrente.
 Toutes les méthodes modifiant la liste sont synchronisées
(en créant une complète copie du tableau interne).
 Classe adaptée à des applications où les opérations de lecture
sont plus nombreuses que les opérations d'écriture.

 CopyOnWriteArraySet<E>
 Implémentation d'un Set basée sur CopyOnWriteArrayList

 Classe adaptée pour la manipulation d'ensembles relativement petits


et où les opérations de lecture sont plus nombreuses que
les opérations d'écriture.

 ConcurrentLinkedQueue<E>
 Implémentation « thread-safe » de Queue sans méthodes synchronisées,
basée en interne sur une liste chaînée.

© Sofia ZAIDENBERG CNRS Novembre 2007 21

Maps
Maps et
et accès
accès concurrents
concurrents
 Comme les collections, les Maps dans java.util ne sont pas thread-safe
java.util

AbstractMap<K,V>
Comme pour Vector,
synchronisation brutale :
Toutes les méthodes sont
synchronisées !

<<interface>> HashMap<K,V>
Map<K,V>
HashTable<K,V>
<<interface>>
TreeMap<K,V> SortedMap<K,V> LinkedHashMap<K,V>

IndentityHashMap<K,V>

java.util.concurrent

<<interface>>
ConcurrentMap<K,V> ConcurrentHashMap<K,V>

Pas de blocage lors des opérations de lecture.


Synchronisation à l'écriture, mais la structure de données interne est segmentée de
manière à ce qu'un verrou ne soit déposé que sur le segment modifié. Des écritures sur les
autres segments peuvent ainsi être réalisées de manière concurrente

© Sofia ZAIDENBERG CNRS Novembre 2007 22


Maps
Maps et
et accès
accès concurrents
concurrents
 ConcurrentMap<K,V>
 Étend l’interface Map<K,V> pour y ajouter 4 méthodes
 V putIfAbsent(K key, V value)
if (!map.containsKey(key))
return map.put(key, value);
else
return map.get(key);

 boolean remove(Object key, Object value)


if ((map.containsKey(key) && map.get(key).equals(value)) {
map.remove(key);
Ces return true;
} else
opérations return false;
sont
atomiques !
 V replace(K key, V value)
if ((map.containsKey(key)) {
return map.put(key, value);
} else
return null;

 boolean replace (K key, V oldValue, V newValue)


if ((map.containsKey(key) && map.get(key).equals(oldValue)) {
map.put(key, newValue);
return true;
} else
return false;

© Sofia ZAIDENBERG CNRS Novembre 2007 23

Conclusion
Conclusion et
et bibliographie
bibliographie

 La programmation multithread est une tâche délicate


 Synchronisation

 Performances

 Lisibilité du code
 Nouveaux packages introduits dans Java 5 pour simplifier
la programmation d'applications concurrentes
 java.util.concurrent, java.util.concurrent.atomic,
java.util.concurrent.locks
 Pour en savoir plus

 JAVA Tutorial : “Threads: Doing Two or More Tasks at Once”, 3rd Edition,
Mary Campione, Kathy Walrath, Alison Huml , Addison Wesley
http://java.sun.com/docs/books/tutorial/essential/threads/index.html

 “Concurrent Programming in Java Design, Principles and Patterns”, 2nd Edition,


Doug Lea, Addison Wesley

© Sofia ZAIDENBERG CNRS Novembre 2007 24