as Programmation - Threads - as Principe des threads Créer un
Transcription
as Programmation - Threads - as Principe des threads Créer un
Gestion des threads Gestion des timers Gestion des threads Gestion des timers 1 Gestion des threads Principe Création Cycle Synchronisation Collections 2 Gestion des timers Principe Création Exemple Programmation - Threads Nicolas Malandain May 17, 2005 as as Architecture des Systèmes d’Information Threads Gestion des threads Gestion des timers Architecture des Systèmes d’Information 1 / 32 Principe Création Cycle Synchronisation Collections Threads Gestion des threads Gestion des timers Principe des threads 2 / 32 Principe Création Cycle Synchronisation Collections Créer un thread avec la classe Thread programme programme thread Description partielle public class Thread implements Runnable { public Thread() public Thread(Runnable target) public Thread(String name) ... public void setDaemon(boolean on) // la JVM se termine s’il ne reste que des démons public void run() public void start() ... } threads Créer un thread hériter de Thread Définition Un thread correspond à l’exécution d’une séquence d’instructions dans un programme. Plusieurs threads peuvent s’exécuter dans un programme, ils partagent alors les ressources de ce programme. Threads redéfinir la méthode run (contient le code exécuté par le thread) instancier la classe lancer le thread (start) 3 / 32 cm07-thread.pdf — May 17, 2005 — 1 Threads 4 / 32 Gestion des threads Gestion des timers Principe Création Cycle Synchronisation Collections Gestion des threads Gestion des timers Principe Création Cycle Synchronisation Collections Créer un thread avec l’interface Runnable CompteurThread.java public class CompteurThread extends Thread { private int bornemax; Exécutions public CompteurThread(String nom, int bornemax) { super(nom); this .bornemax = bornemax; } public void run() { for (int i=0; i<bornemax; i++) { System.out.println(getName()+" : "+i); try { sleep(100); } catch (InterruptedException e) { } } } public static void main(String[] args) { CompteurThread ct1 = new CompteurThread("A",10); CompteurThread ct2 = new CompteurThread("B",10); ct1.start(); ct2.start(); ct1.interrupt(); } A A B A B A B A B A B A B A B A B A B B : : : : : : : : : : : : : : : : : : : : 0 1 0 2 1 3 2 4 3 5 4 6 5 7 6 8 7 9 8 9 B A A B A B A B A B A B A B A B A B A B : : : : : : : : : : : : : : : : : : : : Description 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 public interface Runnable { public void run() } L’interface Runnable permet de créer des threads lorsque l’héritage n’est pas possible Créer un thread implémeter l’interface Runnable définir la méthode run (contient le code exécuté par le thread) instancier un thread avec un objet Runnable instancié lancer le thread (start) } Threads Gestion des threads Gestion des timers Threads 5 / 32 Principe Création Cycle Synchronisation Collections 6 / 32 Gestion des threads Gestion des timers Principe Création Cycle Synchronisation Collections Cycle de vie d’un thread CompteurThreadRunnable.java public class CompteurThreadRunnable implements Runnable { private int bornemax; public CompteurThreadRunnable(int bornemax) { this .bornemax = bornemax; } public void run() { Thread monThread = Thread.currentThread(); for (int i=0; i<bornemax; i++) { System.out.println(monThread.getName()+" : "+i); try { monThread.sleep(100); } catch (InterruptedException e) { } } } Exécutable créé (actif) start prêt déblocage IO notify, interrupt public Thread go(String nom) { Thread thread = new Thread(this,nom); thread.start(); return thread; } public static void main(String[] args) { CompteurThreadRunnable ctr1 = new CompteurThreadRunnable(10); CompteurThreadRunnable ctr2 = new CompteurThreadRunnable(10); ctr1.go("A"); ctr2.go("B"); } fin du code terminé élu attente d'IO wait, join, sleep bloqué (synchro, sleep, IO, ...) } Threads 7 / 32 cm07-thread.pdf — May 17, 2005 — 2 Threads 8 / 32 Gestion des threads Gestion des timers Principe Création Cycle Synchronisation Collections Gestion des threads Gestion des timers Arrêter un thread Principe Création Cycle Synchronisation Collections CompteurThreadArret.java Méthode Un thread doit se terminer naturellement de lui même. Le plus simple est un changement d’état interne donnant fin à la méthode run. Attention !!! La méthode stop n’est plus à utiliser (non supportée dans les futures versions), elle ne permettait pas une fin propre du thread. Threads Gestion des threads Gestion des timers 9 / 32 Principe Création Cycle Synchronisation Collections CompteurThreadCourantArret.java Gestion des threads Gestion des timers ... public class CompteurThreadCourantArret implements Runnable { private int pas; private int compteur; private Thread thisThread; private boolean etat=true; 10 / 32 Principe Création Cycle Synchronisation Collections CompteurThreadCourantArret.java public static void main(String[] args) { CompteurThreadCourantArret ct1 = new CompteurThreadCourantArret(1); Thread t1 = new Thread(ct1,"A"); t1.start(); Scanner input = new Scanner(System.in); String reponse; do { System.out.println("Lancement du 2eme thread ? O/N "); reponse = input.next(); } while (!reponse.toUpperCase().equals("O")); Thread t2 = new Thread(ct1,"B"); t2.start(); do { System.out.println("Arr^ eter le thread ? O/N"); reponse = input.next(); } while (!reponse.toUpperCase().equals("O")); ct1.stop(); } public CompteurThreadCourantArret(int pas) { setPas(pas); } public void setPas(int pas) { this .pas = pas; } public void stop() { thisThread=null; } public void run() { compteur = 0; thisThread = Thread.currentThread(); while (thisThread == Thread.currentThread()) { compteur += pas; System.out.println(thisThread.getName()+" : "+compteur); try { thisThread.sleep(100); } catch (InterruptedException e) { } } } Threads public class CompteurThreadArret extends Thread { private int compteur; private boolean etat=true; public CompteurThreadArret(String nom, int pas) { super(nom); setPas(pas); } public void setFin() { etat=false; } public void run() { while (etat) { compteur += pas; System.out.println(getName()+" : "+compteur); try { sleep(100); } catch (InterruptedException e) { } } } public static void main(String[] args) { CompteurThreadArret ct1 = new CompteurThreadArret("A",1); ct1.start(); Scanner input = new Scanner(System.in); String reponse; do { System.out.println("Voulez vous arr^ eter le thread ? O/N"); reponse = input.next(); } while (!reponse.toUpperCase().equals("O")); ct1.setFin(); } } Threads Dans cet exemple, un seul et unique thread peut être lancé sur le même objet ct1. Le changement d’état de ct1 (stop) ou le lancement d’un nouveau thread arrête le thread. 11 / 32 cm07-thread.pdf — May 17, 2005 — 3 Threads 12 / 32 Gestion des threads Gestion des timers Principe Création Cycle Synchronisation Collections Gestion des threads Gestion des timers Principe Principe Création Cycle Synchronisation Collections Exemples des producteurs/consommateurs 1 ProducteurConsommateurPBAL.java Rappel du problème Plusieurs threads veulent accéder en même temps à une ressource partagée critique. L’accès à cette ressource doit être exclusif. public class ProducteurConsommateurPBAL { public static void main(String[] args) { PetiteBoiteAuxLettres pbal = new PetiteBoiteAuxLettres(); ProducteurPBAL p1 = new ProducteurPBAL(pbal, "lettre", 10); ProducteurPBAL p2 = new ProducteurPBAL(pbal, "lettre", 10); ConsommateurPBAL c1 = new ConsommateurPBAL(pbal); Thread t1 = new Thread(p1, "P1"); t1.setPriority(1); t1.start(); Thread t2 = new Thread(p2, "P2"); t2.setPriority(10); t2.start(); new Thread(c1, "C1").start(); } Java ⇒ synchronized le modificateur synchronized sur une méthode vérouille (lock) l’objet lors de la réception du message et le dévérouille à la fin de l’exécution de la méthode. } Threads Gestion des threads Gestion des timers 13 / 32 Threads Principe Création Cycle Synchronisation Collections Gestion des threads Gestion des timers 14 / 32 Principe Création Cycle Synchronisation Collections ProducteurPBAL.java PetiteBoiteAuxLettres.java public class ProducteurPBAL implements Runnable { private PetiteBoiteAuxLettres pbal; private String prefixe; private int nbLettres; /** Creates a new instance of ProducteurPBAL */ public ProducteurPBAL(PetiteBoiteAuxLettres pbal, String prefixe, int nbLettres) { this .pbal = pbal; this .prefixe = prefixe; this .nbLettres = nbLettres; } public class PetiteBoiteAuxLettres { private String lettre; private boolean nouvelleValeurDisponible = false; public synchronized void put(String lettre) throws InterruptedException { while (nouvelleValeurDisponible) wait(); System.out.println(lettre+" -> boite"); nouvelleValeurDisponible = true; this .lettre = lettre; notifyAll(); } public synchronized String get() throws InterruptedException { while (!nouvelleValeurDisponible) wait(); System.out.println("boite -> "+lettre); nouvelleValeurDisponible = false; notifyAll(); return lettre; } public String produire() { String lettre = prefixe+" : "+(int)(Math.random()*100); System.out.println(Thread.currentThread().getName()+" envoie "+lettre); return lettre; } public void run() { try { for (int i=0; i < nbLettres; i++) pbal.put(produire()); } catch (InterruptedException ie) {} } } } Threads 15 / 32 cm07-thread.pdf — May 17, 2005 — 4 Threads 16 / 32 Gestion des threads Gestion des timers Principe Création Cycle Synchronisation Collections Gestion des threads Gestion des timers Principe Création Cycle Synchronisation Collections Exécution P1 envoie lettre-P1 : 73 lettre-P1 : 73 -> boite P1 envoie lettre-P1 : 16 P2 envoie lettre-P2 : 73 boite -> lettre-P1 : 73 C1 reçoit lettre-P1 : 73 lettre-P2 : 73 -> boite P2 envoie lettre-P2 : 90 boite -> lettre-P2 : 73 C1 reçoit lettre-P2 : 73 lettre-P2 : 90 -> boite P2 envoie lettre-P2 : 60 boite -> lettre-P2 : 90 C1 reçoit lettre-P2 : 90 lettre-P2 : 60 -> boite P2 envoie lettre-P2 : 1 boite -> lettre-P2 : 60 C1 reçoit lettre-P2 : 60 lettre-P2 : 1 -> boite P2 envoie lettre-P2 : 53 boite -> lettre-P2 : 1 C1 reçoit lettre-P2 : 1 lettre-P2 : 53 -> boite P2 envoie lettre-P2 : 19 boite -> lettre-P2 : 53 C1 reçoit lettre-P2 : 53 lettre-P2 : 19 -> boite ConsommateurPBAL.java public class ConsommateurPBAL implements Runnable { private PetiteBoiteAuxLettres pbal; public ConsommateurPBAL(PetiteBoiteAuxLettres pbal) { this .pbal = pbal; } public void consommer(String lettre) { System.out.println(Thread.currentThread().getName()+" reçoit "+lettre); } public void run() { try { while (true) consommer(pbal.get()); } catch (InterruptedException ie) {} } } Threads Gestion des threads Gestion des timers 17 / 32 P2 envoie lettre-P2 : 57 boite -> lettre-P2 : 19 C1 reçoit lettre-P2 : 19 lettre-P2 : 57 -> boite P2 envoie lettre-P2 : 14 boite -> lettre-P2 : 57 C1 reçoit lettre-P2 : 57 lettre-P2 : 14 -> boite P2 envoie lettre-P2 : 63 boite -> lettre-P2 : 14 C1 reçoit lettre-P2 : 14 lettre-P2 : 63 -> boite P2 envoie lettre-P2 : 95 boite -> lettre-P2 : 63 C1 reçoit lettre-P2 : 63 lettre-P2 : 95 -> boite boite -> lettre-P2 : 95 C1 reçoit lettre-P2 : 95 lettre-P1 : 16 -> boite P1 envoie lettre-P1 : 5 boite -> lettre-P1 : 16 C1 reçoit lettre-P1 : 16 lettre-P1 : 5 -> boite P1 envoie lettre-P1 : 67 boite -> lettre-P1 : 5 C1 reçoit lettre-P1 : 5 lettre-P1 : 67 -> boite P1 envoie lettre-P1 : 7 boite -> lettre-P1 : 67 C1 reçoit lettre-P1 : 67 lettre-P1 : 7 -> boite P1 envoie lettre-P1 : 93 boite -> lettre-P1 : 7 C1 reçoit lettre-P1 : 7 lettre-P1 : 93 -> boite P1 envoie lettre-P1 : 24 boite -> lettre-P1 : 93 C1 reçoit lettre-P1 : 93 lettre-P1 : 24 -> boite P1 envoie lettre-P1 : 22 boite -> lettre-P1 : 24 C1 reçoit lettre-P1 : 24 lettre-P1 : 22 -> boite P1 envoie lettre-P1 : 21 boite -> lettre-P1 : 22 C1 reçoit lettre-P1 : 22 lettre-P1 : 21 -> boite P1 envoie lettre-P1 : 47 boite -> lettre-P1 : 21 C1 reçoit lettre-P1 : 21 lettre-P1 : 47 -> boite boite -> lettre-P1 : 47 C1 reçoit lettre-P1 : 47 Threads Principe Création Cycle Synchronisation Collections Gestion des threads Gestion des timers 18 / 32 Principe Création Cycle Synchronisation Collections Exemples des producteurs/consommateurs 2 ProducteurBAL.java public class ProducteurBAL implements Runnable { private LinkedBlockingQueue<String> bal; private String prefixe; private int nbLettres; public ProducteurBAL(LinkedBlockingQueue<String> bal, String prefixe, int nbLettres) { this .bal = bal; this .prefixe = prefixe; this .nbLettres = nbLettres; } ProducteurConsommateurBAL.java public class ProducteurConsommateurBAL { public static void main(String[] args) { // boite aux lettres de taille 3 LinkedBlockingQueue<String> bal = new LinkedBlockingQueue<String>(3); ProducteurBAL p1 = new ProducteurBAL(bal, "lettre-P1", 5); ProducteurBAL p2 = new ProducteurBAL(bal, "lettre-P2", 5); ConsommateurBAL c1 = new ConsommateurBAL(bal); new Thread(p1, "P1").start(); new Thread(p2, "P2").start(); new Thread(c1, "C1").start(); } public String produire() { String lettre = prefixe+" : "+(int)(Math.random()*100); System.out.println(Thread.currentThread().getName()+" envoie "+lettre); return lettre; } public void run() { try { for (int i=0; i < nbLettres; i++) bal.put(produire()); } catch (InterruptedException ie) {} } } } Threads 19 / 32 cm07-thread.pdf — May 17, 2005 — 5 Threads 20 / 32 Gestion des threads Gestion des timers Principe Création Cycle Synchronisation Collections Gestion des threads Gestion des timers Principe Création Cycle Synchronisation Collections Vérouillage (Lock) réentrant Définition Un thread possédant un verrou sur un objet peut ré-accéder sans problème à cette objet via une méthode requérant un verrou sur ce même objet. ConsommateurBAL.java public class ConsommateurBAL implements Runnable { private LinkedBlockingQueue<String> bal; public ConsommateurBAL(LinkedBlockingQueue<String> bal) { this .bal = bal; } Les vérouillages de Java sont réentrants, ce qui évite les “deadlocks” public void consommer(String lettre) { System.out.println(Thread.currentThread().getName()+" reçoit "+lettre); } Exemple de deadlock, si les vérrouillages ne sont pas réentrants public void run() { try { while (true) consommer(bal.take()); } catch (InterruptedException ie) {} } public class ObjetCritique { public synchronized void methode1() { methode2(); System.out.println("methode1()"); } public synchronized void methode2() { System.out.println("methode2"); } } } Threads Gestion des threads Gestion des timers Threads 21 / 32 Principe Création Cycle Synchronisation Collections Gestion des threads Gestion des timers Gestion des threads via Object 22 / 32 Principe Création Cycle Synchronisation Collections Synchronisation d’une partie de code wait(), wait(long timeout), wait(long timeout, int nanos) Met le thread courant en attente et relâche le verrou. Le thread attend jusqu’à ce qu’une des 2 possibilités soit rencontrée : Syntaxe le thread est réveillé via un notify ou notifyAll le temps d’attente est écoulé (sauf wait()) synchronized (objet) { // code critique } timeout : millisecondes nanos : nanosecondes Le thread pose le verrou sur l’objet. Le thread repart une fois qu’il réobtient le verrou. notify(), notifyAll() Réveille le ou les threads en attente sur l’objet. Threads 23 / 32 cm07-thread.pdf — May 17, 2005 — 6 Threads 24 / 32 Gestion des threads Gestion des timers Principe Création Cycle Synchronisation Collections Gestion des threads Gestion des timers Synchronisation et Collections Principe Création Cycle Synchronisation Collections Création des wrappers de synchronisation avec Collections public public public public public public Attention La plupart des collections ne sont pas sures au niveau des threads Quelques collections sures la classe Vector static static static static static static <E> Collection<E> synchronizedCollection(Collection<E> c); <E> List<E> synchronizedList(List<E> list); <K, V> Map<K, V> synchronizedMap(Map<K, V> m); <E> Set<E> synchronizedSet(Set<E> s); <K,V> SortedMap<K, V> synchronizedSortedMap(SortedMap<K, V> m); <E> SortedSet<E> synchronizedSortedSet(SortedSet<E> s); Remarques La syntaxe suivante permet de ne garder la référence que sur la liste synchronisée afin d’éviter les erreurs. les classes implémentant BlockingQueue ... List<Type> list; list = Collections.synchronizedList(new LinkedList<Type>()); Utilisation des Wrappers de collections Les wrappers ajoutent des fonctionnalités supplémentaires aux méthodes des collections (design pattern de décoration). Les wrappers de synchronisation permettent d’ajouter la synchronisation sur les collections. Threads Gestion des threads Gestion des timers L’itération (avec ou sans itérateur) sur les collections doit être effectuée manuellement synchronized(list) for (Type elt : list) toto(e); Threads 25 / 32 Principe Création Exemple Gestion des threads Gestion des timers Timer Timer Description Les timers permettent de planifier l’exécution de tâches. API de la classe Timer 26 / 32 Principe Création Exemple Timer() Timer(boolean isDaemon) Timer(String name) Timer(String name, boolean isDaemon) void cancel() void schedule(TimerTask task, Date time) void schedule(TimerTask task, Date firstTime, long period) void schedule(TimerTask task, long delay) void schedule(TimerTask task, long delay, long period) // méthode précise ne tenant pas compte des retards void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) void scheduleAtFixedRate(TimerTask task, long delay, long period) lancement à une date donnée répétition à intervalle régulier les tâches sont des threads Fonctionnement un Timer est toujours associé à un Thread l’exécution du thread est programmée via les méthodes schedule la tâche a exécuter est créée par héritage de TimerTask (Runnable) Threads 27 / 32 cm07-thread.pdf — May 17, 2005 — 7 Threads 28 / 32 Gestion des threads Gestion des timers Principe Création Exemple Gestion des threads Gestion des timers Principe Création Exemple TimerTask ... RappellerRDV.java public static void main(String[] args) { try { SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); Date date = sdf.parse(args[1]); RappellerRDV rdv = new RappellerRDV(args[0], date, Integer.parseInt(args[2]), Integer.parseInt(args[3])); } catch (Exception e) { System.out.println("Usage : java RappellerRDV \"message\" \"dd/MM/yyyy HH:mm:ss\" rappelnbminutesavant nbrappels"); } } La classe TimerTask public class TimerTask implements Thread { boolean cancel() // annule la t^ ache abstract void run() // méthode à redéfinir pour l’exécution de la t^ ache long scheduledExecutionTime() // rertourne la date de dernière exécution } Création d’une tâche Une tâche se définit par héritage de TimerTask et redéfinition de la méthode run() Threads Gestion des threads Gestion des timers Exécution java fr.insarouen.asi.prog.cours7.RappellerRDV "Hello" "17/05/2005 14:47:10" 10 3 (rappel) 17 mai 2005 14:47:10 : Hello (rappel) 17 mai 2005 14:47:10 : Hello (rappel) 17 mai 2005 14:47:10 : Hello 17 mai 2005 14:47:10 : Hello 29 / 32 Principe Création Exemple Threads Gestion des threads Gestion des timers 30 / 32 Principe Création Exemple RappellerRDV.java (2 classes internes) ... RappellerRDV.java public class RappellerRDV { private Timer timer; private DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, Locale.FRANCE); public RappellerRDV(String texte, Date date, int nbminutesavant, int nbfois) { timer = new Timer(); timer.schedule(new RendezVous(texte, date), date); Calendar cal = Calendar.getInstance(); cal.setTime(date); // pour tester remplacer MINUTE/SECOND et 60000/1000 cal.add(Calendar.MINUTE,-1*nbminutesavant); timer.schedule(new Rappel(texte, date, nbfois), cal.getTime(), (60000*nbminutesavant)/nbfois); } Threads private class RendezVous extends TimerTask { private String texte; private Date date; public RendezVous(String texte, Date date) { this .texte = texte; this .date = date; } public void run() { System.out.println(dateFormat.format(date)+" : "+texte); timer.cancel(); } } private class Rappel extends TimerTask { private String texte; private Date date; private int nbfois; public Rappel(String texte, Date date, int nbfois) { this .texte = texte; this .date = date; this .nbfois = nbfois; } public void run() { System.out.println("(rappel) "+dateFormat.format(date)+" : "+texte); if (nbfois-- <= 0) cancel(); } } 31 / 32 cm07-thread.pdf — May 17, 2005 — 8 Threads 32 / 32