Java Avance
Gestion de processus
Emmanuel ADAM
Université de Valenciennes et du Hainaut-Cambrésis
UVHC/ISTV-LAMIH
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 1 / 30
Plan
1 Présentation
2 Création de processus
3 Transformation en processus
4 Synchronisation
Principes de la synchronisation
Synchronisation de méthode
Mise en veille et Réveil de processus
5 Exemple type : les producteur et les consommateurs
Présentation
Codes
6 Volatilité
7 Bloc synchronisé
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 2 / 30
Présentation
Processus
Présentation
Un processus est un code qui s’exécute “en parallèle”
c’est-à-dire dont l’exécution n’est pas bloquante pour le reste
du programme
Exemple d’utilisation :
Longs calculs lancés en tâche de fond, permettant de lancer
d’autres calculs
L’interaction avec l’utilisateurs (les fenêtres sont des processus)
Le garbage collector (ramasse miettes) est un processus
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 3 / 30
Création de processus
Création de processus
classe Thread
Etendre la classe Thread permet de créer un processus
Le comportement principal du processus est à définir dans la
méthode public void run()
La méthode run() est constituée généralement d’une boucle
longue
longs calculs
attente de données (de l’interface graphique, du réseau, . . .)
Elle est appelée par la méthode start()
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 4 / 30
Création de processus
Exemple de Thread (1/2)
Le programme principal est le processus principal
Exemple de création d’une classe de processus qui affiche son
nom en boucle.
c l a s s UneTache e x t e n d s Thread
{
i n t nbRuns =0;
UneTache ( S t r i n g nom ) { s u p e r ( nom ) ; }
// r e m a r q u e : a p p e l au c o n s t r u c t e u r de Thread
p u b l i c void run ( )
{
while ( true )
{
System . o u t . p r i n t l n ( ” Pour l a ”+ (++nbRuns ) +” e f o i s , mon
nom e s t : ” + getName ( ) ) ;
Thread . y i e l d ( ) ; // p e t i t e p a u s e
} } }
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 5 / 30
Création de processus
Exemple de Thread (2/2)
Le programme principal est le processus principal
Partager les ressources entre 2 processus et le processus
principal
p u b l i c c l a s s MaTache
{
p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] )
{
UneTache t a c h e 1 = new UneTache ( ” t a c h e 1 ” ) ;
UneTache t a c h e 2 = new UneTache ( ” t a c h e 2 ” ) ;
tache1 . s t a r t () ; tache2 . s t a r t () ;
int i = 0;
i n t f i n = 100;
w h i l e ( i <f i n )
{
System . o u t . p r i n t l n ( ” I c i t a c h e p r i n c i p a l e , i=” + i ) ;
i ++;
Thread . y i e l d ( ) ;
}
System . e x i t ( 0 ) ;
}
}
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 6 / 30
Transformation en processus
Transformation en processus
interface Runnable
Si une classe étend une autre classe, elle ne peut étendre
Thread (pas d’héritage multiple)
La solution :
implémenter l’interface Runnable
définir la méthode run()
pour lancer un élément ‘runnable’, créer un Thread prenant en
paramètre cet élément
et démarrer ce processus
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 7 / 30
Transformation en processus
Exemple d’utilisation de Runnable (1/2)
Le programme principal est le processus principal
Exemple de création d’une classe qui compte sans arrêt en
implémentant l’interface Runnable.
c l a s s ObjetComptan t i m p l e m e n t s R u n n a b l e
{
S t r i n g name ;
ObjetComptant ( S t r i n g nom ) { name = nom ; }
p u b l i c void run ( )
{
i n t i =0;
while ( true )
{
System . o u t . p r i n t l n ( ” moi ” + name + ” j ’ en s u i s à ” + ( i
++)) ;
Thread . y i e l d ( ) ;
} } }
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 8 / 30
Transformation en processus
Exemple d’utilisation de Runnable (2/2)
Le programme principal est le processus principal
Créer 2 processus à partir d’objets ‘Runable’
p u b l i c c l a s s MonRunnable
{
p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] )
{
new Thread ( new ObjetComptant ( ” c1 ” ) ) . s t a r t ( ) ;
new Thread ( new ObjetComptant ( ” c2 ” ) ) . s t a r t ( ) ;
int i = 0;
w h i l e ( i <100)
{
System . o u t . p r i n t l n ( ” J e s u i s l a t a c h e p r i n c i p a l e , i=” +
i);
i ++;
Thread . y i e l d ( ) ;
}
System . e x i t ( 0 ) ;
}
}
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 9 / 30
Synchronisation Principes de la synchronisation
Principes de la synchronisation
Problème d’accès mutuel à une même ressource
Supposons deux transporteurs voulant stocker une palette
dans un entrepôt
Un transporteur vérifie la capacité à distance par une appli sur
smartphone et perçoit qu’il reste une place
Un autre transporteur fait de même
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 10 / 30
Synchronisation Principes de la synchronisation
Principes de la synchronisation
Problème d’accès mutuel à une même ressource
Les deux transporteurs arrivent pour stocker leur palette
Un conflit survient !
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 11 / 30
Synchronisation Principes de la synchronisation
Principes de la synchronisation
Solution à l’accès mutuel à une même ressource
L’entrepôt doit gérer les conflits
Dès qu’un transporteur fait une demande, les autres sont mis
en attente tant que la demande n’est pas traitée
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 12 / 30
Synchronisation Principes de la synchronisation
Principes de la synchronisation
Solution à l’accès mutuel à une même ressource
Une réponse est ensuite envoyée :
Soit une autorisation d’accès, soit mise en attente
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 13 / 30
Synchronisation Synchronisation de méthode
Synchronisation de méthode
Eviter l’accès mutuel à une méthode
Pour bloquer l’accès mutuel à une fonction,
celle-ci doit être précédée du mot clé synchronized
Si un processus appelle une fonction synchronisée dans lequel
se trouve déjà un processus, il est bloqué au portes de la
fonction.
Il entrera dans la fonction si :
le processus l’utilisant en sort
et qu’aucun autre processus n’était mis en attente avant lui
le processus l’utilisant est mis en attente (wait())
et qu’aucun autre processus n’était mis en attente avant lui
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 14 / 30
Synchronisation Synchronisation de méthode
Synchronisation de méthode : Exemple (1/3)
/∗ ∗ c l a s s e s i m p l e p o s s é d a n t une f o n c t i o n s y n c h o n i s é e ∗/
public class ClasseSynchro {
C l a s s e S y n c h r o ( ) {}
/∗ ∗ f o n c t i o n s y n c h o n i s é e :
b l o q u e l e s p r o c e s s u s a p p e l a n t s s i l e p a r a mè t r e e s t i m p a i r ∗/
public synchronized void testPair ( int i )
{
i f ( ( i % 2 ) != 0 )
{
System . o u t . p r i n t l n ( ” nb i m p a i r , j e mets en a t t e n t e ” ) ;
try { wait () ; }
catch ( I n t e r r u p t e d E x c e p t i o n e ) { e . printStackTrace () ; }
}
else
{
System . o u t . p r i n t l n ( ” l e nb e s t p a i r ” ) ;
}
System . o u t . p r i n t l n ( ” s o r t i e de l a f o n c t i o n ” ) ;
}
}
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 15 / 30
Synchronisation Synchronisation de méthode
Synchronisation de méthode : Exemple (2/3)
public class ProcessTest e x t e n d s Thread {
int i ;
ClasseSynchro classeS ;
P r o c e s s T e s t ( S t r i n g name , i n t i , ClasseSynchro classeS ){
s u p e r ( name ) ; i = i; classeS = classeS ; }
p u b l i c void run ( ) {
System . o u t . p r i n t l n ( getName ( )+ ” , j e t e s t e ” + i);
classeS . testPair ( i ) ;
System . o u t . p r i n t l n ( getName ( )+ ” , j e s o r s de r u n ” ) ;
}
p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {
C l a s s e S y n c h r o c l a s s e S y n c h r o = new C l a s s e S y n c h r o ( ) ;
P r o c e s s T e s t p1 = new P r o c e s s T e s t ( ” p1 ” , 3 , c l a s s e S ) ;
p1 . s t a r t ( ) ;
P r o c e s s T e s t p3 = new P r o c e s s T e s t ( ” p3 ” , 5 , c l a s s e S ) ;
p3 . s t a r t ( ) ;
t r y { Thread . s l e e p ( 2 0 0 0 ) ; } c a t c h ( E x c e p t i o n e ) {}
P r o c e s s T e s t p2 = new P r o c e s s T e s t ( ” p2 ” , 4 , c l a s s e S ) ;
p2 . s t a r t ( ) ;
} }
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 16 / 30
Synchronisation Synchronisation de méthode
Synchronisation de méthode : Exemple (3/3)
Résultat de l’exécution
moi, p3, je teste la parite de 5
le nb n’est pas pair, je mets en attente
moi, p1, je teste la parite de 3
le nb n’est pas pair, je mets en attente
moi, p2, je teste la parite de 4
le nb est pair
sortie de la fonction
moi, p2, je sors de run
p1 et p3 ne sortent jamais de la méthode run() !
ils restent endormis
modification pour qu’un processus ayant donné un nombre
pair réveille tous les processus mis en attente
ajout de la fonction notify() (réveille un processus)
ou de notifyAll() (réveille tous les processus)
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 17 / 30
Synchronisation Synchronisation de méthode
Réveil de processus : Exemple (1/2)
/∗ ∗ c l a s s e s i m p l e p o s s é d a n t une f o n c t i o n s y n c h o n i s é e ∗/
public class ClasseSynchro {
C l a s s e S y n c h r o ( ) {}
/∗ ∗ f o n c t i o n s y n c h o n i s é e :
b l o q u e l e s p r o c e s s u s a p p e l a n t s s i l e p a r a mè t r e e s t i m p a i r
l e s r e v e i l l e s i un p r o c e s s u s a r r i v e a v e c un p a r a mè t r e p a i r ∗/
public synchronized void testPair ( int i )
{
i f ( ( i % 2 ) != 0 )
{
System . o u t . p r i n t l n ( ” nb i m p a i r , j e mets en a t t e n t e ” ) ;
try { wait () ; }
catch ( I n t e r r u p t e d E x c e p t i o n e ) { e . printStackTrace () ; }
}
else
{
System . o u t . p r i n t l n ( ” l e nb e s t p a i r ” ) ;
n o t i f y A l l () ;
}
System . o u t . p r i n t l n ( ” s o r t i e de l a f o n c t i o n ” ) ;
}
}
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 18 / 30
Synchronisation Synchronisation de méthode
Réveil de processus : Exemple (2/2)
Résultat de l’exécution
Cette fois à l’exécution, on obtient :
moi, p1, je teste la parite de 3
le nb n’est pas pair, je mets en attente
moi, p3, je teste la parite de 5
le nb n’est pas pair, je mets en attente
moi, p2, je teste la parite de 4
le nb est pair
sortie de la fonction
moi, p2, je sors de run
sortie de la fonction
moi, p3, je sors de run
sortie de la fonction
moi, p1, je sors de run
p1 et p3 ne sortent de la méthode run() !
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 19 / 30
Synchronisation Mise en veille et Réveil de processus
Mise en veille et Réveil de processus
wait() and notify()
Placée dans une fonction d’un objet, l’appel à la fonction
wait() met en veille le processus appelant
Placée dans une fonction d’un objet, notify() réveille un
processus mis en veille dans une des procédures de l’objet
Placée dans une fonction d’un objet, notifyAll() réveille tous
les processus mis en veille dans une des procédures de l’objet
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 20 / 30
Exemple type : les producteur et les consommateurs Présentation
Exemple type : les producteur et les consommateurs
Producteurs et Consommateurs : Enoncé
Il existe un Entrepôt de taille limitée (n)
np producteurs produisent régulièrement des produits qu’ils
cherchent à stocker dans l’entrepôt
nc consommateurs tentent régulièrement de retirer des
produits de l’entrepôt
Les producteurs et consommateurs sont des processus
partageant le même entreprôt
le dépôt d’objets et le retrait d’objet doit donc être
synchronisé
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 21 / 30
Exemple type : les producteur et les consommateurs Codes
Producteurs et consommateurs : définition de l’entrepôt I
c l a s s Entrepot {
private int [ ] stockage ;
p r i v a t e i n t t a i l l e , nbDeposes , n b P r i s , n b O b j e t s C o u r a n t s ;
public Entrepot ( int taille ) {
s t o c k a g e = new i n t [ taille ]; taille = taille ;
}
p u b l i c synchronized void depose ( i n t obj ) {
// t q que l e t a b l e a u e s t p l e i n , m e t t r e en p a u s e
w h i l e ( n b O b j e t s C o u r a n t s == ( t a i l l e − 1 ) ) {
// a f f i c h e l e nom du p r o c e s s u s a p p e l a n t e t m i s en p a u s e
System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + ”−−
pause dans l e depot d ’ o b j e t ” ) ;
t r y { wait () ; } catch ( I n t e r r u p t e d E x c e p t i o n e ) { }
}
stockage [ nbObjetsCourants ] = obj ;
n b O b j e t s C o u r a n t s ++; n b D e p o s e s++;
// r e v e i l l e r d e s a u t r e s p r o c e s s u s au c a s ou
n o t i f y A l l () ;
}
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 22 / 30
Exemple type : les producteur et les consommateurs Codes
Producteurs et consommateurs : définition de l’entrepôt II
public synchronized int preleve () {
// t q que l e t a b l e a u e s t v i d e , m e t t r e en p a u s e
w h i l e ( n b O b j e t s C o u r a n t s == 0 ) {
// a f f i c h e l e nom du p r o c e s s u s a p p e l a n t e t m i s en p a u s e
System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + ”−−
pause dans l e r e t r a i t d ’ o b j e t ” ) ;
t r y { wait () ; } catch ( I n t e r r u p t e d E x c e p t i o n e ) { }
}
i n t obj = stockage [ nbObjetsCourants − 1 ] ;
n b O b j e t s C o u r a n t s −−; n b P r i s ++;
// r e v e i l l e r d e s a u t r e s p r o c e s s u s au c a s ou
n o t i f y A l l () ;
return obj ;
}
}
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 23 / 30
Exemple type : les producteur et les consommateurs Codes
Producteurs et consommateurs : définition du Producteur I
c l a s s P r o d u c t e u r e x t e n d s Thread {
/∗ ∗ l i e n v e r s l ’ e n t r e p o t ∗/
Entrepot entrepot ;
/∗ ∗ i n d i q u e s i l e p r o c e s s u s d o i t s ’ a r r e t e r ∗/
p r i v a t e boolean a r r e t = f a l s e ;
/∗ ∗ t o t a l d e s o b j e t s p r o d u i t s ∗/
p r i v a t e s t a t i c i n t nbObjets = 0;
/∗ ∗ no du p r o d u i t à d é p o s e r ∗/
p r i v a t e i n t noObjet = 0 ;
p u b l i c P r o d u c t e u r ( S t r i n g nom , E n t r e p o t entrepot )
{ s u p e r ( nom ) ; e n t r e p o t = e n t r e p o t ;
n o O b j e t = n b O b j e t s ++;}
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 24 / 30
Exemple type : les producteur et les consommateurs Codes
Producteurs et consommateurs : définition du Producteur
II
p u b l i c void run ( )
{
// t a n t que l ’ a r r e t n ’ e s t p a s demandé
while (! arret ) {
n o O b j e t = n b O b j e t s ++;
// t e n t e de d é p o s e r
e n t r e p o t . depose ( noObjet ) ;
System . o u t . p r i n t l n ( getName ( ) + ” : j ’ a i d e p o sé l ’ o b j e t ”+
noObjet ) ;
// p e t i t e p a u s e de 100ms maxi
t r y { Thread . s l e e p ( ( i n t ) ( Math . random ( ) ∗ 1 0 0 ) ) ; }
c a t c h ( I n t e r r u p t e d E x c e p t i o n e ) {}
}
}
/∗ ∗ p e r m e t de demander l ’ a r r e t du p r o c e s s u s ∗/
public void halte () { a r r e t = true ; }
}
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 25 / 30
Exemple type : les producteur et les consommateurs Codes
Producteurs et consommateurs : définition du
Consommateur
c l a s s Consommateur e x t e n d s Thread {
/∗ ∗ l i e n v e r s l ’ e n t r e p o t ∗/
Entrepot entrepot ;
boolean a r r e t = f a l s e ;
p u b l i c Consommateur ( S t r i n g nom , E n t r e p o t ent )
{ s u p e r ( nom ) ; e n t r e p o t = e n t ; }
p u b l i c void run ( ) {
// t a n t que l ’ a r r e t n ’ e s t p a s demandé
while (! arret ) {
int obj = entrepot . preleve () ;
System . o u t . p r i n t l n ( getName ( ) + ” : j ’ a i l ’ objet ” + obj ) ;
// p e t i t e p a u s e de 200ms maxi
t r y { Thread . s l e e p ( ( i n t ) ( Math . random ( ) ∗ 2 0 0 ) ) ; }
c a t c h ( I n t e r r u p t e d E x c e p t i o n e ) {}
} }
public void halte () { a r r e t = true ; } }
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 26 / 30
Exemple type : les producteur et les consommateurs Codes
Producteurs et consommateurs : Lancer la Simu
c l a s s ProductConsom {
p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) {
// c r é e r un e n t r e p ô t l i m i t é à 10 p l a c e s
E n t r e p o t t a b = new E n t r e p o t ( 1 0 ) ;
P r o d u c t e u r p r o d 1 = new P r o d u c t e u r ( ” p r o d u c t 1 ” , tab ) ;
P r o d u c t e u r p r o d 2 = new P r o d u c t e u r ( ” p r o d u c t 2 ” , tab ) ;
Consommateur c o n s 1 = new Consommateur ( ” consom 1” , tab ) ;
Consommateur c o n s 2 = new Consommateur ( ” consom 2” , tab ) ;
Consommateur c o n s 3 = new Consommateur ( ” consom 3” , tab ) ;
P r o d u c t e u r [ ] t a b P r o d = { prod1 , p r o d 2 } ;
Consommateur [ ] t a b C o n s = { c on s1 , co ns 2 , c o n s 3 } ;
f o r ( i n t i = 0 ; i <t a b P r o d . l e n g t h ; i ++) t a b P r o d [ i ] . s t a r t ( ) ;
f o r ( i n t i = 0 ; i <t a b C o n s . l e n g t h ; i ++) t a b C o n s [ i ] . s t a r t ( ) ;
// f a i r e t o u r n e r l e s p r o c e s s u s 4 s e c o n d e s
t r y { Thread . s l e e p ( 4 0 0 0 ) ; } c a t c h ( I n t e r r u p t e d E x c e p t i o n e ) {}
for ( int i = 0 ; i <t a b P r o d . l e n g t h ; i ++) t a b P r o d [ i ] . h a l t e ( ) ;
for ( int i = 0 ; i <t a b C o n s . l e n g t h ; i ++) t a b C o n s [ i ] . h a l t e ( ) ;
System . o u t . p r i n t l n ( t a b ) ; }}
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 27 / 30
Volatilité
Volatilité : pour forcer l’écriture en mémoire
Mémoire centrale et mémoire cache
La JVM de Java est composée d’une mémoire centrale et
d’une mémoire cache
Un changement de valeur d’une variable s’effectue en mémoire
cache
Si un processus souhaite accéder à la variable, il reçoit la
valeur de la mémoire centrale
Certains attributs doivent donc être rafraı̂chis en permanence
→ Utilisation du mot clé volatile
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 28 / 30
Volatilité
Volatilité : pour forcer l’écriture en mémoire
Mot clé volatile
int volatile valeur = 0;
Les attributs volatiles :
sont chargés de la mémoire centrale avant chaque utilisation.
sont stockés en mémoire centrale après chaque accès/écriture.
A utiliser si une variable est partagée, hors d’un bloc
synchronisé
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 29 / 30
Bloc synchronisé
Créer un bloc synchronisé
Possibilité de synchroniser un bloc d’un code
...
s y n c h r o n i z e d ( c o m p t e u r ) { c o m p t e u r . add ( ) ; c o m p t e u r . m u l t ( ) ; }
...
→ synchronise l’accès à l’objet compteur.
Lorsqu’une méthode synchronisée d’un objet est appelée, le
verrou est mis, aucune autre méthode synchronisée de cet
objet peut être exécutée.
tant que le processus n’est pas sorti.
sauf s’il a été mis en attente (wait)
Acquérir le verrou d’un objet est forcément coûteux.
Il faut donc savoir gérer et diminuer au maximum les sections
critiques.
E. ADAM University of Valenciennes Java Avance UVHC/ISTV-LAMIH 30 / 30