Cours de JAVA La gestion de processus Emmanuel ADAM Institut des Sciences et Techniques de Valenciennes Université de Valenciennes et du Hainaut-Cambrésis source principale : «Thinking in Java (B. Eckel)» http://www.laltruiste.com/
Qu est-ce qu un thread? Un thread est un processus qui hérite de son processus père. Java permet la mise en commun de code, de données («light weight process» ou «processus léger») Le processus partage ses ressources système (entrées/sorties,...). La programmation est enfin sans contrainte!
Utilité des processus Afin de «rendre la main» au programme principal. Afin d'interagir efficacement. Pour la gestion des graphiques, des applets.
Définition d un Thread Pour créer une tâche, il suffit de faire hériter sa classe de la classe Thread. La principale méthode de Thread est run() La méthode yield() suspend très brièvement la tâche La classe Thread implémente l interface Runnable. Implémenter Runnable oblige à définir la méthode start().
Exemple de thread class une_tache extends Thread une_tache(string nom) super(nom); // appel au constructeur de Thread public void run() while (true) System.out.println("Mon nom est : " + getname()); Thread.yield(); // petite pause public class ExempleTache public static void main(string args[]) une_tache tache1 = new une_tache("tache 1"); une_tache tache2 = new une_tache("tache 2"); tache1.start(); tache2.start(); while (true) System.out.println("Je suis la tâche principale"); Thread.yield();
Run et Start La méthode run() est généralement constituée d'une boucle infinie La méthode start(), est utilisée pour lancer le Thread et appelle la méthode run()
Accès simultanés à une ressource La programmation multitâche implique le partage de ressources (mémoire, objets). Dans certains cas, ceci peut être problématique. Pour cela, Java dispose du mot clé synchronized qui assure l exclusion mutuelle d une section critique en informant le moniteur (gestionnaire de mémoire). exemple: public synchronized void compte() empêche toute instance de la classe de lancer compte tant qu elle ne s est pas terminée. Lorsqu'un thread t1 exécute cette méthode à partir d'un objet, un autre thread t2 ne peut pas l'exécuter à partir du même objet. Mais t2 peut l'exécuter pour une autre instance de la même classe.
Synchronisation et efficacité Chaque objet possède un verrou lock ou moniteur. (Cela fait partie de la classe Object). Exemple de verrou :... synchronized (compteur) compteur.add() ; compteur.mult() ;...... 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. 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.
Le processus dans tous ses états Nouveau : il est créé mais start() n'est pas encore appelé Exécutable : il a été initialisé (il est exécuté si l'ordonnanceur le décide) Mort : fin normale d'un Thread (sortie de la méthode run() Bloqué : l'ordonnanceur ignore le Tread
Raison d'un blocage Le thread peut être : endormi : sleep(milliseconds) suspendu : par suspend(). Il sera alors relancé par resume() en attente : par wait(). Il pourra alors être réactivé par notify() ou notifyall() IO : en attente d'une Entrée/Sortie synch : en attente de la libération d'une ressource synchronisée
Différence Wait/Suspend Suspend : Tient le verrou de l'objet, donc bloque dans les méthode synchronisées et peut être source de DeadLock Wait ne tient pas les verrous et donc ne doit pas être appelé d'un bloc synchronisé, sous risque d'accès concurrent
Remarques... Il faut éviter l'utilisation de : stop : cette méthode libère les verrous même si l'objet est dans un état instable Il faut le remplacer par un test dans la boucle du run() lever l'exception InterrupedException en utilisant la méthode interrupt() suspend (et donc resume), car ils sont sources de nombreux deadlock
Exercice Un exemple classique de programmation de processus : le producteur-consommateur. Un producteur produit des objets dans une pile, 2 consommateurs en consomment. Ecrire les classes TabObjets, Producteur et Consommateur, ainsi que la classe principale.
Exemple de thread (1/5) class TabObjets private Object tampon[]; private int taille, produits, consommes, nbobjetscourants; public TabObjets (int _taille) tampon = new Object[taille]; taille = _taille; produits = consommes = nbobjetscourants = 0; public synchronized void depose(object obj) while (nbobjetscourants == (taille-1)) try wait(); // s il y a trop d objet, mettre en attente le déposeur catch (InterruptedException e) nbobjetscourants++; tampon[nbobjetscourants] = obj; produits++; notify(); // réveille un processus en attente de non vide... Emmanuel ADAM Université de Valenciennes et du Hainaut Cambrésis
Exemple de thread (2/5) class TabObjets... public synchronized Object preleve() while (nbobjetscourants == 0) try wait(); // s il n y a pas assez d objets, mettre en attente le retireur catch (InterruptedException e) Object obj = tampon[nbobjetscourants]; tampon[nbobjetscourants] = null; nbobjetscourants --; consommes++; notify(); // réveille un processus en attente de non plein return obj;
Exemple de thread (3/5) class Producteur extends Thread private TabObjets tab; private int val = 0; String nom; public producteur(string _nom, TabObjets _tab) nom = _nom; tab = _tab; public void run() while (true) System.out.println(nom + " : je dépose l objet "+val); tab.depose(new Integer(val)); val++; try Thread.sleep((int)(Math.random()*100)); // 100 ms max catch (InterruptedException e)
Exemple de thread (4/5) class Consommateur extends Thread private TabObjets tab; String nom; public consommateur(string _nom, TabObjets _tab) nom = _nom; tab = _tab; public void run() while (true) String nom_obj = (Integer)tab.preleve()).toString(); System.out.println(nom + ": je prélève l objet " + nom_obj) ; try Thread.sleep((int)(Math.random()*200)); // max 200 ms catch (InterruptedException e)
Exemple de thread (5/5) class ProductConsom public static void main(string args[]) TabObjets tab = new TabObjets(5); Producteur prod = new Producteur("product", tab); Consommateur cons1 = new Consommateur("consom 1", tab); Consommateur cons2 = new Consommateur("consom 2", tab); prod.start(); cons1.start(); cons2.start(); try Thread.sleep(10000); // attente 10 sec. catch (InterruptedException e) cons2.stop(); cons1.stop(); prod.stop();
Volatilité La JVM de Java est composée d'une mémoire centrale et d'une mémoire cache Un attribut peut être modifié en cache par un processus, la valeur est stockée dans un registre Si un autre processus souhaite accéder à l'attribut, il reçoit la valeur de la mémoire centrale Certains attributs doivent donc être raffraichis en permanence Utilisation du mot clé volatile
Volatilité int volatile valeur = 0; Les attributs volatiles : sont chargées de la mémoire centrale avant chaque utilisation. sont stockées en mémoire centrale après chaque accès/ écriture. A utiliser si une variable est partagée, hors d'un bloc synchronisé
Attente de processus La méthode join() permet de mettre en attente de fin d 'un processus les processus suivants Thread process = new Thread(); Thread processsuivant= new Thread(); process.start(); process.join(); //processsuivant ne démarre que lorque process n'est plus en vie processsuivant.start();
Groupe de processus Regroupement des Threads par un ThreadGroup. Appliquer à un ensemble de processus une même opération Un processus ne peut changer de groupe Le groupe par défaut est le main System.out.println("groupe de threads par défaut : " + Thread.currentThread().getThreadGroup());.. java.lang.threadgroup[name=main,maxpri=10]
Groupe de processus Pour retrouver le nombre de processus actifs d'un groupe :.activecount() Définition d'un groupe : ThreadGroup mongroupe = new ThreadGroup("Mon groupe"); Création d'un processus dans un groupe : processmarchand = new Thread(monGroupe, producteur, "processus marchand");
Priorité des processus Gérer l'ordre d'exécution Priorités dépendantes du S.E. (ok pour Unix, et Mac) Le processus de priorité la plus grande s'exécute en premier Priorité comprise entre 1 (MIN_PRIORITY) et 10 (MAX_PRIORITY), la normale étant 5 (NORM_PRIORITY)
Priorité des processus Priorité des processus exécutés par la JVM : 4 Screen Updater 5 Thread utilisateur (défaut) 5 main 5 SunToolkit.PostEventQueue-0 5 Signal dispatcher 5 AWT-Windows 6 AWT-EventQueue-0 8 Finalizer 10 Reference Handler
Priorité des processus Affectation d'une priorité (entre 1 et 10) : monprocessus.setpriority(9); Récupération de l'ordre de priorité : int prio = monprocessus.getpriority();