Thread
Transcription
Thread
Les tâches et la synchronisation en langage Java Les threads, les verrous, les sémaphores et les moniteurs en Java D’après les cours de D. Genthial et B. Caylux Langage Java – Threads et synchronisation 1 Généralités z E java En j une tâ tâche h estt un processus lé léger : Î Î z En Java, un processus léger peut être créé par : 1. 2. z il p partage g l’espace p mémoire de l’application pp qui q l’a lancé. il possède sa propre pile d d’exécution. exécution. dérivation de la classe Thread. implémentation de l’interface Runnable. D Documentation t ti officielle ffi i ll d de S Sun : http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Thread.html Langage Java – Threads et synchronisation 2 Th d Thread z z z Un thread est constitué essentiellement d’une méthode run () Cette méthode est appelée par la méthode start() Un Thread vit jusqu’à la fin de l’exécution de sa méthode run. Î Un thread p peut être endormi p par sleep(nb p( de ms)) Î Un thread peut passer son tour par yield() Î Un thread peut se mettre en attente par wait() Langage Java – Threads et synchronisation 3 Dérivation de Thread Création d’une classe class MonThread extends Thread{ MonThread(...){ Constructeur } public void run(){ ... Algorithme décrivant le comportement du thread } ... } Création et lancement du thread MonThread mt = new MonThread (); mt start() // exécute la méthode run de mt mt.start() Langage Java – Threads et synchronisation 4 4 Création de Thread par spécialisation de la classe Thread z E Exemple l class MaPremiereTache extends Thread { public MaPremiereTache() { super(); } public void run() { System.out.println(“Bonjour le monde !”); } } ... MaPremiereTache t = new MaPremiereTache (); t.start(); // exécute la méthode run de t Langage Java – Threads et synchronisation 5 Création de Thread par implémentation de l’interface Runnable z Seule façon possible si la classe X dérive déjà d d’une une autre classe. class X extends . . . implements Runnable{ Cible du Thread qui contient la définition de la méthode run() qui sera exécutée par start() () public void run(){ . . . } . . . } X x = new X(); Thread leThread = new Thread(x); leThread.start(); // exécute le run de la cible Langage Java – Threads et synchronisation 6 6 Création de Thread p par implémentation p de l’interface Runnable L’interface L interface Runnable public abstract interface Runnable { public abstract void run(); } Utilisation public p blic class MaPremiereTache implements Runnable R nnable { ... public void run() { System out println(“Bonjour System.out.println( Bonjour le monde !”); ! ); } } Langage Java – Threads et synchronisation 7 Création de Thread p par implémentation p de l’interface Runnable L’interface L interface Runnable public abstract interface Runnable { public abstract void run(); p } Autre implémentation Cible d Cibl du Thread Th d quii class X extends . . . implements Runnable{ doit contenir la définition de la Thread leThread = new Thread(this); méthode run() public void run(){ ... } . . . leThread.start(); // exécute la méthode run de la cible } Langage Java – Threads et synchronisation 8 Création de Thread p par implémentation p de l’interface Runnable z C é ti d’ Création d’une tâ tâche h à partir ti d’ d’un objet bj t R Runnable bl Runnable maTache = new MaPremiereTache(...); Thread leThread = new Thread(maTache); leThread.start(); // démarrage z ou simplement (tâche anonyme) Runnable maTache = new MaPremiereTache(...); new Thread(maTache).start(); z ou encore... MaPremiereTache maTache = new MaPremiereTache(...); new Thread(maTache).start(); Langage Java – Threads et synchronisation 9 Méth d statiques Méthodes t ti z Ell agissent Elles i sur lle thread h d appelant l Î Thread.sleep(long p( g ms)) bloque le thread appelant pour la durée spécifiée; Î Thread.yield() Le thread appelant relâche le processeur au profit d’un thread de même priorité; Î Thread.currentThread() Th d tTh d() retourne une référence sur le thread appelant; Langage Java – Threads et synchronisation 10 Méth d d’i Méthodes d’instances t MonThread M Th d p = new MonThread M Th d () (); z p.start() démarre le thread p; z p.isAlive() détermine si p est vivant (ou terminé); z p p.join() j () bloque q l’appelant pp jjusqu’à q ce q que p soit terminé;; z p.setDaemon() attache l’attribut « daemon » à p; z p.setPriority(int p setPriority(int pr) assigne la priorité pr à p; z p.getPriority() retourne la priorité de p; z …(beaucoup (b d’ d’autres)) Langage Java – Threads et synchronisation 11 Le cycle y de vie d’un thread Création : new … objet créé TERMINATED NEW start() RUNNABLE yield() y () notify() ou notifyAll() interrupt() bloqué p du temps p expiration BLOCKED Langage Java – Threads et synchronisation fin de la méthode run En cours d’exécution d exécution exécutable verrou alloué Mort Demande de verrou 12 wait() join() sleep() En attente WAITING TIMED_WAITING 12 A êt d’une Arrêt d’ tâ tâche h z Un Thread dans l’état l état bloqué ou en attente peut être arrêté par interrupt() class MonThread extends Thread{ . . . public void run(){ try{ while (true){ . . . sleep(100); } }catch(InterruptedException ie){ . . . } } MonThread t = new MonThread (); . . . t.start(); } . . . t.interrupt(); Langage Java – Threads et synchronisation 13 A êt d’une Arrêt d’ tâ tâche h z L itération peut être contrôlée par un booléen et L’itération arrêtée par une fonction qui rend vrai le booléen. class MonThread extends Thread{ boolean arret = false; public void run(){ try{ while(!arret){ . . . } }catch(InterruptedException . . . } } public void stop (){ arret = true; } . . . } Langage Java – Threads et synchronisation ie){ MonThread t = new MonThread (); t.start(); . . . t.stop(); 14 Arrêt d’une tâche public void run() { try { // Corps de la tâche ... } catch (InterruptedException e) { // Terminer la tâche ... return; } fi finally ll { // Code exécuté avant la fin de la tâche, quelle que soit // la cause qui l’a arrêtée (fin normale ou exception // ou erreur) ... } } Langage Java – Threads et synchronisation 15 J i t Jointure d de th threads d z Un thread peut avoir à attendre la fin d’un d un ou plusieurs threads pour démarrer : join() class MonThread extends Thread{ Thread precedent; public MonThread( Thread p){ précédent é éd t = p; } public p blic void oid run(){ try{ precedent.join(); // le thread reste bloqué tant que // precedent n n’est est pas terminé . . . }catch(InterruptedException ie){ . . . } } } Langage Java – Threads et synchronisation 16 P i ité d Priorité des th threads d z Les Threads ont une priorité qui va de Î Î Î z z z z Thread.MIN_PRIORITY (1) Thread NORM PRIORITY (5) Thread.NORM_PRIORITY Thread.MAX_PRIORITY (10) int getPriority() ; void setPriority(int p) ; void id setDaemon(boolean D (b l on)) permet de d rendre d un thread démon : thread qui s’exécute en tâche de f d fond. Un programme Java se termine lorsque tous les threads user sont terminés (les threads daemon n’ont n ont pas besoin d’être d être terminés). Langage Java – Threads et synchronisation 17 Th d d Threads de lla JVM z En plus E l d des th threads d d du programme JJava, une machine virtuelle Java exécute des threads dédiés à des tâches de gestion : Î Î Î Î thread oisif (priorité 0): exécuté si aucun autre thread ne s’exécute; ramasse-miettes (garbage collector, priorité 1): récupère les objets qui ne sont plus accessibles (référencés) (référencés). Ce thread ne s’exécute que lorsqu’aucun autre thread (à part le thread oisif) ne s’exécute; gestionnaire ti i d’horloge: d’h l gère è tous lles é événements é iinternes lié liés au temps; thread de finalisation: appelle la méthode finalize( ) des objets récupérés par le ramasse-miettes. Langage Java – Threads et synchronisation 18 Ordonnancement des tâches : la théorie z Les threads exécutable se trouvent dans les queues de différentes priorités, gérées par le run-time de Java: Î Î Î z z si plus d’un thread exécutable, Java choisit le thread de plus haute priorité; si plus d’un thread de plus haute priorité, Java choisit un thread arbitrairement; un thread en cours d’exécution perd le CPU au profit d’un d un thread de plus haute priorité qui devient exécutable; par défaut, un thread a la même priorité que le thread qui l’a créé; la priorité peut être changée en appelant Thread.setPriority(). Langage Java – Threads et synchronisation 19 Ordonnancement des tâches : la réalité z La politique L liti d’allocation d’ ll ti d du processeur aux th threads d de même priorité n’est pas fixée par le langage. Î z Ell peutt êt Elle être avec ou sans préemption!!! é ti !!! Pas de traitement cohérent de la priorité: exemple: la priorité n’est pas utilisée par la méthode notify() pour choisir le processus à débloquer; z La spécification f du langage n’exige pas que les priorités soient prises en compte par le runtime. Langage Java – Threads et synchronisation 20 S Synchronisation h i ti z z Comment empêcher plusieurs threads d’accéder en même temps à un objet ? Chaq e objet Ja Chaque Java a possède un n verrou erro d’exclusion mutuelle. Î S Synchronisation h i ti d’un d’ bloc bl d’instructions d’i t ti synchronized (objet){ . . . // Aucun autre thread ne . . . // peut accéder à objet } Î Synchronisation d d’une une méthode synchronized void f(){ . . . // Aucun autre thread ne . . . // peut t accéder éd à l’ l’objet bj t . . . // pour lequel est appelé la méthode f } Langage Java – Threads et synchronisation 21 les verrous (rappel) Type verrou = structure ouvert : booléen; attente : liste de tâches; fin verrou;; procédure verrouiller (modifié v : verrou); début si v.ouvert alors v.ouvert Å faux sinon Ajouter la tâche dans la liste v.attente dormir finsi; fin verrouiller; Langage Java – Threads et synchronisation procédure init_verrou init verrou (modifié v : verrou); début v.ouvert Å vrai; v.attente Å vide;; fin init_verrou; procédure déverrouiller (modifié v : verrou); début si v.attente ≠ vide alors Sortir la première tâche de v.attente, La réveiller sinon v.ouvert Å vrai fi i finsi; fin déverrouiller; 22 Exemple : voie unique (rappel) Tâche Tâ h Train T i (modifié ( difié voie i : verrou); ) début rouler; verrouiller(voie); Passer; ( ); déverrouiller(voie); rouler; rouler; synchronized (voie){ passer; fin TrainGauche; } rouler; Langage Java – Threads et synchronisation 23 E Exemple l d de bl bloc synchronized h i d Comment garantir une exécution atomique des commandes suivantes ? System.out.print (new Date ( )) ; System.out.print (" : ") ; System.out.println (...) ; (les méthodes print et println de la classe Printstream sont synchronisées ) Synchronized (System.out){ System.out.print (new Date ( ) ) ; System.out.print (" : ") ; System.out.println (...) ; } lorsqu’une méthode synchronized est appelée, le thread appelant pp doit obtenir le verrou associé à l’objet j Langage Java – Threads et synchronisation 24 S Synchronisation h i ti z La synchronisation par verrou peut provoquer des « interblocages » (dead l k ) amenantt l’l’arrêt locks) êt défi définitif itif d de un ou plusieurs threads. class MonThread1 extends Th Thread{ d{ public void run(){ synchronized(a){ . . . synchonized(b){ . . . } } } } Langage Java – Threads et synchronisation class MonThread2 extends Th Thread{ d{ public void run(){ synchronized(b){ . . . synchonized(a){ . . . } } } } 25 L verrous JJava : utilisation Les tili ti (1) z Synchronisation (verrouillage) sur un objet public class Compte { private double xSolde; public Compte(double DépôtInitial) { xSolde = DépôtInitial; } public synchronized double solde() { etu xSolde; So de; return } public synchronized void dépôt(double montant) { xSolde S ld = xSolde S ld + montant; t t } } Î Solde et Dépôt sont exécutées en exclusion mutuelle sur un objet donné (xSolde) Langage Java – Threads et synchronisation 26 L verrous JJava : utilisation Les tili ti (2) public class Chèques implements Runnable { public class Liquide implements Runnable { private Compte xCompte; private Compte xCompte; public Chèques(Compte c) { xCompte = c; } public Liquide(Compte c) { xCompte = c; } public void run() { double montant; // Lecture du montant . . . xCompte.dépôt(montant); } public void run() { double montant; // Lecture du montant . . . xCompte.dépôt(montant); } } Langage Java – Threads et synchronisation } 27 L verrous JJava : utilisation Les tili ti (3) public class Banque { public static void main(String [] arg) { Compte monCompte = new Compte(2000.00); Chèques c = new Chèques(monCompte); Liquide q l = new Liquide(monCompte); q ( p ) new Thread(c).start(); new Thread(l).start(); } } Langage Java – Threads et synchronisation 28 S Synchronisation h i ti z Bl Blocage d’une d’ tâche tâ h void wait(); Î z sur un objet quelconque Réveil d’une d une tâche void notify(); Î z sur l’objet l objet cause du blocage Réveil é e de toutes les es tâc tâches es void notifyAll(); Î sur l’objet cause du blocage Langage Java – Threads et synchronisation 29 S Synchronisation h i ti La méthode wait() est définie dans la classe Object. Object z On ne peut utiliser cette méthode que dans un code synchronisé. z Elle provoque ll'attente attente du thread en cours d d'exécution. exécution. z Elle doit être invoquée sur l'instance verrouillée. synchronized(unObjet){ … try{ unObjet.wait(); // unObjet bloque l’exécution du thread }catch(InerruptedException ie){ … } z Pendant P d t que le l thread th d attend, tt d le l verrou sur unObjet Obj t estt relaché. l hé Î wait(long ms) attend au maximum ms millisecondes Î wait(long ait(long ms, ms int ns) attend au a maximum ma im m ms millisecondes + ns nanosecondes Langage Java – Threads et synchronisation 30 Synchronisation z L méthode La éth d notify() tif () permett de d débloquer débl un thread. th d synchronized(unObjet){ … unObjet.notify(); // débloque un thread bloqué par unObjet … } z La méthode notifyAll() permet de débloquer tous les threads. synchronized(unObjet){ … unObjet.notifyAll(); // débloque tous les threads // bloqués par unObjet … } Langage Java – Threads et synchronisation 31 l moniteurs les it M d l avec d Module des propriétés iété particulières ti liè z z z on n’a accès qu’aux primitives externes (publiques), pas aux (privées)) ; variables (p les procédures sont exécutées en exclusion mutuelle et donc les variables internes sont manipulées en exclusion mutuelle. on peut bloquer et réveiller des tâches tâches. Le blocage et le réveil s’exprime au moyen de conditions. Langage Java – Threads et synchronisation 32 R Rappel l : lles moniteurs it Conditions = variables, manipulables avec : procédure attendre (modifié c : condition); début Bl Bloque lla tâ tâche h quii l’ l’exécute é t ett l’ l’ajoute j t d dans la l file fil d’attente d’ tt t associée ié à c Fin attendre; fonction vide (c : condition) Æ un booléen; début si la file d’attente associée à c est vide alors renvoyer(VRAI) sinon renvoyer(FAUX) finsi Fin vide; procédure signaler (modifié c : condition); début si non vide(c) alors extraire la première tâche de la file associée à c la réveiller finsi Fin signaler; Langage Java – Threads et synchronisation 33 M it Moniteurs en JJava (1) z z En java on dispose de moniteurs simplifiés Définition d’une classe public bli class l M Moniteur it { private type uneVariable; public synchronized unePrimitive( unePrimitive(. . .) ) throws InterruptedException { . . . if ( (. . .) ) wait(); // le thread se met en attente sur la cible . . . } public synchronized unePrimitive(. . .) { . . . y // réveil de la cible notify(); . . . } Langage Java – Threads et synchronisation 34 M it Moniteurs en JJava (2) z I lé Implémentation t ti réduite éd it des d conditions diti public class Moniteur { private type uneVariable; private Object uneCondition = new Object(); public unePrimitive(. . .) // pas synchronized throws InterruptedException { . . . if (. . .) synchronized (uneCondition) {uneCondition.wait();} . . . } public synchronized unePrimitive(. . .) { . . . synchronized (uneCondition) {uneCondition.notify();} . . . Langage Java } – Threads et synchronisation 35 R d Rendez-vous avec un moniteur it Moniteur RDV; Point d’entrée Arriver Lexique n : un entier constant = ?? i : un entier; _ : une condition; tous_là Début {Initialisations} i Å 0;; procédure Arriver(); début i Å i + 1; si i < n alors attendre(tous_là); fi i finsi signaler(tous_là); {Réveil en cascade} fin Arriver; Fin RDV; Langage Java – Threads et synchronisation 36 Rendez-vous en Java : moniteur simplifié class RendezVous { private int nbattendus; private int i; // nombre attendus // nombre arrivés // constructeur public RendezVous(int n) { nbattendus = n; i = 0; } public synchronized void arriver() throws InterruptedException { i++; if (i < nbattendus) b tt d ) { wait(); }; notify(); } } Langage Java – Threads et synchronisation 37 Rendez-vous en Java avec condition class RendezVous { private int nbattendus; private int i; private i Object j tousla = new Object(); j // nombre attendus // nombre arrivés // condition i i // constructeur public RendezVous(int p ( n) ) { nbattendus = n; i = 0; } public void arriver() throws InterruptedException { i++; if (i < nbattendus) { synchronized(tousla) {tousla.wait();} }; synchronized(tousla) {tousla {tousla.notify();} notify();} } } Langage Java – Threads et synchronisation 38 L Sé Les Sémaphores h en JJava z z Il est inutile de définir des sémaphores en Java, car les mécanismes fournis par le l langage pour lla synchronisation h i ti ett lle partage de ressources sont suffisamment puissants pour résoudre tous les problèmes qu'on p q peut avoir à résoudre avec des sémaphores. Il est cependant intéressant de montrer avec quelle facilité ces mécanismes peuvent être ê utilisés ili é pour défi définir i d des sémaphores. Langage Java – Threads et synchronisation 39 R Rappel l : Sé Sémaphores h z z Mécanisme de synchronisation entre processus processus. Un sémaphore S est une variable entière, manipulable par l’intermédiaire de deux opérations : P (proberen) et V (verhogen) acquire release acquire acqu e (S) S Å (S − 1)) si S < 0, alors mettre le processus en attente ; sinon on poursuit l’exécution release (S) S Å (S + 1) ; réveil d’un processus en attente. Î Î acquire (S) correspond à une tentative de franchissement. S’il n’y a pas de j t pour lla section jeton ti critique iti alors l attendre, tt d sinon i prendre d un jjeton t ett entrer t dans la section (S), puis rendre son jeton à la sortie de la section critique. Chaque dépôt de jeton release (S) autorise un passage. Il est possible de déposer des jetons à ll’avance avance Langage Java – Threads et synchronisation 40 Réalisation é de la classe S Semaphore public class Semaphore { private int xVal; // la valeur du sémaphore // En java, l'initialisation du sémaphore vient naturellement // dans le constructeur Semaphore (int valeur_initiale) { xVal = valeur_initiale; } // Aquire : noter l'indispensable synchronized peut // générer une InterruptedException (wait). public synchronized void acquire() throws InterruptedException { xVal = xVal - 1; while(xVal < 0) wait(); } // Release public synchronized void release() { xVal = xVal + 1; if (xVal <= 0) notify(); } } Langage Java – Threads et synchronisation 41 G Groupe de d th threads d z Un Thread peut faire partie d d’un un groupe de Thread : ThreadGroup tgl = new ThreadGroup(nom du groupe); Thread t = new Thread(nom du goupe, nom du thread); Langage Java – Threads et synchronisation 42 A t Autres méthodes éth d d de lla classe l Th d Thread z z z static int activeCount() nombre de threads actifs du même groupe ou d’un d un sous-groupe sous groupe. static void enumerate(Thread[ ] t) énumère dans le tableau t les threads actifs du même groupe ou d’un sous-groupe. static t ti Thread Th d currentThread() tTh d() retoune t une référence au Thread courant. Langage Java – Threads et synchronisation 43 Communication entre tâches (rappel) ( pp ) Partage de mémoire commune + exclusion mutuelle Modèle général : producteur / consommateur Producteur Æ Données Æ Consommateur z La communication est en général asynchrone : une des deux tâches travaille en général plus vite que l’autre. z Pour limiter les effets de l’asynchronisme, on insère un tampon entre le producteur et le consommateur : Langage Java – Threads et synchronisation 44 Producteur/Consommateur avec tampon (rappel) Langage Java – Threads et synchronisation 45 Producteur/Consommateur avec tampon (rappel) Moniteur Tampon; {Suppose Taille et Message connus} Points d’entrée déposer, retirer; Lexique nb : entier; {Nbre d’élt. dans le tampon (0..Taille)} non_plein, non_vide : conditions; {Pour le blocage et le réveil} tamp : tableau[0..Taille-1] de Messages; tête, queue : entier; {O..Taille-1} procédure déposer (consulté m:Message) début si nb = Taille alors attendre(non_plein) finsi {Ajouter m au tampon} nb nb + 1; tamp[queue] m; queue (queue+1) mod Taille; signaler(non_vide); fin déposer; procédure retirer (consulté m:Message) début si nb = 0 alors attendre(non_vide) finsi { Enlever m du tampon p } m tamp[tête]; tête (tête+1) mod Taille; nb nb - 1; signaler(non_plein); fin retirer; Débutt Déb nb 0; tête 0; queue 0; Fin Tampon; Langage Java – Threads et synchronisation 46 Le problème des Philosophes et d spaghettis des h tti z Le problème L blè d des philosophes hil h ett d des spaghettis h tti estt le l cas d'école concernant le problème classique de partage de ressources resso rces en informatiq informatique e ssystème. stème Il concerne l'ordonnancement des processus et ll'allocation allocation des ressources à ces derniers. derniers La situation est la suivante : Î Î Î cinq philosophes (initialement mais il peut y en avoir beaucoup plus) se trouvent autour d'une table chacun des philosophes a devant lui une assiette de spaghettis ; à gauche de chaque plat de spaghetti se trouve une fourchette. Langage Java – Threads et synchronisation 47 Le problème des Philosophes et d spaghettis des h tti z Un philosophe n'a n a que trois états possibles : Î Î Î z penser pendant un temps indéterminé ; être affamé (pendant un temps déterminé et fini sinon il y a famine) ; manger pendant un temps déterminé et fini. Des contraintes D t i t extérieures té i s'imposent 'i t à cette tt situation : Î Î Î quand d un philosophe hil h a ffaim, i il va se mettre tt d dans l'ét l'étatt « affamé ff é » et attendre que les fourchettes soient libres ; pour manger, p g , un p philosophe p a besoin de deux fourchettes : celle qui se trouve à gauche de sa propre assiette, et celle qui se trouve à gauche de celle de son voisin de droite (c'est-à-dire les deux fourchettes qui entourent sa propre assiette) ; si un philosophe n'arrive pas à s'emparer d'une fourchette, il reste affamé pendant un temps déterminé, en attendant de renouveler l sa tentative. i Langage Java – Threads et synchronisation 48 Le problème des Philosophes et d spaghettis des h tti z z Le problème L blè consiste i t à ttrouver un comportement t t des philosophes tel qu'ils puissent tous manger, chac n à leur chacun le r to tour. r Remarque : Les philosophes, s'ils agissent tous de f façons naïves ï ett identiques, id ti risquent i t fort f t de d se retrouver en situation d'interblocage. En effet, il suffit ffit que chacun h saisisse i i sa fourchette f h tt de d gauche h et, qu'ensuite, chacun attende que sa fourchette de droite se libère pour qu'aucun d'entre eux ne puisse manger, et ce pour l'éternité. Langage Java – Threads et synchronisation 49 E Exemples l en JJava z z z Yield Yi ld Rendez-vous Philosophes Langage Java – Threads et synchronisation 50 Langage Java – Threads et synchronisation 51