0% ont trouvé ce document utile (0 vote)
133 vues55 pages

Java: Maîtriser les Threads et Runnable

Ce document décrit les concepts de base des threads en Java, notamment la création et l'exécution de threads, l'interface Runnable, les méthodes de la classe Thread et les états possibles d'un thread. Le document contient de nombreuses informations sur la programmation multithread en Java.

Transféré par

Fa
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
133 vues55 pages

Java: Maîtriser les Threads et Runnable

Ce document décrit les concepts de base des threads en Java, notamment la création et l'exécution de threads, l'interface Runnable, les méthodes de la classe Thread et les états possibles d'un thread. Le document contient de nombreuses informations sur la programmation multithread en Java.

Transféré par

Fa
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Les

Threads en Java

1
Concepts
•  Thread:
–  Unité de programme qui est exécuté en parallèle
avec le reste des programmes
•  Processus en parallèle
–  Plusieurs processus sont exécutés en même
temps
•  Créer, lancer et terminer un thread
•  Interface Runnable
•  SynchronisaHon des threads
2
Interface Runnable
•  Une méthode run() exigée:
public interface Runnable
{
void run();
}

•  ImplantaHon:
public class MyRunnable implements Runnable
{
public void run()
{
// Task statements go here
. . .
}
}

3
Créer un thread
Un Thread est une classe qui implante Runnable

1. Créer une sous-classe de Thread (doit aussi implanter run() )

class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
[Link] = minPrime;
}

public void run() {
// compute primes larger than minPrime
. . .
}
}

UHlisaHon:
PrimeThread p = new PrimeThread(143);
[Link]();

4
Créer un thread
2. Un thread est créé à base d’un objet
Runnable

Runnable r = new MyRunnable();


Thread t = new Thread(r);
[Link]();

5
Classe Thread
public class Thread extends Object
implements Runnable

Constructeurs:
Thread()
Thread(Runnable target)
Thread(String name)

6
Classe Thread
•  Méthodes:
sta,c int ac,veCount(): nb. de threads de ce groupe
void destroy()
void interrupt()
sta,c void sleep(long millis)
void start()
sta,c void yield(): faire une pause et perme_re aux autres
threads d’exécuter (traitement de deadlock)

7
ExplicaHon de thread

Séquence d’acHons
•  Bien ordonnée en parallèle
8
Types de threads

En Java, on distingue deux grandes catégories de threads: ceux dits utilisateurs


et les démons.

Les threads utilisateurs se terminent lorsque les instructions dans le corps de leur
méthode run ( ) sont toutes exécutées.

Un thread démon continue indéfiniment si aucune précaution n’a été prise pour
l’arrêter.

Nous commencerons par étudier les threads dits utilisateurs pour terminer sur
un bref aperçu portant sur les threads démons.

9
États d’un Thead

10
Détecter l’état d’un Thread
•  [Link]():
–  NEW : lors de sa créaHon
–  RUNNABLE: lorsqu'on invoque la méthode start(),
le thread est prêt à travailler.
–  BLOCKED : lorsque le thread a effectué toutes ses
tâches ; on dit aussi qu'il est « mort ».
–  WAITING : lorsque le thread est en a_ente
indéfinie.
–  TIMED_WAITING: : lorsque le thread est en pause
(quand vous uHlisez la méthode sleep(), par
exemple).
–  TERMINATED
11
Constructeurs de Thread

La classe Thread dispose de plusieurs constructeurs que vous pouvez utiliser


aisément pour instancier des objets threadés:
/*crée un Thread dont le nom est généré automatiquement (aléatoirement)*/
public Thread ( )
/*target est le nom de l’objet dont la méthode run ( ) est utilisée pour
lancer le thread*/
public Thread (Runnable target )
/*on précise l’objet et le nom du thread*/
public Thread (Runnable target, String name)
/*on ne précise que le nom du thread*/
public Thread (String name)
Il existe aussi des constructeurs de groupe de Threads que nous allons voir plus tard.

12
Méthodes de la classe Thread

/*met fin au thread brutalement, à n’utiliser qu’en dernier recours*/


void destroy ( )
/*renvoie la priorité du thread*/
int getPriority ( )
/*retourne le nom du thread*/
String getName( )
/*pour interrompre le thread*/
void interrupt ( )
/*teste si le thread courant a été interrompu*/
static boolean interrupted ( )
/*attendre la mort du thread*/
void join ( ) | void join (long millis) | void join (long millis, int nanos)
/*redémarre le thread :cette méthode est DEPRECIEE*/
void resume ( )
/*méthode contenant le code à exécuter par le thread*/
void run ( )
13
Méthodes de la classe Thread

/*changer la priorité du thread*/


void setPriority (int newpriotity)
/*mettre en veille le thread*/
static void sleep (long millis) | static void sleep (long millis, int nanos)
/*démarre l’exécution du thread*/
void start ( )
/*renvoie le nom du thread, sa priorité et le groupe auquel il appartient*/
String toString ( )
/*renvoie un booléen qui indique si le thread est actif ou non*/
boolean isAlive ( )
/*renvoie un objet qui encapsule le groupe auquel le thread appartient*/
ThreadGroup getThreadGroup ( )
/*indique à l’interpréteur que le thread peut être suspendu pour permettre à
d’autres threads de s’exécuter*/
void yield ( )

14
Premier Thread avec [Link]

(programme simple qui simule l’exécution de deux threads)

public class FirstThread extends Thread { sortie

FirstThread (String name) { 0 thread 1


super (name ); 0 thread 2
} 1 thread 1
public void run ( ) { // code à exécuter par chaque thread 1 thread 2
try { for ( int i = 0;i < 5; i++) 2 thread 1
{ [Link] .println (i+" "+ [Link] ( ) ) ; 2 thread 2
[Link] ((int) [Link] ( )*10) ;// mise en attente 3 thread 1
} 3 thread 2
} 4 thread 1
catch (InterruptedException e) { } 4 thread 2
}
public static void main(String [ ] args) {
new FirstThread("thread 1").start ( ) ; //le code lancé par start ( ) est le code de run ( )
new FirstThread("thread 2").start ( ) ;
} 15
Notes

Un appel de la méthode start ( ) dans une instruction de la forme


new FirstThread("thread 1").start ( ) ; assure qu’un thread sera bien pris en compte
par la machine virtuelle et par le système d’exploitation puis lance l’exécution de la
méthode run ( ) de l’objet thread correspondant.

L’usage de la méthode statique sleep (long millis) nous permet de voir que les deux
threads s’exécutent en apparente simultaneité.
Cette méthode peut lever une exception de type InterruptedException qu’il faut donc
intercepter et capturer.
La méthode start ( ) ne peut être appelée q’une seule fois pour un thread donné, sinon
une exception de type IllegalThreadStateException est levée.

Il est possible d’appeler la méthode run ( ) pour chaque thread mais cela entraîne l’
exécution complète du thread 1 puis celle complète du thread 2. l’appel de sleep
entraînerait alors l’exécution d’autres threads autres que ceux –ci, donc ralentissement
de l’exécution de notre programme.
16
Deuxième Thread avec [Link]
(le premier exemple réalisé ici avec l’interface Runnable)

public class SecondThread implements Runnable { sortie


String name;
SecondThread(String name){ 0 thread 1
[Link] = name ); } 0 thread 2
public void run ( ) { // code à exécuter par chaque thread 1 thread 1
try { for ( int i = 0;i < 5; i++) 1 thread 2
{ [Link] .println (i+" "+ [Link] ) ; 2 thread 1
[Link] ((int) [Link] ( )*10) ;// mise en attente 2 thread 2
} 3 thread 1
} 3 thread 2
catch (InterruptedException e) { } 4 thread 1
} 4 thread 2
public static void main(String [ ] args) {
SecondThread t1 = new SecondThread("thread 1") ;
SecondThread t2 = new SecondThread("thread 2") ;
Thread thread1 = new Thread (t1); [Link] ( ) ;
Thread thread2 = new Thread (t2); [Link] ( ) ; } 17
0 thread 1
InterrupHon des Threads 0 thread 2
1 thread 1
1 thread 2
(bloquer un thread pour laisser un autre continuer)
2 thread 1
public class TestInterrupt extends Thread { 3 thread 1
long attente; 2 thread 2
TestInterrupt (String name, long attente) 4 thread 1
{ super(name); [Link] = attente;} thread 1 interrompu true
public void run( ){ 3 thread 2
try { for (int i = 0;i < 10;i++) thread 1 redémarré
{ [Link] .println (i+ " "+[Link] ( ) ) ; [Link] (attente) ; 5 thread 1
if (i = = 4) 6 thread 1
{[Link]( ).interrupt ( ); 7 thread 1
4 thread 2
boolean trv = [Link]( ).isInterrupted ( );
8 thread 1
[Link] .println ([Link]() +" "+"interrompu "+ trv) ;
thread 2 interrompu true
} }
9 thread 1
if ([Link]( ).IsInterrupted ( )) thread 2 redémarré
{ [Link] ( ) ;
5 thread 2
[Link] .println ([Link] ( ) +" "+"redémarré") ; 6 thread 2
}}} 7 thread 2
catch (InterruptedException e) { } } 8 thread 2
public static void main(String[] args) { 9 thread 2
new TestInterrupt("thread 1",5).start ( ) ; 18
new TestInterrupt("thread 2",10).start ( ) ; }}
Notes importantes (1/2)

L’interruption d’un thread se contente d’attirer son attention.


L’appel de la méthode interrupt ( ) n’entraîne pas automatiquement
A_enHon l’interruption du thread courant . Il ne s’agit qu’une demande.
Cette méthode entre en vigueur que si le thread entre en
sommeil ou et en attente (via l’appel de sleep (…) ou wait (…)).

Comme la méthode interrupt( ) ne fait que placer un indicateur


de demande d’arrêt, il est tout à fait bon de connaître l’état de
celui-ci à un instant donné en appelant la méthode isInterrupted ( ).
Si la méthode interrupt ( ) a été appelée pendant que le thread
n’était pas en sommeil ou en attente, aucune
InterruptedException ne peut être générée.
La méthode non statique isInterrupted ( ) permet de voir si le
thread correspondant est effectivement interrompue et alors l’
appel de interrupted( ) repositionne l’indicateur à false (pour
redémarrer le thread).
19
Notes importantes (2/2)

Si un thread est bloqué, il ne peut pas déterminer s’il est interrompu. C’est à ce moment
que la classe InterruptedException intervient. Lorsque la méthode interrupt est appelée
sur un thread bloqué, l’appel bloquant (comme sleep ou wait) est terminé par une
InterruptedException.

Pour savoir si un thread est couramment actif c’est-à-dire qu’il est soit exécutable, soit
bloqué, utilisez la méthode boolean isAlive ( ). Elle renvoie true si le thread est
exécutable ou bloqué et false si le thread est nouveau et pas encore exécutable ou si le
thread est mort.

20
[Link]

Par défaut un thread appartient (est créé) au groupe (de threads) courant càd
celui qui l’a créé.
Il faut savoir que le premier thread que l’on rencontre est la méthode main.
Par défaut donc, un thread est créé dans ce groupe.

Mais il est possible de créer des groupes de threads autre que le groupe courant, et à
chacun, associé un certain nombre de threads.
Dans ce cas, il sera plus facile d’interrompre un ensemble de threads, en
interrompant simplement le groupe.
Pour cela, on crée une instance de la classe ThreadGroup avec l’un des constructeurs:

ThreadGroup ( String name) // groupe de threads de nom name


ThreadGroup ( ThreadGroup parent, String name) // sous-groupe d’ un autre groupe
L’ajout d’un thread au groupe peut se faire avec le constructeur:
Thread (ThreadGroup g, String name)
21
Thread démon

Un thread démon est un thread qui n’a aucun autre but que de servir d’autres threads.
L’exécution de tels threads peut se poursuivre même après l’arrêt de l’application
qui les a lancés. On dit qu’ils s’exécutent en tâche de fond.

Une application dans laquelle les seuls threads actifs sont des démons est
automatiquement fermée.
Un thread doit toujours être créé comme thread standard, puis il peut être
transformé en thread démon grâce à un appel de la méthode setDaemon ( true).
Mais cet appel doit se faire avant le lancement du thread sinon une exception
de type IllegalThreadStateException est levée.
Un thread démon dépend du thread parent qui l’a lancé et s’exécute en arrière plan
de ce dernier.

22
SynchronisaHon

+2 X -100

*1.02 -20

Accès conflictuels:
écrire lire
(put - producer) (get -consumer)
23
SynchronisaHon pour un bloc

Ici, il s’agit d’interdire à deux threads différents d’accéder simultanément


à un même objet, en plaçant le mot clé synchronized pour le bloc concerné.

Dans une instruction telle que:


synchronized (expression) { // du code}
où expression repère un objet quelconque, le système Java pose un verrou
sur cet objet pendant l’exécution du bloc. Aucun autre bloc synchronized sur
cet objet APPARTENANT A UN AUTRE THREAD ne peut être exécuté
et un tel bloc ne peut être lancé qu’après avoir obtenu ce verrou.

On définit ainsi une section critique.

24
Premier exemple SynchronisaHon de bloc (1/5)

Considérons une classe Compte permettant de gérer les comptes de clients dans une
quelconque banque disposant de plusieurs agences.
Pour une gestion efficace et sécurisée des comptes client, il ne faudrait pas permettre
par exemple que deux (au moins) transactions s’effectuent simultanément sur un même
compte à un instant donné (exemple: le retrait et le dépôt).

Les opérations de retrait et de dépôt sont gérées séparément par deux threads distincts.

Notre travail sera de créer ces deux threads de telle sorte qu’ils ne pourront jamais s’
exécuter simultanément sur un même compte.

25
Premier exemple SynchronisaHon de bloc (2/5)

([Link])
package [Link];
public class Compte {
private double solde;
private double decouvert;
public Compte(double solde, double decouvert){
[Link] = solde;
[Link] = decouvert;
}
public void deposer (double montant){
[Link] += montant;
}
public void retirer (double montant){
if (montant + decouvert <= solde)
[Link] -= montant;
}
public double getSolde ( ) {
return solde ;
26
}}
Premier exemple SynchronisaHon de bloc (3/5)

(ThreadCompteDepot .java)

package [Link];
public class ThreadCompteDepot extends Thread {
private Compte c;
private double depot;
public ThreadCompteDepot (String name, Compte c, double depot){
super (name);
this.c =c;
[Link] = depot;
}
public void run ( ){
synchronized (c) { // fondamental: on pose un verrou sur le compte
try {
[Link] ([Link] ( ) + ":avant le depot: "+[Link]( ));
[Link] ([Link]);
[Link] (1000);
[Link] ([Link]() + ":apres le depot: "+[Link]( )); ;
}
27
catch (InterruptedException e) { [Link]("retrait avorte"); } } }}
Premier exemple SynchronisaHon de bloc (4/5)

(ThreadCompteRetrait .java)

package [Link];
public class ThreadCompteRetrait extends Thread {
private Compte c;
private double retrait;
public ThreadCompteRetrait (String name, Compte c, double retrait){
super (name);
this.c = c;
[Link] = retrait;
}
public void run ( ){
synchronized (c) { // fondamental: on pose un verrou sur le compte
try {
[Link] ([Link] ( ) + ":avant le retrait:"); S.O.P([Link] ( ));
[Link] ([Link]);
[Link] (1000);
[Link] ([Link]() + ":apres le retrait:"); S.O.P([Link] ( ));
}
28
catch (InterruptedException e) { [Link] (" depot avorte"); } } }}
Premier exemple SynchronisaHon de bloc (5/5)

(TestThreadsCompte .java)

package [Link]; Le choix du thread à exécuter


en premier lieu est aléatoire,
public class TestThreadsCompte { les deux threads étant de
public static void main(String [ ] args) { même priorité.
Compte u = new Compte (5000,100) ;
ThreadCompteRetrait tcr = new ThreadCompteRetrait ("retrait",u,2000);
ThreadCompteDepot tcd = new ThreadCompteDepot ("depot",u,1500);
[Link] ( ) ;
retrait: avant le retrait votre solde est: 5000.0
[Link] ( ) ; } BON retrait: apres le retrait votre solde est: 3000.0
}
depot: avant le depot: votre solde est: 3000.0
depot: apres le depot: votre solde est: 4500.0

retrait:avant le retrait votre solde est: 5000.0 Si vous ne synchroniser pas le Compte
depot:avant le depot: votre solde est: 3000.0 dans les deux threads, vous aurez un
retrait: apres le retrait votre solde est: 4500.0 solde erroné .
depot:apres le depot: votre solde est: 4500.0
Mr NDONG. FST/UCAD 29
Deuxième Exemple de bloc synchronisé

On considère une classe qui permet d’inverser les éléments d’un tableau d’entiers.
Mais avant que l’inversion ne se fasse, les éléments du tableau doivent être incrémentés
d’une valeur égale à l’indice de l’élément en cours. Et après, l’inversion pourra se faire
après un certain délai.
On disposera d’une méthode affiche qui nous permettra d’envoyer sur la console
les éléments du tableau inversé.
Mais ici, nous avons un problème à gérer:
Avant que la méthode affiche n’accède au tableau pour l’afficher, il faudra que la
méthode qui se charge de l’incrémentation et de l’inversion finisse carrément son
travail, sinon l’affichage sera complètement faux.

Regardons maintenant comment on peut passer d’un mauvais exemple vers un cas où
les données seront correctes.

30
Exemple de bloc NON synchronisé (1/4)

(la classe qui gère l’inversion et l’affichage du tableau)

class TabInverse {
int t [ ];
int [ ] inverse (int tableau [ ])
{ t = new int [[Link] ];

for (int i = 0;i < [Link] ;i++) tableau [i]+=i;


try {[Link] (1000) ;} // pour marquer une pause entre l’incrémentation
catch (InterruptedException er) { }// et l’inversion des éléments du tableau
for (int i = 0;i < [Link] ;i++)
t [[Link] -i-1] = tableau[i];
return t;
}
void affiche ( ){
for (int i = 0 < [Link] ;i++)
[Link] .print (t[i]+":::::") ;
}
}
31
Exemple de bloc NON synchronisé (2/4)

/*cette classe permet de créer un thread qui n’accédera qu’à la methode inverse
Cet objet peut donc manipuler simultanément le tableau qu’un autre thread */
class EssaiSynchroInverse extends Thread{
TabInverse inv;
int tab[ ];
public EssaiSynchroInverse (String name, TabInverse inv, int tab [ ] )
{ super (name);
[Link] = inv;
[Link] = tab;
}
public void run ( )
{[Link] .println ([Link] ( ) ) ;
[Link] (tab) ;
try {[Link] (1000) ;}
catch (InterruptedException er) { }
[Link] .println("FIN de "+[Link] ( ) ) ;
}} 32
Exemple de bloc NON synchronisé (3/4)

/*cette classe permet de créer un thread qui n’accédera qu’à la methode affiche
Cet objet peut donc manipuler simultanément le tableau qu’un autre thread */
class EssaiSynchroAffiche extends Thread{
TabInverse inv;
int tab[ ];
public EssaiSynchroAffiche (String name, TabInverse inv, int tab[] )
{ super (name);
[Link] = inv;
[Link] = tab;
}
public void run ( )
{ [Link] .println ([Link] ( ) ) ;
[Link] ( ) ;
try {[Link](1000) ;}
catch (InterruptedException er) { }
[Link] .println ("FINITION de "+ [Link] ( ) ) ;
}}
33
Exemple de bloc NON synchronisé (4/4)

/*pour tester l’accès simultané à un même objet tableau*/ ThreadInv


public class TestSynchroBloc {
ThreadAff
static int t [ ] = {1,2,3,4,5}; 0::0::0::0::0::
public static void main (String [ ] args) throws Exception{
FINITION de ThreadAff
TabInverse ti = new TabInverse ( ); FIN de ThreadInv

EssaiSynchroInverse es = new EssaiSynchroInverse("ThreadInv",ti, t);


EssaiSynchroAffiche ess = new EssaiSynchroAffiche("ThreadAff",ti, t);
[Link] ( ) ;
[Link] ( ) Ici, on crée deux threads es et ess qui accèdent au tableau t
simultanément. Le thread ess va vouloir afficher un résultat qui
[Link] ( ) ;
} n’est pas encore entièrement connu, puisque es n’a pas terminé
l’incrémentation et l’inversion.
}
Le résultat affiché sera donc erroné.

Pour corriger ce défaut, il faut poser un verrou sur l’objet tableau partagé et sur inv…34
Deuxième Exemple de bloc synchronisé

(voici comment il faut implémenter la classe TabInverse)


Résultat attendu
class TabInverse {
int t [ ]; ThreadInv
int [ ] inverse (int tableau [ ]) ThreadAff
{ t = new int [[Link] ]; 9::7::5::3::1::
synchronized (t) { // verrouillage du tableau FINITION de ThreadAff
FIN de ThreadInv
for (int i = 0;i < [Link] ;i++) tableau [i]+=i;
try {[Link] (1000) ;}
catch (InterruptedException er) { }
for (int i = 0;i < [Link] ;i++)
t [[Link] -i-1] = tableau[i];
}
return t;}
void affiche ( ){
synchronized (t) {
for (int i = 0 < [Link] ;i++) [Link] .print (t[i]+":::::") ;
35
} }}
SynchronisaHon de méthodes

Chaque fois que deux threads s’exécutent en même temps, il faut souvent prendre des
mesures adéquates pour qu’ ils n’accèdent pas simultanément à une même variable.

Le principe d’exclusion mutuelle doit être assuré sur le partage simultané d’objet
(pour assurer la cohérence des données) par l’utilisation de méthodes dites synchronisées.
Ce principe est assuré lorsqu’une méthode est déclarée avec le mot clé synchronized.
Une méthode synchronisée appartient à un objet quelconque, pas forcément à un thread.

Lorsqu’une méthode déclarée synchronized, est en cours d’exécution par un thread, tous
les autres threads qui en auraient besoin doivent attendre la fin de son exécution.

Lorsque synchronized est utilisé comme modifieur de méthode d’instance, une instruction
telle que: synchronized void method ( ) {…….} est équivalente à:
void method ( ) {
synchronized (this) { ….}
}
36
Exemple de SynchronisaHon de méthodes (1/4)

Considérons un exemple qui permet de réaliser deux opérations ( addition et affichage)


sur deux champs d’une instances d’un objet (syn) d’une classe Synchro. Il s’agit
d’incrémenter la valeur du premier champ (de 1) et de faire la somme avec le deuxième
champ. On souhaite que les deux champs sont accédés dans les deux méthodes et utilisés
de façon concurrente par trois threads que nous créerons dans le main.

Regardons d’abord comment les valeurs des deux champs sont érronées et incohérentes si
l’exclusion mutuelle n’est pas bien gérée: l’incohérence s’explique par le fait que le s
trois threads manipulent les deux champs pèle mêle.

Nous verrons alors une version qui dégage une utilisation correcte de la valeur de ces
variables.

37
Exemple de SynchronisaHon de méthodes (2/4)

class Synchro {
int n, som;
public Synchro (int n,int som) { this.n =n; [Link] =som;
}
void addition ( ){ // methode non synchronisée
[Link] .print ("n++= "+(n++) +" suivi de ") ;
try { [Link] (222) ;}
catch (InterruptedException t){ }
som += n; [Link] .println(" et som="+som) ;
}
void affiche ( ){ // methode non synchronisée
[Link] .print("affiche: n= " +(++n)+" et ");
try {[Link] (222) ;}
catch (InterruptedException t){ }
[Link] ("affiche :som= "+som);
}}
38
Exemple de SynchronisaHon de méthodes (3/4)

class TestAddition extends Thread {


Synchro syn;
public TestAddition (String t, Synchro syn) {super (t); [Link] =syn;}
public void run ( ){ [Link] ([Link] ( )+" " ) ;
try { [Link] ( ) ; [Link](522); }
catch (InterruptedException er){ }
[Link]("FIN "+[Link] ( ) ) ;
}
} // fin de TestAddition
class TestAffiche extends Thread{
Synchro syn;
public TestAffiche (String t, Synchro syn){super (t); [Link] =syn;}
public void run ( ){ [Link]("******"+[Link] ( )+"******" ) ;
try { [Link] ( ) ; [Link](52); }
catch (InterruptedException er) { }
[Link]("fin "+[Link] ( ) ) ; }
} // fin de TestAffiche 39
Exemple de SynchronisaHon de méthodes (4/4)

public class TestSynchro {


public static void main (String [ ] args) {
Synchro sy = new Synchro (1,1);
TestAffiche taf = new TestAffiche (" ThreadAffiche",sy);
TestAddition tad1 = new TestAddition ("ThreadAdd1",sy);
TestAddition tad2 = new TestAddition ("ThreadAdd2", sy);
TestAddition tad3 = new TestAddition ("ThreadAdd3", sy);
[Link] ( );
[Link] ( );
[Link] ( );
[Link] ( ) ;

}
}

40
SorHe de l’ exemple

Pour rectifier le problème encouru, il faudra synchroniser les méthodes addition( ) et


affiche( ) de la classe Synchro , en les déclarant avec le mot clé synchronized.

ThreadAdd1 n++ = 1 ThreadAdd1 n++= 1


suivi de ThreadAdd2 n++ = 2 suivi de ThreadAdd2
suivi de ****** ThreadAffiche****** ****** ThreadAffiche******
affiche: ++n= 4 et ThreadAdd3 n++= 4 suivi de ThreadAdd3 et som = 3 exact
n++= 2 suivi de et som=6
et som = 6 affiche: ++n= 4 et affiche :som= 6
et som=11 n++= 4 suivi de
affiche :som= 11 Erronée: on s’ fin ThreadAffiche
et som = 16 attendait à avoir 3 FIN ThreadAdd1
fin ThreadAffiche et som=11
FIN ThreadAdd1 FIN ThreadAdd2
FIN ThreadAdd2 FIN ThreadAdd3
FIN ThreadAdd3
41
AmélioraHon de l’exemple

/*pour que les données ne sont plus érronées et falsifiées*/


class Synchro {
int n, som;
public Synchro (int n, int som) { this.n =n; [Link] =som;
}
synchronized void addition ( ){ // methode synchronisée,donc bloquant
[Link] .print ("n++= "+n++ +" suivi de ") ;
try { [Link](222) ;}
catch (InterruptedException t){ }
som+=n; [Link] .println(" et som="+som) ;
}
synchronized void affiche ( ){ // methode synchronisée,donc bloquant
[Link] .print("affiche: n= " +(++n)+" et ");
try {[Link] (222) ;}
catch (InterruptedException t){ }
[Link] ("affiche :som= "+som);
}}
42
A_ente et noHficaHon:
les méthodes wait ( ) et noHfyAll ( )
(ces méthodes doivent êtres lancées dans des blocs ou méthodes synchronisés)

Attention: c’est des méthodes de la super classe Object et non de la classe Thread.
Elles implantent des mécanismes de demande de verrou et d’ avertissement de libération
de ce verrou.
une méthode synchronisée peut appeler la méthode wait( ) de l’objet dont elle possède
le verrou, pour:
- rendre le verrou à l’environnement qui peut alors le donner à une autre méthode
synchronisée,
- mettre en attente le thread correspondant.
Une méthode synchronisée peut appeler la méthode notifyAll ( ) d’un objet afin de
prévenir tous les threads en attente sur cet objet et de leur donner la possibilité de
s’exécuter.
NB: notifyAll( ) permet de débloquer un wait ( ) sur un objet où notify( ) a été lancé.
La méthode notify ( ) prévient un seul thread. Avant de faire un notify ( ) ou notifyAll()
il faut changer la condition de boucle qui mène au wait ( ).
43
Exemple: producteur - consommateur. (1/4)

Un producteur est un thread qui dépose des jetons numérotés dans un chapeau
qui ne peut contenir qu’un seul jeton. Un consommateur prend ce jeton qui doit
être présent dans le chapeau. Donc:
-  le producteur doit s’arrêter de déposer des jetons lorsqu’il y en a déjà un et doit
être informé qu’un jeton a été retiré.
-  le consommateur ne peut pas prendre de jetons s’il n’y en a pas (arrêt du
consommateur) et doit être informé lorsqu’un jeton a été déposé.
L’objet le plus à même pour avertir le producteur et le consommateur est le
chapeau lui-même.

44
Exemple: producteur - consommateur. (2/4)

(fichier [Link])

public class Producteur extends Thread{


private Chapeau chapeau;
private int number; // le numéro du jeton à déposer
public Producteur (Chapeau chapeau, int number) {
[Link] = chapeau;
[Link] = number;
}
public void run ( ){
for (int i = 0;i < 5 ;i++) {
[Link] (i);
[Link] .println ("le producteur N° "+[Link] +" a mis "+i) ;
try { [Link](1000);}
catch (InterruptedException e){ }
}
}
}
45
Exemple: producteur - consommateur. (3/4)

(fichier [Link])

public class Consommateur extends Thread {


private Chapeau chapeau;
private int number;
public Consommateur (Chapeau chapeau, int number) {
[Link] = chapeau;
[Link] = number;
}
public void run( ){
int value = 0;
for (int i = 0; i < 5 ; i++) {
value = [Link] ( );
[Link] .println ("le consommateur N° "+[Link] +" a pris "+value) ;
try {[Link] (1000);}
catch (InterruptedException e){ }
}
}
} 46
Exemple: producteur - consommateur. (4/4)

(fichier [Link])
public class Chapeau {
private int contenu; /*une classe pour tester*/
private boolean permis = false; public class TestProductCons {
public synchronized int get ( ){ public static void main(String [ ] args) {
while (permis == false) Chapeau chap = new Chapeau ( );
{ try {wait ( ); // rendre le verrou } Producteur p = new Producteur(chap,1);
catch (InterruptedException e){ } Consommateur c=
} new Consommateur(chap,1);
permis = false; notifyAll ( ); [Link] ( ) ;
return contenu; } [Link] ( );
public synchronized void put (int value){ }
while (permis == true) }
{ try { wait ( );}
catch (InterruptedException er) { } }
contenu = value; permis = true;
notifyAll ( );// attribuer le verrou à un autre
// qui peut alors s’exécuter
47
Exemple: producteur – consommateur: sorHe

Voici la sortie du programme précédent avec un seul producteur et un seul


consommateur.

le producteur N° 1 a mis 0
le consommateur N° 1 a pris 0
le producteur N° 1 a mis 1
le consommateur N° 1 a pris 1
le producteur N° 1 a mis 2
le consommateur N° 1 a pris 2
le producteur N° 1 a mis 3
le consommateur N° 1 a pris 3
le producteur N° 1 a mis 4
le consommateur N° 1 a pris 4

48
Exercice: producteur – consommateur

Reprendre l’exemple précédent en créant trois (3) producteurs et trois (3)


consommateurs. Il y a un truc à faire.
/*une classe de test pour 3 producteurs et 3 consommateurs*/
public class Test3ProductCons {
public static void main(String [ ] args) {
Chapeau chap = new Chapeau ( );
Producteur2 p1 = new Producteur2 (chap,1);
Producteur2 p2 = new Producteur2 (chap, 2);
Producteur2 p3 = new Producteur2 (chap, 3);
Consommateur2 c1 = new Consommateur2 (chap, 1);
Consommateur2 c2 = new Consommateur2 (chap, 2);
Consommateur2 c3 = new Consommateur2 (chap, 3);
[Link] ( ) ; [Link] ( ) ; [Link] ( ) ;
[Link] ( );[Link] ( ); [Link] ( );
}} 49
Exercice: producteur – consommateur: Corrigé (1/2)
(on ne modifie que le producteur et le consommateur)

(fichier [Link])

public class Producteur2 extends Thread{


private Chapeau chapeau;
private int number;
public Producteur2 (Chapeau chapeau, int number) {
[Link] = chapeau;
[Link] = number;
}
public void run ( ){
synchronized (chapeau) {// il faut absolument synchroniser ce bloc
for (int i = 0;i < 5 ;i++) {
[Link] (i);
[Link] .println ("le producteur N° "+[Link] +" a mis "+i) ;
try { [Link](1000);}
catch (InterruptedException e){ }
} // fin du bloc synchronized
} }} 50
Exercice: producteur – consommateur: Corrigé (2/2)

(fichier [Link])

public class Consommateur2 extends Thread {


private Chapeau chapeau;
private int number;
public Consommateur2 (Chapeau chapeau, int number) {
[Link] =chapeau;
[Link] =number;
}
public void run ( ){
int value = 0;
synchronized (chapeau) { // il faut absolument synchroniser ce bloc
for (int i = 0;i < 5 ;i++) {
value = [Link] ( );
[Link] .println("le consommateur N° "+[Link] +" a pris "+value) ;
try {[Link] (1000);}
catch (InterruptedException e){ }
} // fin du bloc synchronized
} }} 51
Exercice: producteur – consommateur: sorHe

le producteur N° 1 a mis 0
le consommateur N° 1 a pris 0
le producteur N° 2 a mis 0
le consommateur N° 1 a pris 0
le producteur N° 3 a mis 0
le consommateur N° 1 a pris 0
Voici un exemple de sortie le producteur N° 3 a mis 1
du programme de test.
le consommateur N° 1 a pris 1
le producteur N° 3 a mis 2
le consommateur N° 1 a pris 2
le producteur N° 1 a mis 1
le consommateur N° 2 a pris 1
le producteur N° 1 a mis 2
le consommateur N° 2 a pris 2
le producteur N° 1 a mis 3
le consommateur N° 2 a pris 3
le producteur N° 1 a mis 4
………
52
Priorité des Threads

Jusqu’à présent, nous n’avons manipulé que des threads de même priorité.
En théorie, il est permis d’attribuer une certaine priorité à un thread.
Pour cela, on utilise la méthode setPriority (int threadPriority) où le paramètre
transmis en argument est une valeur entière comprise entre [Link]
(correspondant à la valeur 1) et la valeur [Link] (correspondant à 10).

La priorité par défaut est la valeur [Link] ( valeur 5).


La priorité d’un thread est exploité par l’environnement de cette façon:
- lorsqu’il peut donner la main à un thread, l’environnement choisi celui de plus
haute priorité parmi ceux qui sont dans l’état prêt; s’il y a plusieurs candidats
le thread choisi dépendra de l’environnement;
- si un thread plus prioritaire que le thread en cours d’exécution devient prêt, on lui
donne la main (le thread courant passant alors à l’état prêt).

53
Priorité des Threads: Exemple (1/2)

public class ThreadPriority extends Thread{


public ThreadPriority (String name) {super (name); }
public void run ( ){
for (int i = 0; i < 5 ; i++)
{ [Link] ( [Link] ( ) +" valeur de i = "+i);
try { [Link] (1000) ;}
catch ( InterruptedException er ) { }
}
[Link] ([Link] ( ) +" se termine ");
}
public static void main(String[] args) {
ThreadPriority t = new ThreadPriority ("thread # 1");
ThreadPriority p = new ThreadPriority ("thread # 2");
[Link] (7) ; [Link] (Thread.MAX_PRIORITY ) ;
[Link] ( ) ;[Link] ( ) ;
}
} 54
Priorité des Threads: Exemple (2/2)

thread # 1 valeur de i = 0 thread # 1 valeur de i = 0 Le thread thread# 2


thread # 2 valeur de i = 0 thread # 2 valeur de i = 0 étant de priorité
thread # 1 valeur de i = 1 thread # 2 valeur de i = 1 supérieure, son
thread # 2 valeur de i = 1 thread # 1 valeur de i = 1 exécution s’achève
thread # 1 valeur de i = 2 thread # 2 valeur de i = 2 avant celle du thread
thread # 2 valeur de i = 2 thread # 1 valeur de i = 2 thread # 1 lancé
thread # 1 valeur de i = 3 thread # 2 valeur de i = 3 en premier lieu.
thread # 2 valeur de i = 3 thread # 1 valeur de i = 3
thread # 1 valeur de i = 4 thread # 2 valeur de i = 4
thread # 2 valeur de i = 4 thread # 1 valeur de i = 4
thread # 1 se termine thread # 2 se termine
thread # 2 se termine thread # 1 se termine

Ce qu’on aurait si les deux threads étaient de même priorité (celle par défaut).

55

Vous aimerez peut-être aussi