Java Avance - Gestion de processus

Transcription

Java Avance - Gestion de processus
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 ++;
Thread . y i e l d ( ) ;
}
System . e x i t ( 0 ) ;
}
}
E. ADAM
University of Valenciennes
Java Avance
i=” + i ) ;
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 ++;
Thread . y i e l d ( ) ;
}
System . e x i t ( 0 ) ;
}
}
E. ADAM
University of Valenciennes
Java Avance
i=” +
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
et qu’aucun
le processus
et qu’aucun
E. ADAM
University of Valenciennes
l’utilisant en sort
autre processus n’était mis en attente avant lui
l’utilisant est mis en attente (wait())
autre processus n’était mis en attente avant lui
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
i = i;
classeS = classeS ;
s u p e r ( name ) ;
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
s t o c k a g e = new i n t [
}
taille ) {
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
{ 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
entrepot )
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
{ s u p e r ( nom ) ; e n t r e p o t = e n t ; }
ent )
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 ” ,
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 ” ,
Consommateur c o n s 1 = new Consommateur ( ” consom
Consommateur c o n s 2 = new Consommateur ( ” consom
Consommateur c o n s 3 = new Consommateur ( ” consom
tab ) ;
tab ) ;
1” , tab ) ;
2” , tab ) ;
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
for ( int
i = 0 ; i <t a b P r o d . l e n g t h ;
i = 0 ; i <t a b C o n s . l e n g t h ;
i ++) t a b P r o d [ i ] . h a l t e ( ) ;
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