1 / 82

Badr Benmammar bbm@badr-benmammar

Programmation concurrente et temps réel en Java. Badr Benmammar bbm@badr-benmammar.com. Cycle de vie d’un Thread. class Perroquet4 extends Thread { private String cri = null; private int fois = 0; public Perroquet4 (String s, int i) { cri = s; fois = i; }

valmai
Download Presentation

Badr Benmammar bbm@badr-benmammar

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Programmation concurrente et temps réel en Java Badr Benmammar bbm@badr-benmammar.com

  2. Cycle de vie d’un Thread class Perroquet4 extends Thread { private String cri = null; private int fois = 0; public Perroquet4 (String s, int i) { cri = s; fois = i; } public void run() { System.out.println("Thread perroquet : " + Thread.currentThread().getName()); for (int n=0; n<fois; n++) { try { Thread.sleep(1000); } catch(InterruptedException e) { } System.out.println(cri); } } } class BavarderEtLancerLePerroquet4 { public static void main(String args[]) { Perroquet4 perroquet = new blabla(); Perroquet4("coco",5); perroquet.start(); System.out.println ("Thread bavard : " + Thread.currentThread().getName()); for (int n=0; n<15; n++) { try { Thread.sleep(1000); } catch(InterruptedException e) { } System.out.println("Thread perroquet isAlive : " + perroquet.isAlive()); } } private static void blabla() { System.out.println("blabla"); } }

  3. Cycle de vie d’un Thread Le Thread "principal", celui qui exécute main s'appelle main. L’autre, celui du perroquet, a reçu comme nom par défaut Thread-0. La méthode isAlive() détermine si un Thread est en train d'exécuter sa méthode run. La méthode de classe currentThread() retourne un pointeur sur l’objet Thread qui appelle cette méthode. La méthode getName() donne le nom du Thread. Exécution: Thread bavard : main Thread perroquet : Thread-0 Thread perroquet isAlive : true blabla coco coco Thread perroquet isAlive : true blabla coco Thread perroquet isAlive : true blabla coco Thread perroquet isAlive : true blabla coco Thread perroquet isAlive : false blabla Thread perroquet isAlive : false blabla Thread perroquet isAlive : false blabla Thread perroquet isAlive : false blabla Thread perroquet isAlive : false blabla Thread perroquet isAlive : false blabla Thread perroquet isAlive : false blabla Thread perroquet isAlive : false blabla Thread perroquet isAlive : false blabla Thread perroquet isAlive : false blabla Thread perroquet isAlive : false blabla

  4. Propriétés des différents Threads public void run(){ afficheThreads(); for (int n=0; n<fois; n++) { try { Thread.sleep(1000); } catch(InterruptedException e) { } System.out.println(cri); } afficheThreads(); } private void afficheThreads() { Thread[] tabThread = new Thread[Thread.activeCount()]; int nbrThread = Thread.enumerate(tabThread); for (int i = 0; i < nbrThread ; i++) System.out.println(i + "-ieme Thread : " + tabThread[i].getName()); } } class BavarderEtLancerLePerroquet5 { public static void main(String args[]) { Thread.currentThread().setName("bavard"); Perroquet5 perroquet = new Perroquet5("coco",15); perroquet.start(); for (int n=0; n<5; n++) { try { Thread.sleep(1000); } catch(InterruptedException e) { } blabla(); }} private static void blabla() { System.out.println("blabla"); }} class Perroquet5 extends Thread { private String cri = null; private int fois = 0; public Perroquet5(String s, int i) { super("perroquet"); cri = s; fois = i; }

  5. Propriétés des différents Threads La méthode setName (String nom) permet de nommer le Thread. Le constructeur offre la possibilité de le nommer ainsi Thread (String nom). La méthode de classe activeCount () donne le nombre de Threads actifs dans le groupe de l'appelant. La méthode de classe enumerate (Thread[] tableau) stocke dans le tableau donné les références des Threads actifs dans le groupe de l'appelant et ses sous-groupes. Elle renvoie le nombre de Threads actifs obtenus. Remarquons que le Thread main, renommé bavard, a fini avant le Thread perroquet, et ne l'attend pas pour terminer. Exécution: 0-ieme Thread : bavard 1-ieme Thread : perroquet coco blabla coco blabla coco blabla coco blabla coco blabla coco coco coco coco coco coco coco coco coco coco 0-ieme Thread : perroquet 1-ieme Thread : DestroyJavaVM

  6. Synchronisation sur terminaison Exécution: blabla coco blabla coco blabla coco blabla coco coco blabla coco coco coco coco coco fin du Thread perroquet ! blabla blabla blabla blabla blabla class Perroquet6 extends Thread { private String cri = null; private int fois = 0; public Perroquet6 (String s, int i) { super ("perroquet"); cri = s; fois = i; } public void repeter() { System.out.println(cri); try {Thread.sleep(1000); } catch(InterruptedException e) { } } public void run() { for (int n=0; n<fois; n++) repeter(); } } class BavarderEtLancerLePerroquet6{ public static void main(String args[]) { Perroquet6 perroquet = new Perroquet6("coco",10); perroquet.start(); for (int n=0; n<5; n++) blabla(); try { perroquet.join(); } catch(InterruptedException e) { System.out.println(e.getMessage()); System.exit(2); } System.out.println("fin du Thread perroquet !"); for (int n=0; n<5; n++) blabla(); } private static void blabla() { System.out.println("blabla"); try { Thread.sleep(1000); } catch(InterruptedException e) { } }}

  7. Synchronisation sur terminaison La méthode join() attend la terminaison du Thread spécifié. Si le Thread est créé mais pas "starté", il est considéré comme terminé ! join() peut aussi avoir un paramètre donné qui est un timeout maximal. Les 2 Threads ne sont plus indépendants puisque le "main" attend, à une certaine étape, la terminaison (fin d'exécution) de l'autre. C'est une forme de synchronisation. Exécution: blabla coco blabla coco blabla coco blabla coco coco blabla coco coco coco coco coco fin du Thread perroquet ! blabla blabla blabla blabla blabla perroquet.join(); Exécution en multitâche Le main attend la terminaison du Thread perroquet Le main termine son exécution

  8. Exemple de join() : synchroniser deux écrivains public class Ecrivain extends Thread { private String texte; public Ecrivain(String t) { texte=t; } public void run() { for (int i=0; i<10; i++) { int j=0; for (;j<texte.length()-1; j++) { System.out.print(texte.substring(j,j+1)); try { sleep((long)(Math.random() * 100)); } catch (InterruptedException e) {} } System.out.println(texte.substring(j,j+1)); } System.out.println("ecrivain de " +texte+" a fini"); } } public class Prog55 { public static void main (String argv[]) { Ecrivain ecrivainA, ecrivainB; ecrivainA = new Ecrivain ("ABC"); ecrivainB = new Ecrivain ("XYZ"); ecrivainA.start(); ecrivainB.start(); } } Exemple de substring : String date = "15/08/2000"; String jour = date.substring(0,2);// donne 15 String mois = date.substring(3,5); // donne 08 String annee = date.substring(6,10); // donne 2000 Chaine du caractère 6 à 9 (premier caractère d’indice 0).

  9. Exemple de join() : synchroniser deux écrivains Exécution: AXBYC ABZ XC AYZ XBC AYBC AZ XBC AYBC AZ XBC ABYZ XC AYBC AZ XBC ecrivain de ABC a fini YZ XYZ XYZ XYZ ecrivain de XYZ a fini Exécution : ABC ABC ABC ABC ABC ABC ABC ABC ABC ABC ecrivain de ABC a fini XYZ XYZ XYZ XYZ XYZ XYZ XYZ XYZ XYZ XYZ ecrivain de XYZ a fini public class Prog55 { public static void main (String argv[]) { Ecrivain ecrivainA, ecrivainB; ecrivainA = new Ecrivain ("ABC"); ecrivainB = new Ecrivain ("XYZ"); ecrivainA.start(); try { ecrivainA.join(); } catch(InterruptedException e) { System.out.println(e.getMessage()); System.exit(1); } ecrivainB.start(); } }

  10. Deux Threads sans sleep Exécution: blabla blabla blabla blabla blabla blabla blabla blabla blabla blabla coco coco coco coco coco coco coco coco coco coco class BavarderEtLancerLePerroquet7 { public static void main(String args[]) { Perroquet7 perroquet = new Perroquet7("coco",10); perroquet.start(); for (int n=0; n<10; n++) blabla(); } private static void blabla() { System.out.println("blabla"); } } class Perroquet7 extends Thread { private String cri = null; private int fois = 0; public Perroquet7(String s, int i) { cri = s; fois = i; } public void repeter() { System.out.println(cri); } public void run() { for (int n=0; n<fois; n++) repeter(); } } Le temps d’exécution est trop court pour visualiser la répartition du CPU entre les 2 Threads.

  11. Répartition de temps entre deux Threads public class CourseInfernale1 { public static void main(String[] args) { Coureur A = new Coureur("A"); Coureur B = new Coureur("B"); A.start(); B.start(); }} class Coureur extends Thread { String nom; public Coureur(String nom) { super(nom); this.nom = nom; } public void run() { long coupsDePedale = 0; while (coupsDePedale < 5000000) { coupsDePedale++; if ((coupsDePedale % 500000) == 0) { System.out.println("Coureur " + nom + " a donne " + coupsDePedale + " coups de pedale."); } } }}

  12. Répartition de temps entre deux Threads Exécution: Coureur A a donne 500000 coups de pedale. Coureur B a donne 500000 coups de pedale. Coureur B a donne 1000000 coups de pedale. Coureur A a donne 1000000 coups de pedale. Coureur B a donne 1500000 coups de pedale. Coureur A a donne 1500000 coups de pedale. Coureur A a donne 2000000 coups de pedale. Coureur B a donne 2000000 coups de pedale. Coureur A a donne 2500000 coups de pedale. Coureur B a donne 2500000 coups de pedale. Coureur A a donne 3000000 coups de pedale. Coureur B a donne 3000000 coups de pedale. Coureur A a donne 3500000 coups de pedale. Coureur B a donne 3500000 coups de pedale. Coureur A a donne 4000000 coups de pedale. Coureur B a donne 4000000 coups de pedale. Coureur A a donne 4500000 coups de pedale. Coureur B a donne 4500000 coups de pedale. Coureur A a donne 5000000 coups de pedale. Coureur B a donne 5000000 coups de pedale. Java n'impose pas que le système soit "time-sliced" : cad que la même quantité de temps soit impartie aux Threads de même niveau de priorité.

  13. Priorité entre Threads class Coureur extends Thread { String nom; public Coureur(String nom) { super(nom); this.nom = nom; } public void run() { long coupsDePedale = 0; while (coupsDePedale < 5000000) { coupsDePedale++; if ((coupsDePedale % 500000) == 0) { System.out.println("Coureur " + nom + " a donne " + coupsDePedale + " coups de pedale."); } } } } • La méthode setPriority fixe le niveau de priorité entre les différents Threads. • La valeur doit être comprise entre une valeur minimale, MIN_PRIORITY, et maximale, MAX_PRIORITY. public class CourseInfernale2 { public static void main(String[] args) { Coureur A = new Coureur("A"); Coureur B = new Coureur("B"); A.setPriority(Thread.MAX_PRIORITY); B.setPriority(Thread.MIN_PRIORITY); System.out.println("Thread Coureur " + A.nom + " a la priorite = " + A.getPriority()); System.out.println("Thread Coureur " + B.nom + " a la priorite = " + B.getPriority()); A.start(); B.start(); } }

  14. Priorité entre Threads Exécution: Thread Coureur A a la priorite = 10 Thread Coureur B a la priorite = 1 Coureur A a donne 500000 coups de pedale. Coureur A a donne 1000000 coups de pedale. Coureur A a donne 1500000 coups de pedale. Coureur A a donne 2000000 coups de pedale. Coureur A a donne 2500000 coups de pedale. Coureur A a donne 3000000 coups de pedale. Coureur A a donne 3500000 coups de pedale. Coureur A a donne 4000000 coups de pedale. Coureur A a donne 4500000 coups de pedale. Coureur A a donne 5000000 coups de pedale. Coureur B a donne 500000 coups de pedale. Coureur B a donne 1000000 coups de pedale. Coureur B a donne 1500000 coups de pedale. Coureur B a donne 2000000 coups de pedale. Coureur B a donne 2500000 coups de pedale. Coureur B a donne 3000000 coups de pedale. Coureur B a donne 3500000 coups de pedale. Coureur B a donne 4000000 coups de pedale. Coureur B a donne 4500000 coups de pedale. Coureur B a donne 5000000 coups de pedale.

  15. Méthode yield() • La méthode yield() "rend le processeur" : elle indique au contrôleur d'exécution des Threads d'en choisir un nouveau à exécuter (donc ca pourrait être le même !). Elle est intéressante dans peu de cas. class BavarderEtLancerLePerroquet8 { public static void main(String args[]) { Perroquet8 perroquet = new Perroquet8("coco",10); perroquet.start(); for (int n=0; n<10; n++) { blabla(); Thread.currentThread().yield(); } } private static void blabla() { System.out.println("blabla"); } } class Perroquet8 extends Thread { private String cri = null; private int fois = 0; public Perroquet8(String s, int i) { cri = s; fois = i; } public void repeter() { System.out.println(cri); } public void run() { for (int n=0; n<fois; n++) { repeter(); yield(); } } }

  16. Essayer de Stopper l’exécution d’un Thread class Perroquet9 extends Thread { private String cri = null; public Perroquet9(String s) { cri = s; } public void repeter() { System.out.println(cri); try { Thread.sleep((int)Math.random()*1000); } catch(InterruptedException e) {} } public void run() { while (true) { repeter(); yield(); } }} class LancerEtArreterLePerroquet9 { public static void main(String args[]) { Perroquet9 perroquet = new Perroquet9("coco"); perroquet.start(); String reponse="o"; do {System.out.println("voulez-vous que le perroquet continue ? (o/n)"); Thread.currentThread().yield(); reponse = Saisie.litexte(); } while (reponse.equals("o")); }} Exécution: coco coco coco coco coco … L’arrêt de l'exécution du Thread main n'entraine pas l'arrêt du Thread perroquet. L'arrêt brutal d’un Thread pouvait laisser des objets dans des états inconsistants (par exemple, des verrous pouvaient avoir été posés). C'est au programmeur de prévoir quand (et donc comment) l'exécution du Thread peut s'arrêter sans risque.

  17. Stopper proprement : mot clé volatile class Perroquet10 extends Thread{ private volatile boolean continuer = true; private String cri = null; public Perroquet10(String s) { continuer = true; cri = s; } public void repeter() { System.out.println(cri); try { Thread.sleep((int)Math.random()*2000); } catch(InterruptedException e) { } } public void stopper() { continuer = false;} public void run() { while (continuer) { repeter(); } } } class LancerEtArreterLePerroquet10{ public static void main(String args[]) { Perroquet10 perroquet = new Perroquet10("coco"); perroquet.start(); String reponse="o"; do { System.out.println("voulez-vous que le perroquet continue ? (o/n)"); reponse = Saisie.litexte(); } while (reponse.equals("o")); perroquet.stopper(); } }

  18. Stopper proprement La solution est très simple : une variable booléenne sert dans la méthode run pour savoir s’il faut continuer ou arrêter. Cette variable doit être déclarée volatile. Pour une variable, le modificateur volatile force la JVM, avant et après chaque utilisation de la variable par un Thread , à la rafraîchir à partir de la mémoire principale au lieu d'utiliser un cache local. Cela permet de synchroniser la valeur de la variable entre plusieurs Threads. Exécution : coco coco coco coco coco coco coco coco coco coco coco coco ncoco coco coco coco coco coco

  19. Programmer une tâche en précisant un délai initial class Perroquet11 extends TimerTask { private String cri = null; private int fois = 0; public Perroquet11(String s, int i) { cri = s; fois = i; } public void repeter() { System.out.println(cri); } public void run() { for (int i = 0; i < fois; i++) { repeter(); } } } import java.util.TimerTask; import java.util.Timer; class DeclancherLePerroquet11{ public static void main(String args[]) { Perroquet11 perroquet = new Perroquet11("coco", 3); Timer timer = new Timer(); timer.schedule(perroquet, 4000); String reponse="oui"; do { System.out.println("blabla"); System.out.println("blabla"); System.out.println("voulez-vous encore bavarder ? (o/n)"); reponse = Saisie.litexte(); } while (reponse.equals("o")); timer.cancel(); } }

  20. TimerTask public abstract class TimerTask extends Object implements Runnable • TimerTask est une classe abstraite qui implémente Runnable, donc une méthode run(), il faut donc hériter de la classe TimerTask et redéfinir la méthode run() qui code la tâche à effectuer. • Un Timer permet de déclencher l'exécution de tâches une ou plusieurs fois en précisant un délai initial et/ou une périodicité. Plusieurs tâches peuvent être programmées selon des programmes divers.

  21. Programmer une tâche en précisant un délai initial Exécution: blabla blabla voulez-vous encore bavarder ? (o/n) o blabla blabla voulez-vous encore bavarder ? (o/n) coco coco coco o blabla blabla voulez-vous encore bavarder ? (o/n) n A un Timer correspond un Thread qui exécutera successivement les tâches à effectuer. Les tâches sont des TimerTask et doivent être courte. Aucune garantie de temps réel n'est assurée par ce mécanisme. schedule(tache, long millisecondes) programme la tâche en précisant un délai initial en millisecondes. La méthode cancel() arrête la programmation du Timer.

  22. Programmer une tâche avec délai initial et une périodicité class Perroquet11 extends TimerTask { private String cri = null; private int fois = 0; public Perroquet11(String s, int i) { cri = s; fois = i; } public void repeter() { System.out.println(cri); } public void run() { for (int i = 0; i < fois; i++) { repeter(); } } } import java.util.TimerTask; import java.util.Timer; class DeclancherLePerroquet11{ public static void main(String args[]) { Perroquet11 perroquet = new Perroquet11("coco", 3); Timer timer = new Timer(); timer.schedule(perroquet, 3000,2000); String reponse="oui"; do { System.out.println("blabla"); System.out.println("blabla"); System.out.println("voulez-vous encore bavarder ? (o/n)"); reponse = Saisie.litexte(); } while (reponse.equals("o")); timer.cancel(); } } schedule(tache, long delai, long période) programme la tâche après un délai pour une exécution périodiques : les temps sont donnés en millisecondes.

  23. Programmer une tâche avec délai initial et une périodicité Exécution: blabla blabla voulez-vous que le perroquet continue ? (o/n) coco coco coco coco coco coco ncoco coco coco

  24. Partager une ressource: imprimeur public class Ecrivain2 extends Thread { private String texte; private Imprimeur1 imprim; public Ecrivain2(String t, Imprimeur1 i) { imprim=i; texte=t; } public void run() { for (int i=0; i<10; i++) { imprim.imprimer(texte); try { sleep((long)(Math.random() * 100)); } catch (InterruptedException e) {} } System.out.println("ecrivain de " +texte+" a fini"); } } public class Imprimeur1 { private String texte; public Imprimeur1() { texte=""; } public void imprimer(String t) { texte=t; for (int j=0;j<texte.length()-1; j++) { System.out.print(texte.substring(j,j+1)); try { Thread.sleep(100); } catch (InterruptedException e) {}; } System.out.println (texte.substring(texte.length()-1,texte.length())); }} public class Prog56 { public static void main (String argv[]) { Ecrivain2 ecrivainA, ecrivainB; Imprimeur1 imprim= new Imprimeur1(); ecrivainA = new Ecrivain2("ABC", imprim); ecrivainB = new Ecrivain2("XYZ", imprim); ecrivainA.start(); ecrivainB.start(); }}

  25. Partager une ressource: imprimeur Les écrivains passent par un imprimeur pour écrire. Les 2 écrivains s'adressent maintenant à un imprimeur commun. Non seulement, les écrivains écrivent en "interleaving" (entrelacement), mais ils "écrasent" la variable texte de l'imprimeur. L'ensemble est encore illisible. Exécution: AXYYZ Z AXYYZ Z AXYYZ AC XYYZ Z XABBC C AXYYZ Z AXYYZ Z XABBC C XABBC C XABBC ecrivain de XYZ a fini C ecrivain de ABC a fini

  26. Ressource en exclusion mutuelle public class Imprimeur1 { private String texte; public Imprimeur1() { texte=""; } public synchronized void imprimer(String t) { texte=t; for (int j=0;j<texte.length()-1; j++) { System.out.print(texte.substring(j,j+1)); try { Thread.sleep(100); } catch (InterruptedException e) {}; } System.out.println (texte.substring(texte.length()-1,texte.length())); } }

  27. Ressource en exclusion mutuelle Exécution : ABC XYZ ABC XYZ ABC XYZ ABC XYZ ABC XYZ ABC XYZ ABC XYZ ABC XYZ ABC XYZ ABC Xecrivain de ABC a fini YZ ecrivain de XYZ a fini synchronized définit un verrou/ une section en exclusion mutuelle sur la méthode imprimer : Un seul Thread au plus peut exécuter la méthode à la fois.

  28. Synchronisation des threads

  29. Plan • Variables partagées • Problème de l’exclusion mutuelle • Bloc synchronisé • Méthode d’instance synchronisée • Problème de coopération des threads • wait, notifyAll et notify • Demi-synchronisation (wait et sleep) • Problème du Producteur et du Consommateur • Interblocage

  30. Variables partagées class Perroquet20extends Thread { private String cri = null; private int fois = 0; public Perroquet20 (String s, int i) { cri = s; fois = i; } public void repeter() { String repete = cri + " " + compteur; System.out.println(repete); compteur++; try { Thread.sleep((int)(Math.random()*1000)); } catch(InterruptedException e) { } } public void run(){ for (int n=0; n<fois; n++) repeter(); } }} class PerroquetsMatheux20 { private int compteur; public static void main(String args[]) { new PerroquetsMatheux20(); } public PerroquetsMatheux20 () { compteur = 1; Perroquet20 perroquetA = new Perroquet20("coco", 10); Perroquet20 perroquetB = new Perroquet20("bonjour", 10); perroquetA.start(); perroquetB.start(); try { perroquetA.join(); perroquetB.join(); } catch(InterruptedException e) { } System.out.println("compteur = "+compteur); }

  31. Variables partagées La classe Perroquet20 se situe à l’intérieur de la classe PerroquetsMatheux20, et la méthode join est utilisée afin de ne pas afficher la valeur du compteur avant la terminaison des deux Threads. Du fait des règles de visibilité de Java, la variable compteur est visible/accessible à partir de la classe Perroquet20 donc des 2 objets threads perroquetA et perroquetB, par contre, les variables d'instance cri et fois de Perroquet20 existent en autant d'exemplaires que d'instances de Perroquet20. Les 2 threads accède donc à un espace partagé/commun de variables. Contrairement au processus qui possède son propre espace de travail clairement séparé des autres processus, les threads sont exécutés au sein du même processus "java". Exécution: coco 1 bonjour 2 bonjour 3 coco 4 bonjour 5 coco 6 bonjour 7 bonjour 8 bonjour 9 coco 10 bonjour 11 coco 12 bonjour 13 coco 14 coco 15 bonjour 16 coco 17 bonjour 18 coco 19 coco 20 compteur = 21

  32. Problème de l’accès concurrent (partage de ressource) class Perroquet21 extends Thread { private String cri = null; private int fois = 0; public Perroquet21(String s, int i) { cri = s; fois = i; } public void repeter() { int valeur = compteur + 1; String repete = cri + " " + valeur; System.out.println(repete); try { Thread.sleep((int)(Math.random()*100)); } catch(InterruptedException e) { } compteur = valeur; try {Thread.sleep((int)(Math.random()*100));} catch(InterruptedException e) { } } public void run(){ for (int n=0; n<fois; n++) repeter(); } } } class PerroquetsMatheux21{ private int compteur; public static void main(String args[]) { new PerroquetsMatheux21(); } public PerroquetsMatheux21() { compteur = 0; Perroquet21 perroquetA = new Perroquet21("coco", 10); Perroquet21 perroquetB = new Perroquet21("bonjour", 10); //perroquetA.setPriority(perroquetB.getPriority()%2); perroquetA.start(); perroquetB.start(); try { perroquetA.join(); perroquetB.join(); } catch(InterruptedException e) { } System.out.println("compteur = "+compteur); }

  33. Problème de l’accès concurrent (partage de ressource) Exécution: coco 1 bonjour 1 coco 2 coco 3 bonjour 3 coco 4 bonjour 4 coco 5 bonjour 5 coco 6 bonjour 7 coco 7 bonjour 8 coco 8 bonjour 9 coco 9 coco 10 bonjour 10 bonjour 11 bonjour 12 compteur = 12 Les 2 threads perroquet travaillent alternativement : un thread peut être suspendu au milieu de l’exécution de sa méthode repeter pour que le contrôleur de thread laisse l’autre s'exécuter.

  34. Définir une section critique : Bloc synchronisé public void repeter() { synchronized (compteur) { int valeur = compteur.getValeur() + 1; String repete = cri + " " + valeur; System.out.println(repete); try { Thread.sleep((int)(Math.random()*100)); } catch(InterruptedException e) { } compteur.setValeur(valeur); } try { Thread.sleep((int)(Math.random()*100)); } catch(InterruptedException e) { } } public void run(){ for (int n=0; n<fois; n++) repeter(); } } class Compteur { private int valeur = 0; public int getValeur() { return valeur; } public void setValeur(int v) { valeur = v; } } } class PerroquetsMatheux22{ private Compteur compteur; public static void main(String args[]) { new PerroquetsMatheux22(); } public PerroquetsMatheux22() { compteur = new Compteur(); Perroquet22 perroquetA = new Perroquet22("coco", 10); Perroquet22 perroquetB = new Perroquet22("bonjour", 10); perroquetA.setPriority(perroquetB.getPriority()%2); perroquetA.start(); perroquetB.start(); try { perroquetA.join(); perroquetB.join(); } catch(InterruptedException e) { } System.out.println("compteur = "+compteur.getValeur()); } class Perroquet22 extends Thread { private String cri = null; private int fois = 0; public Perroquet22(String s, int i) { cri = s; fois = i; }

  35. Définir une section critique : Bloc synchronisé Exécution: bonjour 1 coco 2 bonjour 3 coco 4 bonjour 5 coco 6 coco 7 bonjour 8 coco 9 bonjour 10 bonjour 11 coco 12 coco 13 bonjour 14 coco 15 bonjour 16 bonjour 17 coco 18 coco 19 bonjour 20 compteur = 20 • Le mot-clé synchronized définit un bloc d'instruction qui ne peut s'exécuter qu'exclusivement même si plusieurs threads souhaitent l'exécuter : • Lorsque le thread perroquetA exécute ce bloc synchronisé, et que le thread perroquetB souhaite commencer l'exécution de ce même bloc, alors le thread perroquetB doit attendre. Quand le thread perroquetA aura finit, le thread perroquetB pourra reprendre. • Seul un thread à la fois peut exécuter un bloc synchronisé. • On dit que le bloc est en exclusion mutuelle ou encore que c'est une section critique. • Les autres threads, s'ils désirent exécuter cette section, doivent attendre que le thread en section critique la termine. • Si plusieurs threads attendent pour un même bloc synchronisé qui "se libère", le contrôleur de thread n'en autorisera qu'un à l'exécuter. • L'appel à sleep() ne provoque pas de sortie de la section critique.

  36. Bloc synchronisé

  37. Bloc synchronisé synchronized(objet) signifie que le bloc est en exclusion mutuelle relativement à un moniteur (monitor) de cet objet : sont en exclusion mutuelle, les threads synchronisés sur le même objet. Le thread de gauche et celui du milieu ont une section critique mutuelle, le thread de droite a une section critique mais pas avec les 2 autres threads. Le moniteur "tient" le rôle de superviseur s'assurant que seul un thread à la fois peut exécuter la section critique qu'il supervise : c'est un système de verrouillage (lock). Le thread qui exécute synchronized d'un objet devient propriétaire du moniteur de cet objet. Thread.sleep ne fait pas perdre la propriété d'un moniteur même temporairement. Il n'est pas souhaitable de mettre un sleep(délai) dans une zone synchronisée : on préfèrera wait(timeout).

  38. Méthode d’instance synchronisée public void repeter() { int valeur = compteur.plus1(); String repete = cri + " " + valeur; System.out.println(repete); try { Thread.sleep((int)(Math.random()*100)); } catch(InterruptedException e) { } } public void run(){ for (int n=0; n<fois; n++) repeter(); } } class Compteur { private int valeur = 0; public synchronized int plus1() { return ++valeur; } } } class PerroquetsMatheux23 { private Compteur compteur; public static void main(String args[]) { new PerroquetsMatheux23(); } public PerroquetsMatheux23() { compteur = new Compteur(); Perroquet23 perroquetA = new Perroquet23("coco", 10); Perroquet23 perroquetB = new Perroquet23("bonjour", 10); perroquetA.setPriority(perroquetB.getPriority()%2); perroquetA.start(); perroquetB.start(); try { perroquetA.join(); perroquetB.join(); } catch(InterruptedException e) { } System.out.println("compteur = "+compteur.valeur); } class Perroquet23 extends Thread { private String cri = null; private int fois = 0; public Perroquet23(String s, int i) { cri = s; fois = i; }

  39. Méthode d’instance synchronisée Le mot synchronised définit le bloc de la méthode en exclusion mutuelle içi c'est l'objet compteur qui "monitorise", la méthode synchronisée est aussi un mécanisme d'exclusion mutuelle sur une portion de code : le moniteur qui supervise cette section critique est celui de l'objet sur lequel est appelée la méthode. remarque : synchronized méthode(paramètres) { bloc d'instructions } La synchronisation ralentit l'ensemble de l'exécution, donc il faut limiter le nombre de portion synchronisée et leur taille (en instructions), il est possible de synchroniser sur une classe pour accéder en exclusion mutuelle sur les variables de classe, idem pour une méthode de classe synchronisée. Le mécanisme de "monitor" d'un objet s'applique à toutes les instances de Object : c'est donc un mécanisme implémenté au coeur de JAVA, un seul thread peut être à la fois le propriétaire du moniteur d'un objet. Exécution : coco 1 bonjour 2 bonjour 3 coco 4 coco 5 bonjour 6 bonjour 7 coco 8 bonjour 9 coco 10 bonjour 11 coco 12 bonjour 13 bonjour 14 coco 15 bonjour 16 coco 17 bonjour 18 coco 19 coco 20 compteur = 20 est équivalent à : méthode(paramètres) { synchronized(this) { bloc d'instructions } }

  40. Rappel

  41. Multitâche • Un système d’exploitation est dit multitâche ou à temps partagé lorsque plusieurs «tâches» (processus) peuvent être exécutées simultanément. • 2 types de système d’exploitation multitâche : • Multitâche coopératif (non-préemptif) : • Une forme simple de multitâche où chaque processus doit explicitement permettre à une autre tâche de s’exécuter. Cette approche simplifie l’architecture du système d’exploitation mais présente plusieurs inconvénients : • Si un des processus ne redonne pas la main à un autre processus, par exemple si le processus est bugué, le système entier peut s’arrêter. • Multitâche préemptif : • Pour remédier à cette situation, les systèmes ont évolué pour utiliser une approche nommée « multitâche préemptif ». Dans un tel système, le processeur signale au système d’exploitation que le processus en cours d’exécution doit être mis en pause pour permettre l’exécution d’un autre processus. • Ne pas attendre des heures qu’un programme planté cède la priorité.

  42. Processus vs Thread • La plupart des systèmes d’exploitation offrent la distinction entre : • Processus lourd : • Un programme (un ensemble d’instructions) à exécuter. • Sont complètement isolés les uns des autres. • Processus léger:Thread • Portion de code capable de s’exécuter en parallèle à d’autres traitements. • Ils partagent code, données et ressources. • Mais peuvent disposer de leurs propres données.

  43. Création de thread • 2 manières pour créer un Thread : • Une classe qui dérive de java.lang.Thread. • java.lang.Thread implémente Runnable. • Il faut redéfinir la méthode run(). • Une classe qui implémente l’interface Runnable • Il faut implémenter la méthode run()

  44. Méthode 1 : Sous-classer Thread class Thread1 extends Thread { Thread1() {...} // Le constructeur ... public void run() { ... // Ici ce que fait le thread } } ... Thread1 p1 = new Thread1(); // Création du thread p1 p1.start(); // Démarre le thread et exécute p1.run()

  45. Méthode 2 : une classe qui implémente Runnable class Thread2 implements Runnable { Thread2() { ...} // Constructeur ... public void run() { ... // Ici ce que fait le thread } } ... Thread2 p = new Thread2(); Thread p2 = new Thread(p); ... p2.start(); // Démarre le thread et exécute p.run()

  46. Quelle solution choisir ? • Méthode 1 : sous-classer Thread • Lorsqu’on désire paralléliser une classe qui n’hérite pas déjà d’une autre classe (classe autonome). • Attention : héritage simple. • Méthode 2 : implémenter Runnable • Lorsqu’une super-classe est imposée. • Cas des applets public class MyThreadApplet extends Applet implements Runnable {}

  47. Applet et thread • Un thread qui conte de 1 à 20, il fait l’affichage à la fois dans l’applet et sur la console. import java.applet.*; import java.awt.*; public class CounterThread extends Applet implements Runnable { Thread t; int Count; public void init() { Count=0; t=new Thread(this); // t doit prendre un objet this comme paramètre t.start(); } public void run() { while (Count < 20) { Count++; repaint(); try { t.sleep(1000); } catch (InterruptedException e) {} } } public void paint(Graphics g) { g.drawString(Integer.toString(Count),10,10); System.out.println("Count= "+Count); } }

  48. ThreadGroup • ThreadGroup : dans java.lang. • Plusieurs Threads peuvent s’exécuter en même temps, il serait utile de pouvoir les manipuler comme une seule entité. • Pour les suspendre, • Pour les arrêter, ... • Java offre cette possibilité via l’utilisation des groupes de threads : java.lang.ThreadGroup. • On groupe un ensemble nommé de threads. • Ils sont contrôlés comme une seule unité. • La JVM crée au minimum un groupe de threads nommé main. • Par défaut, un thread appartient au même groupe que celui qui l’a crée (son père). • getThreadGroup() : pour connaitre son groupe.

  49. Création d’un groupe de threads • Pour créer un groupe de threads : ThreadGroup groupe1 = new ThreadGroup("GP1"); Thread p1 = new Thread(groupe1, "P1"); Thread p2 = new Thread(groupe1, "P2"); Thread p3 = new Thread(groupe1, "P3"); • Le contrôle des ThreadGroup passe par l’utilisation des méthodes standards qui sont partagées avec Thread : • Exemple : interrupt(), destroy(), • Par exemple : appliquer la méthode interrupt() à un ThreadGroup revient à invoquer pour chaque Thread du groupe cette même méthode.

  50. Création d’une arborescence de threads ThreadGroup groupe1 = new ThreadGroup("GP1"); Thread p1 = new Thread(groupe1, "P1"); Thread p2 = new Thread(groupe1, "P2"); Thread p3 = new Thread(groupe1, "P3"); ThreadGroup groupe11 = new ThreadGroup(groupe1, "GP11"); Thread p4 = new Thread(groupe11, "P4"); Thread p5 = new Thread(groupe11, "P5");

More Related