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