Sie sind auf Seite 1von 17

Bibliographie

‰ Pour en savoir plus sur les threads :


Java Threads
Threads
n

de Scott Oaks et Henry Wong


O’Reilly
(en Français)

Université de Nice - Sophia Antipolis


Version 2.2 – 31/03/03
Richard Grin

R. Grin Java : threads page 2

Définitions Systèmes d’exploitation


‰ Tous les systèmes d'exploitation modernes sont
‰ Un programme est multitâche quand il lance
multitâches et ils permettent l’exécution de
(ou peut lancer) l’exécution de plusieurs
programmes multitâches
parties de son code en même temps
‰ Sur une machine monoprocesseur cette
‰ A un moment donné, il comporte plusieurs
exécution en parallèle est simulée
points d’exécution liés aux différentes parties
‰ Si le système est préemptif, il peut à tout
qui s'exécutent en parallèle
moment prendre la main à un programme pour
la donner à un autre
‰ Sinon, un programme garde la main jusqu’à ce
qu’il la cède à un autre

R. Grin Java : threads page 3 R. Grin Java : threads page 4

Threads et processus Exemples de thread


‰ Ce multitâche s'appuie sur les processus ou les ‰ L’interface graphique avec l’utilisateur lance un
threads (processus légers) thread pour charger une image pour continuer à
‰ Chaque processus a son propre espace traiter les événements générés par les actions de
mémoire (espace où sont rangées les valeurs l’utilisateur
des variables utilisées par le processus) ‰ Le serveur réseau qui attend les demandes de
‰ Un processus peut lancer plusieurs threads qui connexions venant des autres machines lance un
se partagent le même espace mémoire et thread pour traiter chacune des demandes
peuvent donc se partager des variables ‰ La multiplication de 2 matrices (m, p) et (p, n)
‰ Un thread prend moins de ressources système peut être effectuée en parallèle par m × n threads
qu’un processus
R. Grin Java : threads page 5 R. Grin Java : threads page 6

1
Utilité du multitâche Utilité du multitâche (2)
‰ Sur une machine multiprocesseurs il permet ‰ Sur une machine monoprocesseur, il peut aussi
d’améliorer les performances en répartissant les être intéressant d’utiliser le multitâche pour
différentes tâches sur différents processeurs n modéliser plus simplement (simulation par exemple)
‰ Par exemple, le calcul du produit de 2 matrices peut n profiter des temps de pose d’une tâche (attente
être réparti en n tâches parallèles (ou k tâches si le d’entrées-sorties ou d’une action de l’utilisateur)
pour exécuter d’autres tâches
nombre k de processeurs est inférieur à n)
n réagir plus vite aux actions de l’utilisateur en rejetant
‰ La répartition des tâches sur les processeurs est le une tâche longue et non-interactive dans un autre
plus souvent faite automatiquement par le système thread (par exemple, chargement d’une image ou
qui offre le multitâche lecture de données qui proviennent d’un réseau)

R. Grin Java : threads page 7 R. Grin Java : threads page 8

Problèmes du multitâche Java et le multitâche


‰ Il est souvent plus difficile d'écrire un ‰ Java supporte l'utilisation des threads
programme multitâche
‰ A l’inverse de la plupart des autres langages, le
‰ Et surtout, il est difficile de déboguer un
programmeur n'a pas à utiliser des librairies
programme qui utilise le multitâche natives du système pour écrire des programmes
multitâches

R. Grin Java : threads page 9 R. Grin Java : threads page 10

Threads en Java Interface Runnable

‰ A tout thread Java sont associés ‰ La classe de l’objet qui définit le code à
n un objet qui détermine le code qui est
exécuter doit implémenter l’interface
Runnable
exécuté par le thread
n un objet qui « contrôle » le thread et le
public interface Runnable {
représente auprès des objets de
void run();
l’application ; on l’appellera le « contrôleur
}
de thread » méthode qui contient
le code à exécuter par
le thread
R. Grin Java : threads page 11 R. Grin Java : threads page 12

2
Un thread n’est pas un objet ! Contrôleur de thread
‰ La méthode run() « saute » d’un objet à ‰ Le contrôleur d’un thread est un objet qui
l’autre en exécutant les méthodes des
n est l’intercesseur entre le thread et les objets
classes de ces objets :
o1.m1(); de l’application
o2.m2(); n permet de contrôler l’exécution du thread
... (pour le lancer en particulier)
‰ Un thread est une unité d’exécution qui, à n a des informations sur l’état du thread (son
un moment donné, exécute une méthode nom, sa priorité, s’il est en vie ou non,…)
‰ A un autre moment, ce même thread pourra
n est une instance de la classe Thread (ou
exécuter une autre méthode d’une autre
classe une classe fille)

R. Grin Java : threads page 13 R. Grin Java : threads page 14

Classe Thread 2 façons de créer


un contrôleur de thread
‰ Elle implémente l'interface Runnable (mais la
méthode run() ne fait rien) ‰ Créer une instance d’une classe fille de la
classe Thread ;
‰ Une instance d’une classe fille de Thread peut
la classe fille doit redéfinir la méthode run()
donc être à la fois un contrôleur de thread et
‰ Utiliser le constructeur Thread(Runnable)
définir le code à exécuter
de la classe Thread :
le code qui sera contrôlé
n créer un Runnable par le contrôleur
n le passer au constructeur de Thread

R. Grin Java : threads page 15 R. Grin Java : threads page 16

Créer un contrôleur de thread avec Créer un contrôleur de thread


une classe fille de la classe Thread avec l'interface Runnable
class Tache implements Runnable {
class ThreadTache extends Thread { . . .
. . . public void run() {
public void run() { // Code qui sera exécuté par le thread
// Code qui sera exécuté par le thread . . .
. . . }
} }
}
Tache tache = new Tache(…);
ThreadTache threadTache = new ThreadTache(…); Thread t = new Thread(tache) ;

R. Grin Java : threads page 17 R. Grin Java : threads page 18

3
Quelle façon utiliser ? Nom d’un thread
‰ Si on veut hériter d’une autre classe pour la ‰ Des constructeurs de Thread permettent de
classe qui contient la méthode run(), on est donner un nom au thread en le créant
obligé de choisir la 2ème façon ‰ Le nom va faciliter le repérage des threads
(Thread(Runnable)) durant la mise au point
‰ Il est aussi plus simple d’utiliser la 2ème
façon pour partager des données entre
plusieurs threads
‰ Sinon, l’écriture du code est (légèrement)
plus simple en utilisant la 1ère façon

R. Grin Java : threads page 19 R. Grin Java : threads page 20

Lancer l’exécution d'un thread Relancer l’exécution d'un thread

‰ On appelle la méthode start() du ‰ On ne peut relancer un thread qui a déjà été


contrôleur de thread : lancé
t.start(); ‰ Si l’exécution de la méthode run du thread
‰ Le code du Runnable s’exécute en parallèle n’est pas encore terminée, on obtient une
au code qui a lancé le thread java.lang.IllegalThreadStateException
‰ Attention, une erreur serait d’appeler ‰ Si elle est terminée, aucune exception n’est
directement la méthode run() : la méthode lancée mais rien n’est exécuté
run() serait exécutée par le thread qui l’a
appelée et pas par un nouveau thread
R. Grin Java : threads page 21 R. Grin Java : threads page 22

Vie du contrôleur de thread Utilisation d'une classe interne


‰ Le contrôleur de thread existe indépendamment
du thread, ‰ La méthode run est public
n avant le démarrage du thread, ‰ Si on ne souhaite pas qu’elle soit appelée
par exemple, pour initialiser des variables directement, on peut utiliser une classe
d'instances du contrôleur interne à une classe fille de Thread pour
n après la fin de l’exécution de ce thread, implémenter Runnable
par exemple, pour récupérer des valeurs
calculées pendant l’exécution du thread et
rangées dans des variables d’instances du
contrôleur

R. Grin Java : threads page 23 R. Grin Java : threads page 24

4
Utilisation d'une Méthodes principales
classe interne anonyme de la classe Thread
void start()
‰ Si le code d'une tâche comporte peu de
lignes, on peut lancer son exécution en static void sleep(long)
throws InterruptedException
parallèle en utilisant une classe anonyme :
Thread t = new Thread() { void join() throws InterruptedException
. . . void interrupt()
Ou encore :
public void run() { new Thread( static boolean interrupted()
. . . new Runnable() { int getPriority()
} . . . void setPriority(int)
}; public void run() {
. . . static Thread currentThread()
t.start();
} static void yield()
});
R. Grin Java : threads page 25 R. Grin Java : threads page 26

Thread courant Attente de la fin d’un thread


‰ La méthode currentThread montre bien ‰ Soit un thread t
qu’un thread n’est pas un objet t.join();
‰ Placée dans une méthode de n’importe attend la fin de l’exécution du thread t
quelle classe, elle retourne l’objet Thread qui ‰ On remarquera qu’après la fin de l’exécution
contrôle le thread qui exécute cette méthode du thread t on peut encore envoyer de
au moment où currentThread est appelé messages à l’objet contrôleur de thread t
‰ On peut ainsi faire un traitement spécial dans ‰ On peut ainsi interroger t pour récupérer le
le cas où la méthode est exécuté par un résultat d’un calcul effectué par le thread
certain thread (par exemple le thread de répartition
des événements dans un GUI)

R. Grin Java : threads page 27 R. Grin Java : threads page 28

Passer la main Dormir


‰ La méthode static de la classe Thread ‰ La méthode static de la classe Thread
public static void yield() public static void sleep(long millis)
permet de passer la main à un autre thread throws InterruptedException
de priorité égale ou supérieure fait dormir le thread qui l'appelle
‰ Elle permet d'écrire des programmes plus ‰ Si elle est exécutée dans du code
portables qui s'adaptent mieux aux systèmes synchronisé, le thread ne perd pas le moniteur
multitâches non préemptifs (Macintosh ou (au contraire de wait())
Green Threads de Solaris)

R. Grin Java : threads page 29 R. Grin Java : threads page 30

5
Interrompre un thread en attente Threads et exceptions

‰ Un thread peut se mettre en attente par la ‰ Si une exception n’est pas traitée (par un bloc
méthode sleep, ou par l'attente d'une try-catch), elle interrompt l’exécution du
entrée-sortie, ou par wait ou join thread courant mais pas des autres threads
‰ La méthode run ne peut déclarer lancer une
‰ Un autre thread peut interrompre cette
attente par la méthode interrupt() exception contrôlée car elle redéfinit une
méthode sans clause « throws »
‰ Remarque : jusqu’à la version SDK1.3 de
Java, interrupt n'interrompt pas une ‰ Une exception non saisie peut être saisie par

attente d'entrée-sortie ; il faut utiliser le le groupe du thread (étudié plus loin)


paquetage java.nio du SDK 1.4 pour cela

R. Grin Java : threads page 31 R. Grin Java : threads page 32

Synchronisation
‰ L’utilisation de threads peut entraîner des
besoins de synchronisation pour éviter les
Synchronisation entre threads problèmes liés aux accès simultanés aux
variables

R. Grin Java : threads page 33 R. Grin Java : threads page 34

Sections critiques Pourquoi synchroniser ?


‰ En programmation, des sections de code
critiques ne peuvent être exécutées en même ‰ Il faut donc éviter l’exécution simultanée de
temps par plusieurs threads sans risquer de sections de code critiques par plusieurs
provoquer des anomalies de fonctionnement threads
‰ Exemple simpl(ist)e : ‰ Par exemple, si plusieurs threads veulent
x = 2; modifier en même temps le même objet, on
x++; devra les synchroniser pour qu’ils effectuent
exécuté par 2 threads, peut donner en fin ces modifications les uns après les autres
d’exécution 3 ou 4 suivant l’ordre d’exécution,
si les threads utilisent un cache local (registre
par exemple) pour ranger la valeur de x
R. Grin Java : threads page 35 R. Grin Java : threads page 36

6
Mécanisme de synchronisation Code synchronisé
sur un objet o
‰ En Java, la synchronisation des threads
repose sur les moniteurs des objets ‰ Méthode synchronisée m (avec un message
‰ Chaque objet Java a un moniteur qui contrôle
envoyé à l’objet o : o.m(…)) :
l’autorisation d’exécuter du code synchronisé public synchronized int m(…) { . . }
sur cet objet : un seul thread peut posséder le ‰ Bloc synchronisé sur cet objet :
moniteur d’un objet à un moment donné synchronized(o) {
// le code synchronisé
. . .
}

R. Grin Java : threads page 37 R. Grin Java : threads page 38

Mécanisme de synchronisation Mécanisme de synchronisation (2)


‰ Aucun autre thread ne peut exécuter du code
‰ Un thread t acquiert le moniteur d’un objet en
synchronisé sur le même objet o tant que t
exécutant du code synchronisé sur cet objet
exécute le code synchronisé
‰ t rend le moniteur en quittant le code
‰ Si un autre thread veut exécuter du code
synchronisé (ou en appelant la méthode wait() de
synchronisé sur o, il est mis en attente
l’objet o)
‰ Lorsque t rend le moniteur, un des threads en
‰ Il peut quitter le code synchronisé normalement, ou si
une exception est lancée et non saisie attente se saisira du moniteur et pourra
redémarrer
‰ Les autres threads en attente auront la main à
tour de rôle (si tout se passe bien…)
R. Grin Java : threads page 39 R. Grin Java : threads page 40

Exemple Exemple (suite)


public class Compte {
‰ On lance 3 threads du type suivant :
private double solde; Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
public void deposer(double somme) {
compte.deposer(1000);
solde = solde + somme; }
} }
};
public double getSolde() { ‰ A la fin de l’exécution, on n’obtient pas
return solde;
nécessairement 300.000
‰ Il faut rendre deposer (et getSolde) synchronisée :
}
public synchronized
} void deposer(double somme)

R. Grin Java : threads page 41 R. Grin Java : threads page 42

7
Provoquer le problème
Problèmes de portabilité
public class Compte {
private double solde;
‰ En fait, si on exécute le code précédent sans
rendre deposer synchronized, on obtiendra
public void deposer(double somme) {
bien souvent le bon résultat
double soldeTemp = solde; Avec tous les
‰ Ça dépend du fonctionnement du multitâche
Thread.yield(); SE et JVM, la
du système d’exploitation sous-jacent, et de main pourra
solde = soldeTemp + somme;
la JVM être rendue
}
‰ Pour rendre plus portable du code multi- pendant
tâche, il faut ajouter des appels de la public double getSolde() {
l’exécution de
méthode yield() qui forcent le thread à cette méthode
return solde;
rendre la main, et permettre ainsi à un autre
}
thread de pouvoir s’exécuter
}
R. Grin Java : threads page 43 R. Grin Java : threads page 44

Synchronisation et performances Méthodes statiques


‰ L’exécution de code synchronisé nuit aux ‰ Si on synchronise une méthode static, on
performances (vérifications sur le moniteur bloque le moniteur de la classe
des objets) ‰ On bloque ainsi tous les appels à des
méthodes synchronisées de la classe (mais
pas les appels synchronisés sur une instance
de la classe)

R. Grin Java : threads page 45 R. Grin Java : threads page 46

Méthode synchronisée et héritage

‰ La redéfinition d’une méthode synchronisée


dans une classe fille peut ne pas être wait et notify
synchronisée
‰ De même, la redéfinition d’une méthode non
synchronisée peut être synchronisée

R. Grin Java : threads page 47 R. Grin Java : threads page 48

8
Exécution conditionnelle Schéma d’utilisation de wait-notify
‰ Lorsqu’un programme est multi-tâche, la
situation suivante peut se rencontrer : ‰ Cette utilisation demande un travail coopératif
n Un thread t1 ne peut continuer son
entre les threads t1 et t2 :
exécution que si une condition est remplie 1. Ils se mettent d’accord sur un objet commun
n Le fait que la condition soit remplie ou non
objet
dépend d’un autre thread t2 2. Arrivé à l’endroit où il ne peut continuer que si
‰ Une solution coûteuse serait que t1 teste la la condition est remplie, t1 se met en attente :
condition à intervalles réguliers objet.wait();
‰ Les méthodes wait() et notify() de la 3. Quand t2 a effectué le travail pour que la
classe Object permettent de programmer condition soit remplie, il le notifie :
plus efficacement ce genre de situation objet.notify();
R. Grin Java : threads page 49 R. Grin Java : threads page 50

Besoin de synchronisation Méthode wait()


‰ public final void wait()
‰ Le mécanisme d’attente-notification lié à un throws InterruptedException
objet met en jeu l’état interne de l’objet ; pour
‰ objet.wait()
éviter des accès concurrent à cet état interne,
n nécessite que le thread en cours possède le
une synchronisation est nécessaire
moniteur de objet
‰ Les appels aux méthodes wait() et
n bloque le thread qui l’appelle, jusqu’à ce qu’un
notify() (et notifyAll()) d’un objet ne autre thread appelle la méthode
peuvent donc être effectués que dans du objet.notify() ou objet.notifyAll()
code synchronisé sur l’objet n libère le moniteur de l’objet (l’opération
« blocage du thread – libération du moniteur »
est atomique)
R. Grin Java : threads page 51 R. Grin Java : threads page 52

Utilisation de wait Méthode notifyAll()


‰ Mauvaise utilisation :
if (!condition) objet.wait();
‰ public final void notifyAll()
n si on quitte l’attente avec le wait(), cela signifie qu’un

autre thread a notifié que la condition était remplie ‰ objet.notifyAll()

n mais, après la notification, et avant le n nécessite que le thread en cours possède le

redémarrage de ce thread, un autre thread a pu moniteur de objet


prendre la main et modifier la condition n débloque tous les threads qui s’étaient bloqués

‰ Le bon code (dans du code synchronisé) : sur l’objet avec objet.wait()


while (!condition) {
objet.wait();
}
R. Grin Java : threads page 53 R. Grin Java : threads page 54

9
Méthode notifyAll() Méthode notify()
‰ Un seul des threads débloqués va récupérer le ‰ objet.notify()
moniteur ; on ne peut prévoir lequel n idem notifyAll() mais
‰ Les autres devront attendre qu’il relâche le
n ne débloque qu’un seul thread
moniteur pour être débloqués à tour de rôle, mais
‰ On ne peut prévoir quel sera le thread débloqué
ils ne sont plus bloqués par un wait
et, le plus souvent, il vaut donc mieux utiliser
‰ En fait, ils se bloqueront à nouveau eux-mêmes notifyAll()
le plus souvent (s’ils sont dans une boucle while
avec wait)

R. Grin Java : threads page 55 R. Grin Java : threads page 56

Déblocage des threads notify perdus

‰ Le thread débloqué (et élu) ne pourra reprendre ‰ Si un notifyAll() (ou notify()) est exécuté
son exécution que lorsque le thread qui l’a notifié alors qu'aucun thread n'est en attente, il est
rendra le moniteur de l’objet en quittant sa portion perdu : il ne débloquera pas les wait()
de code synchronisé exécutés ensuite
‰ Le redémarrage et l’acquisition se fait dans une
opération atomique

R. Grin Java : threads page 57 R. Grin Java : threads page 58

Exemple avec wait-notify Exemple avec wait-notify


public class Depot {
‰ Les instances d’une classe Depot contiennent private int nbJetons = 0;
des jetons
public synchronized void donneJeton() {
‰ Ces jetons sont try {
n déposés par un producteur while (nbJetons == 0) {
n consommés par des consommateurs wait();
}
‰ Les producteurs et consommateurs sont des
nbJetons--;
threads }
catch (InterruptedException e) {}
}

R. Grin Java : threads page 59 R. Grin Java : threads page 60

10
Exemple avec wait-notify Variante de wait
public synchronized void recois(int n) { ‰ Si on ne veut pas attendre éternellement une
nbJetons += n; notification, on peut utiliser une des variantes
notifyAll(); suivantes de wait :
} public void wait(long timeout)
public void wait(long timeout,
int nanos)
‰ Dans ce cas, le thread doit gérer lui-même le
fait de connaître la cause de son déblocage
(notify ou temps écoulé)

R. Grin Java : threads page 61 R. Grin Java : threads page 62

Moniteurs réentrants Affectations atomiques


‰ Il est inutile de synchroniser une partie de code
‰ Un thread qui a acquis le moniteur d’un objet qui ne fait qu'affecter une valeur à une variable
peut exécuter les autres méthodes de type primitif de longueur 32 bits ou moins
synchronisées de cet objet ; il n’est pas (int, short, …)
bloqué en demandant à nouveau le moniteur
‰ En effet, la spécification du langage Java
spécifie qu'une telle affectation ne peut être
interrompue pour donner la main à un autre
thread
‰ Mais, cette spécification n'assure rien pour les
affectations de double et de long !

R. Grin Java : threads page 63 R. Grin Java : threads page 64

Stopper un thread Simuler stop()


‰ Pour rendre possible l'arrêt d'un thread T, on peut
‰ Si on stoppe un thread il arrête de s’exécuter utiliser une variable arretThread visible depuis
‰ On ne peut reprendre son exécution ; si on T et les threads qui peuvent stopper T :
veut pouvoir suspendre et reprendre n T initialise arretThread à false lorsqu'il
l’exécution du thread, voir suspend et resume démarre
‰ stop() est deprecated car le thread stoppé n pour stopper T, un autre thread met

peut laisser des objets dans un état arretThread à true


inconsistant car il relâche leur moniteur quand n T inspecte à intervalles réguliers la valeur de

il arrête son exécution arretThread et s'arrête quand


arretThread a la valeur true
‰ On peut simuler stop en utilisant une variable
‰ arretThread doit être déclarée volatile si
elle n’est pas accédée dans du code synchronisé
R. Grin Java : threads page 65 R. Grin Java : threads page 66

11
Interrompre un thread Interrompre un thread
‰ Le mécanisme décrit précédemment pour
stopper un thread ne fonctionne pas si le ‰ Quand un thread souhaite permettre son
thread est en attente (wait , sleep, IO) interruption, il doit entrer dans une boucle du
type
‰ Dans ce cas, on utilise interrupt() qui
while (!interrupted()) {
interrompt les attentes (même celle des
. . . // faire son travail
entrées-sorties depuis SDK 1.4) et met l’état
}
du thread à « interrupted », mais ne le stoppe
pas autoritairement)
‰ La méthode static interrupted()
indique si le thread courant a été interrompu
(et enlève l’état « interrupted » du thread)
R. Grin Java : threads page 67 R. Grin Java : threads page 68

Interrompre un thread en attente Suspendre et relancer un thread


‰ S’il attend par un wait ou sleep, il doit ‰ suspend() et resume() sont deprecated car
traiter les InterruptedException le plus ils peuvent provoquer des blocages (un thread
souvent en s’interrompant lui-même car la suspendu ne relâche pas les moniteurs qu’il
levée d’une telle exception enlève l’état possède)
« interrupted » du thread :
try { ‰ On peut les remplacer en utilisant une variable
. . . suspendreThread comme pour la méthode
catch(InterruptedException e) { stop()
Thread.currentThread().interrupt();
‰ Comme il faut pouvoir reprendre l’exécution on
}
doit en plus utiliser wait() et notify()
‰ Voir nio pour les entrées-sorties
R. Grin Java : threads page 69 R. Grin Java : threads page 70

Cycle de vie d'un thread


Simuler suspend et resume
Bloqué sleep()
‰ Pendant son exécution le thread scrute la new wait()
Thread()
valeur de suspendreThread
notify()
‰ Si la valeur est true, le thread se met en
attente avec wait sur un objet o start() Eligible pour En
Nouveau
‰ Quand un autre thread veut relancer thread l'exécution exécution
yield()
l’exécution du thread, il met la variable
suspendreThread à false et il appelle
notify sur l’objet o exit()en
Rappel : les appels de wait et notify doivent se fin d'exécution
‰ Mort
faire dans des sections synchronisées sur o
R. Grin Java : threads page 71 R. Grin Java : threads page 72

12
Threads démons Threads démons
‰ 2 types de threads ‰ La méthode void setDaemon(boolean on) de
n les threads utilisateur
la classe Thread permet d’indiquer que le thread
n les démons
sera un démon (thread utilisateur par défaut)
‰ Elle doit être appelée avant le démarrage du
‰ La différence :
thread par l’appel de start()
n la JVM fonctionne tant qu’il reste des threads
utilisateurs en exécution
n 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
R. Grin Java : threads page 73 R. Grin Java : threads page 74

Principe de base
‰ Si plusieurs threads de même priorité sont en
exécution, on ne peut pas prévoir quel thread
va prendre la main
Priorités
‰ S’ils sont en attente d’exécution, un thread de
plus grande priorité prendra toujours la main
avant un autre thread de priorité plus basse
‰ Cependant, il peut arriver exceptionnellement qu’un
thread continue son exécution alors que des threads
de priorité supérieure sont en attente d’exécution

R. Grin Java : threads page 75 R. Grin Java : threads page 76

Niveaux de priorité
‰ Un nouveau thread a la même priorité que le
thread qui l’a créé
‰ En général, tous les threads ont la même Difficultés liées au multitâche
priorité (NORM_PRIORITY )
‰ Il faut faire appel à la méthode setPriority
si on veut modifier cette priorité par défaut
‰ Le paramètre de setPriority doit être
inclus entre MIN_PRIORITY et
MAX_PRIORITY

R. Grin Java : threads page 77 R. Grin Java : threads page 78

13
Difficultés du multitâche Difficultés du multitâche
‰ Si un thread doit attendre 2 notify de 2 autres ‰ En effet, les 2 notify() peuvent arriver "presque
threads, ce serait une faute de coder en même temps" :
wait();
wait();
n le 1er notify() débloque le 1er wait() qui
relâche le moniteur et permet ainsi à un des
autres threads…
n … d’envoyer le 2ème notify() avant que le
2ème wait() ne soit lancé
n le thread reste bloqué éternellement sur le
2ème wait() (qui ne recevra jamais de notify())

R. Grin Java : threads page 79 R. Grin Java : threads page 80

Comment coder cette situation ? Comment coder cette situation


‰ On compte le nombre de notify() avec une
(suite)
variable qui est incrémentée dans une partie ‰ Et on se met en attente dans une boucle (dans
critique (synchronisée) qui contient le notify() une portion synchronisée) :
(pour être certain que la variable représente while (nbNotify < 2) {
wait();
vraiment le nombre de notify()) :
nbNotify++; }
notifyAll(); ‰ Si on reçoit 1 notify() entre les 2 wait(),
nbNotify sera égal à 2 et on sortira de la
boucle sans faire le 2ème wait()

R. Grin Java : threads page 81 R. Grin Java : threads page 82

Éviter la synchronisation Partage de variables


par les threads
‰ Comme la synchronisation a un coût non ‰ Soit v une variable partagée par plusieurs threads
négligeable, il faut essayer de l’éviter quand
‰ Si le thread T modifie la valeur de v, cette
on peut
modification peut ne pas être connue
‰ Par exemple, si un seul thread écrit une immédiatement par les autres threads
valeur de type int qui est lue par plusieurs ‰ Par exemple, le compilateur a pu utiliser un
autres threads, on peut se passer de registre pour conserver la valeur de v pour T
synchronisation car les opérations de lecture- ‰ La spécification de Java n'impose la connaissance
écriture de int sont atomiques de cette modification par les autres threads que
‰ Mais attention, il y a de nombreux pièges ! lors de l'acquisition ou le relâchement du moniteur
d'un objet (synchronised)
R. Grin Java : threads page 83 R. Grin Java : threads page 84

14
Volatile
‰ Pour éviter ce problème, on peut déclarer la
variable v volatile
‰ On est ainsi certain que tous les threads Autres classes liées aux threads
partageront une zone mémoire commune
pour ranger la valeur de la variable v
‰ De plus, si une variable de type long et
double est déclarée volatile, sa lecture
et son écriture est garantie atomique

R. Grin Java : threads page 85 R. Grin Java : threads page 86

ThreadGroup Lancer un thread d'un groupe


‰ ThreadGroup représente un ensemble de
threads, qui peut lui-même comprendre un ‰ Pour créer un thread d’un groupe, on doit utiliser
threadGroup ; on a ainsi une arborescence le constructeur de la classe Thread qui prend un
de threadGroup groupe en paramètre ; par exemple :
‰ On peut ainsi jouer en une seule instruction ThreadGroup tg =
new MonGroupe("monGroupe");
sur la priorité des threads du groupe ou sur le Thread t = new MonThread(tg, "monThread");
fait que les thread soient des démons ou non t.start();
‰ Cette classe n’est pas très utile et on se limite
ici à l’essentiel

R. Grin Java : threads page 87 R. Grin Java : threads page 88

ThreadGroup et exceptions ThreadGroup et exceptions


class MyThreadGroup extends ThreadGroup {
public MyThreadGroup(String s) {
‰ La classe ThreadGroup contient la méthode super(s);
public void uncaughtException(Thread t, }
Throwable e)
qui est exécutée quand un des threads du groupe public void uncaughtException(Thread t, Throwable e) {
// On met ici le traitement qui doit être exécuté
est stoppé par une exception non saisie // si un des threads du groupe reçoit une exception
‰ On peut redéfinir cette méthode pour faire un // non attrapée
traitement spécial sur les autres threads System.err.println("uncaught exception: " + e);
}

R. Grin Java : threads page 89 R. Grin Java : threads page 90

15
ThreadLocal

‰ ThreadLocal permet d'associer un état local


(typiquement une variable static private) à
chaque thread sans créer d'instances différentes Timers
‰ Pas étudié en détails dans ce cours

R. Grin Java : threads page 91 R. Grin Java : threads page 92

Classes Timer et TimerTask Exemple d'utilisation de timer


‰ Ces 2 classes du paquetage java.util final long debut =
System.currentTimeMillis();
permettent de lancer l'exécution de tâches à
TimerTask afficheTemps =
des intervalles donnés
new TimerTask() {
‰ TimerTask a une méthode run() qui public void run() {
détermine la tâche à accomplir System.out.println(
‰ Timer détermine quand seront exécutées les System.currentTimeMillis()- debut);
tâches qu'on lui associe }
};
‰ Dans les 2 classes des méthodes cancel()
Timer timer = new Timer();
permettent d'interrompre une tâche ou toutes Timer.schedule(afficheTemps, 0, 2000);
les tâches associées à un timer
R. Grin Java : threads page 93 R. Grin Java : threads page 94

Timers et swing
‰ Pour utiliser un timer qui modifie l'affichage
en Swing, il faut utiliser la classe
javax.swing.Timer Utiliser des classes non sûres
‰ Cette classe utilise le thread de distribution vis-à-vis des threads
des événements pour faire exécuter les
tâches

R. Grin Java : threads page 95 R. Grin Java : threads page 96

16
Utiliser des classes non sûres Exemple des collections
‰ Si c’est possible, synchroniser explicitement
les accès aux objets partagés en construisant ‰ Les nouvelles collections (Java 2) ne sont
des classes qui enveloppent les classes non pas sûres vis-à-vis des threads
sûres, avec des méthodes synchronisées ‰ Ça permet
(illustré par les collections)
n d’améliorer les performances en
‰ Sinon, synchroniser les accès au niveau des
environnement mono-thread
clients de ces classes ; c’est plus difficile et
moins pratique n davantage de souplesse en environnement

‰ On peut aussi s’arranger pour que les multi-threads : par exemple, pour ajouter
méthodes non sûres ne soient appelées que plusieurs objets, on peut n’acquérir qu’une
par un seul thread (illustré par swing et le thread seule fois un moniteur
de distribution des événements)
R. Grin Java : threads page 97 R. Grin Java : threads page 98

Collections synchronisées Protection des collections non


synchronisées
‰ L’API des collections permet d’obtenir une
collection synchronisée à partir d’une ‰ Il faut synchroniser explicitement les
collection non synchronisée, par exemple modifications des collections :
avec la méthode static private ArrayList al;
Collections.synchronizedList . . .
Synchronized(al) {
al.add(…); Avantage sur
Vector : une
al.add(…);
seule acquisition
} de moniteur
pour plusieurs
modifications
R. Grin Java : threads page 99 R. Grin Java : threads page 100

Protection des collections non


synchronisées
‰ Cette technique n’est pas toujours possible si
les classes non synchronisées font appel
elles-mêmes à des objets non-protégés
auxquelles on ne peut accéder
‰ En ce cas, le plus souvent le plus simple est
de reprendre le source des classes s’il est
disponible

R. Grin Java : threads page 101

17

Das könnte Ihnen auch gefallen