Java
Transcription
Java
La programmation concurrente Java page : 1 Introduction Dans de nombreux contextes, il est intéressant d'effectuer plusieurs traitements distincts en même temps Application réseau En même temps ne signifie pas une simultanéité parfaite Il est possible de simuler l'exécution simultanée de deux traitements en leur permettant d'avoir accès, chacun à leur tour, au processeur On dit que les traitements sont CONCURRENTS z Puisqu'ils sont en concurrence permanent pour l'accès au processeur Java page : 2 Prog. Concurrente en Java Deux mécanismes permettent l'ordonnancement automatique des traitements La concurrence entre commandes du système correspondant à des processus La concurrence entre processus léger (threads) de la machine virtuelle, qui correspondent à différents traitements au sein d'un même processus Ces mécanismes créent de nouveaux problèmes liés à : La synchronisation, L'exclusion mutuelle La vivacité Java page : 3 Processus Vs Threads Java page : 4 Les processus Java page : 5 Les processus Processus : unité d'exécution gérée par le système En Java, il est possible d'accéder à ce mécanisme pour gérer la concurrence via les classes Runtime représentant un environnement d'exécution Process représentant un processus Les possibilités d'interaction sont limitées et cela interdit une gestion fine de la concurrence entre les processus En java : il est possible de lancer l'exécution de commandes quelconques en concurrence avec une application Java Java page : 6 Créer un processus A toute application Java est associé un objet de contrôle de son unité d'exécution : Objet de la classe Runtime Cet objet est obtenu par la méthode statique Runtime.getRuntime(); Cet objet permet d'obtenir des informations Concernant la mémoire z totalMemory() z freeMemory() D'appeler le ramasse-miettes z gc() De terminer l'application courante z exit( int val ) Java page : 7 Créer un processus L'objet Runtime permet de Créer et démarrer l'exécution d'une autre commande Démarrer l'exécution d'une autre commande Méthodes surchargées exec() de l'objet runtime z Le processus appelant : processus PERE z Le processus appelé : processus FILS Exemples: Runtime.getRuntime.exec("javac TP1.java"); Runtime.getRuntime.exec( new String[] { "javac", "TP2.java" } ); z Java L'objet retourné est de la classe Process et permet de contrôler le processus fils page : 8 Mort d'un processus La méthode destroy() Appelée sur un objet de la classe Process contrôlant un processus FILS, permet de demander sa terminaison Attente Un processus PERE peut également attendre la mort d'un processus fils grâce à la méthode waitFor() appliquée à l'objet processus du fils Cette méthode attend que le processus fils termine (l'attente est dite BLOQUANTE) Au moment de sa terminaison, un processus retourne un entier (le paramètre de la méthode exit() de son objet runtime) dont la valeur peut être obtenue par la méthode exitValue() 0 pour un processus forcé par destroy() Java page : 9 Communiquer par les flots L'objet processus permet également de récupérer des flots connectés à l'entrée standard à la sortie standard à la sortie erreur du processus fils La méthode getOutputStream() appelée sur un objet processus d'un fils permet au processus père de récupérer un flot en écriture connecté à l'entrée standard de ce fils Symétriquement, les méthodes getInputStream() et getErrorStream() retournent des flots en lecture permettant au père de lire les sorties standard et d'erreur du processus fils Java page : 10 Les Threads Java page : 11 Qu'est-ce qu'un thread ? un processus unité d'exécution qui a un but fonctionnel un espace mémoire des lignes de code décrivant son déroulement une routine procédure ou fonction tranche de temps du processus un thread se situe entre les deux sorte de processus à l'intérieur d'un processus appel d'un thread est comme un appel d'une méthode qui rendrait tout de suite la main, alors qu'elle n'est pas terminée Java Les Threads page : 12 Vous dites que j'en ai déjà utilisé ? le noyau Java s'appuie sur le multithreading un des threads exécute le programme un autre récupère la mémoire s'il fallait construire un robot imitant l'être humain, il serait multithread une personne peut z conduire une voiture z et parler en même temps Les Threads Java page : 13 intérêt des threads par rapport aux méthodes ? exécuter des tâches en parallèle exemple : programme gérant plusieurs voies de communication simultanément OU utiliser des threads z vous programmer comme si vous n'aviez qu'une seule voie à traiter à la fois z et vous lancer autant de threads concurrents qu'il y a de voies à traiter OU vous n'utilisez pas les threads z programmation par automate qui examine les voies les unes après les autres, en sauvegardant dans un tableau les états correspondants à toutes les autres voies (DIFFICILE) Java Les Threads page : 14 Intérêt des threads par rapport aux processus disposer d'un espace mémoire PARTAGE de code PARTAGE de ressources PARTAGEES Le lancement et l'exécution d'un thread est beaucoup plus économique que le lancement et l'exécution d'un processus Les Threads Java page : 15 Exemples de threads (1) class Fable { static Animal leLievre; static Animal laTortue; public static void main (String arg [ ] ) { leLievre = new Animal (5,"L"); laTortue = new Animal (1,"T"); laTortue.run () ; leLievre.run () ; } } Java Les Threads page : 16 Exemples de threads (2) class Animal { int maVitesse; String monNom; public Animal (int laVitesse, String leNom) { monNom = leNom ; maVitesse = laVitesse ; } public void sleep (int temps) { for (int i = 0 ; i <= temps ; i++) {;} public } void run () { for (int i = 0; i <10 ; i++ ) { System.out.println(monNom); sleep (1000/maVitesse); } System.out.println( "\n" + monNom + " est arrivé"); } } Les Threads Java page : 17 Exemples de threads (3) résultat TTTTTTTTTT T est arrivé LLLLLLLLL L est arrivé le lièvre n'a aucune chance d'arriver le premier le programme commence par la tortue et après l'arrivée de celle-ci s'occupe du lièvre partage de la ressource CPU dans le temps Tortue Java puis Les Threads Lièvre page : 18 Exemple de threads (4) transformation de la classe Animal en thread class Fable { static Animal leLievre; static Animal laTortue; public static void main (String arg [ ] ) throws InterruptedException { leLievre = new Animal (5,"L"); laTortue = new Animal (1,"T"); laTortue.start() ; // lancement du thread leLievre.start () ; laTortue.join();// attente de la fin des deux threads leLievre.join();// avant la sortie du programme } } Les Threads Java page : 19 Exemple de threads (5) class Animal extends Thread { int maVitesse; String monNom; public Animal (int laVitesse, String leNom) { monNom = leNom ; maVitesse = laVitesse ; } public void run () { for (int i = 0; i <10 ; i++ ) { System.out.println(monNom); try { sleep (1000/maVitesse); } catch (Exception e) {;}; } System.out.println( "\n" + monNom + " est arrivé"); } } Java Les Threads page : 20 Exemple de threads (6) L'exécution de ce programme donne alors le résultat suivant : TLLLLTLLLLL L est arrivé TTTTTTTT T est arrivé partage de la ressource CPU dans le temps Lièvre Java Tortue L'appel à la méthode start() de la classe Thread démarre le thread et entraîne l'appel à la méthode run() Les Threads page : 21 Je savais bien que l'héritage multiple me manquerait Si je veux créer un thread qui hérite d'une autre classe que Thread, par exemple Applet, je ne peux pas ??? Autre mécanisme de lancement des threads fournir au constructeur du thread un objet ( Animal par exemple) exemple : Thread t = new Thread ( new Animal()) ; t.start () ; quelle est la méthode de Animal appelée lors du start() ? z réponse : la méthode run() de Animal Java Les Threads page : 22 L'interface Runnable l'interface Runnable décrit les méthodes que doivent implémenter les objets pouvant être passés en paramètre des constructeurs de threads. une classe qui implémente Runnable a pour devoir de fournir une implémentation de la méthode run() pour droit d'être passée en paramètre à la création d'un thread exemple : class Animal implements Runnable { ..... public void run () { ... } } Les Threads Java page : 23 Techniques pour implémenter les threads Définir une classe qui hérite de la classe Thread Définir une classe qui implémente l'interface Runnable et dont une instance est passée en paramètre du constructeur du Thread Dans les deux cas, la classe doit définir la méthode run(), appelée lorsqu'on appelle la méthode start() du thread Conseils : hériter de Thread lorsque cela est possible sinon implémenter Runnable lorsque la classe hérite déjà d'une autre classe Java Les Threads page : 24 Etats d'un thread les threads sont des objets ayant un attribut prédéfini : leur état d'exécution cet attribut est privé comme les processus, les threads ont un état relatif à leur exécution ils peuvent être : créées actifs endormis morts Les Threads Java page : 25 Transitions entre états new() créé start() wait() sleep() suspend() actif endormi resume() stop() stop() mort Java Les Threads page : 26 Thread créé les threads sont d'abord créés, comme n'importe quel objet Java: laTortue = new Animal( 1 , "T" ); new () crée l'objet, alloue la mémoire nécessaire mais n'alloue pas d'autres ressources le thread est dans les limbes, mais il n'est pas encore vivant !!!! Les Threads Java page : 27 Thread Actif la méthode start() alloue les différentes ressources nécessaires à l'exécution du thread et lance la méthode run () le thread devient actif remarque il ne s'exécute pas en permanence mais dispose régulièrement d'une tranche de temps les tranches de temps sont brèves ce qui donnent à l'utilisateur l'illusion d'une exécution simultanée un thread actif est un thread auquel le processeur alloue régulièrement une tranche de temps Java Les Threads page : 28 Thread endormi un thread endormi ne consomme pas de ressources machine, mais une fois réveillé il redeviendra actif plusieurs cas peuvent conduire un thread à l'état endormi: méthode : sleep( durée) ours.sleep( hiver ) réveil au bout du temps durée méthode : suspend() gaston.suspend() reveil : méthode resume() gaston.resume() attente d'une ressource système d'E/S attente sur une condition Les Threads Java page : 29 Thread mort La mort intervient de deux façons : fin de la méthode start() z exemple: leLievre et laTortue z le thread a terminé ce qu'il avait à faire appel de la méthode stop () z intervention extérieure au thread Java Les Threads page : 30 Priorité Les Threads Java page : 31 Priorité définition da la priorité un processus peut être découpé en threads( sousprocessus) parmi ces threads ,certains sont z plus importants z ou plus consommateurs de ressources le noyau exécutable de Java implémente la notion de priorité entre threads ex: le ramasse-miettes a une priorité faible Java la création d'un thread précise sa priorité absolue Les Threads page : 32 Partage du temps entre threads la partage de temps entre 2 threads de priorités différentes : tant que le thread de plus haute priorité n'est pas endormi, il possède les ressources s'il s'endort, il donne à l'autre la possibilité d'utiliser la ressource CPU s'il se réveille il reprend la ressource le partage de temps entre threads de même priorité dépend de la machine !!! Les Threads Java page : 33 Les tranches de temps méthode de répartition de la ressource CPU - par tranche de temps les threads de la plus haute priorité ont chacun leur tour, quelques millisecondes de temps CPU si ce n'est pas le cas : un thread ayant accaparé la ressource ne la rendra pas avant de s'endormir !!!! solution : méthode yield() cette méthode permet à un thread de partager la ressource CPU avec les threads de même priorité Java Les Threads page : 34 Exemple de gestion de priorité (1) import java.io.*; class Priorite { static Animal leLievre ; static Animal laTortue; public static void main (String argv [ ] ) throw InterruptedException { System.out.print ln("prio. max :" + thread.MAX_PRIORITY); System.out.print ln("prio. min :" + thread.MIN_PRIORITY); leLievre = new Animal(6, "L"); laTortue = new Animal(2,"T"); leLievre.start(); laTortue.start(); leLievre.join(); laTortue.join(); System.out.println( ); } } Java Les Threads page : 35 Verrouillage des ressources Saint Kronisme Java Les Threads page : 36 Verrouillage de ressources (1) conflit de ressources deux threads tentent de mettre à jour le même emplacement de mémoire en simultanée verrou d'un objet : mot clé "synchronized" une méthode verrouillée ne peut être utilisée que par un seul thread à la fois exemple : class Compte { public synchronized void retrait(double montant) { solde = solde - montant; } public synchronized void credit(double montant) { solde = solde + montant; } } Les Threads Java page : 37 Verrouillage de ressources (2) un objet peut aussi être verrouillé dans une section de code exemple : class Test { public void fonction(Compte ba) { synchronized(ba) { ... je suis seul à accéder } } Java Les Threads à l'objet ba page : 38 Conclusion sur la Synchronisation synchronized nomFonction ou synchronized(nomObjet) empêchent : deux threads d'exécuter la même portion de code en même temps sur le même objet MAIS deux threads peuvent exécuter z deux méthodes différentes sur le même objet z ou la même méthode sur deux objets différents Java Les Threads page : 39 Rendez-vous Java Les Threads page : 40 Rendez-vous (1) les threads peuvent organiser des rendez-vous entre-eux, en partageant une attente sur un objet commun Un ou plusieurs threads attendent qu'ils se passe quelque chose sur un objet et un autre thread met fin à leur attente la classe Object propose trois méthodes : notify() et notifyAll() wait() wait() appelée dans un thread, signifie que le thread va attendre qu'un autre thread appelle notify() dans une autre méthode du même objet Les Threads Java page : 41 Rendez-vous (2) Java exemple : import java.io.*; public class ecole { public static void main( String argv [ ]) { try { Notes sex = new Note () ; Professeur etienne = new Professeur(sex); Eleve jean = new Eleve (sex, " jean"); jean.start() ; etienne.start(); jean.join(); etienne.join(); } catch (Exception e){ System.out.println(e.toString());} }} Les Threads page : 42 Rendez-vous (3) class Notes { public synchronized void seFontAttendre() { try { wait () ; } catch( InterruptedException e) { System.out.println (e.toString () ) ; } } public synchronized void etreDiffusees () { notifyAll() ; } } class Eleves extends Thread { Notes notes; String monNom; Java Les Threads page : 43 Rendez-vous (4) public Eleve (Notes lesNotes, String leNom) { notes = lesNotes; monNom = leNom; } public void run () { System.out.println (monNom + " attend sa note) ; notes.seFontAttendre() ; System.out.println( monNom +" a eu sa note"); } } class Professeur extends Thread { Notes notes; public Professeur(Notes lesNotes) { notes= lesNotes; } public synchronized void run () { System.out.println("Je vais rendre les notes"); notes.etreDiffusees () ; } } Java Les Threads page : 44 Exercice 1 Vous devez ordonner les exécutions de 5 processus légers. Chaque processus léger possède un numéro différent, entre 0 et 4 Le processus N ne peut s'exécuter que si un compteur externe est égal à son numéro Une fois son exécution terminée, le processus léger incrémente le compteur, modulo le nombre de processus légers et réveille tous les processus légers afin que le processus léger du numéro suivant puisse prendre la main Java page : 45 Exercice 2 Simulation du départ d'une fusée Le but de cette simulation : simuler le lancement entre l'instant où le chef du centre déclenche le compte à rebours et l'instant où il allume les réacteurs de la fusée La simulation utilise trois objets : z Un compte à rebours z Deux équipes (l'équipe météo et l'équipe sécurité) : chaque équipe est représentée par un objet qui est une instance de la classe Equipe Après le déclenchement du compte à rebours par le chef du centre les deux équipes peuvent prendre la décision de retarder le décollage de la fusée. Les comportements des deux équipes est régi par deux règles : z Les deux équipes exécutent des tâches indépendamment les unes des autres z Les deux équipes partagent une ressource commune : le compte à rebours Java page : 46 Groupe de threads Java page : 47 Groupes de threads (1) chaque thread appartient à un groupe de threads un groupe est défini par la class ThreadGroup exemple: ThreadGroup lesAnimaux = new ThreadGroup("Animaux"); Thread Lievre = new Thread (lesAnimaux,"Lievre"); Thread Lapin = new Thread(lesAnimaux,"Lapin"); les groupes de threads forment une hiérarchie à sa création un groupe est fils du groupe courant Java Groupe de Threads page : 48 Groupe de threads (2) intérêts des groupes de threads les méthodes z resume() z stop () z suspend () agissent sur les threads du groupe courant et de ses descendants les méthodes z getMaxPriority() z getName() z getParent() permettent d'obtenir les valeurs des attributs décrivants le groupe de threads permet d'effectuer une action sur un ens. de threads Java Groupe de Threads page : 49 Application : programmation réseau Java page : 50 Les sockets Les sockets, mécanismes de communication entre processus, fonctionnent, que les processus soient sur la même machine ou non Les processus échangent des données à travers un réseau, local ou distant. Java page : 51 Le principe des sockets Le principe des sockets est du type client-serveur: un processus serveur déclare accepter les connexions des clients, et lorsqu'un client se présente, un socket est créé pour établir le lien entre les deux. Le mécanisme des sockets n'est pas propre à Java, mais Java en fournit une implémentation portable, réalisée grâce aux classes ServerSocket et Socket. Java page : 52 ServerSocket Les Constructeurs de la classe ServerSocket : ServerSocket(int port) ServerSocket(int port, int backlog) ServerSocket(int port, int backlog, InetAddress bindAddr) Le point commun entre ces trois constructeurs est le numéro de port sur lequel l'instance va "écouter" les requêtes des clients. Chacun de ces trois constructeurs est susceptible de lever une exception de type IOException lors de leur appel. Exemple : try { ServerSocket server = new ServerSocket(1515); } catch(IOException ioe) {} Java page : 53 ServerSocket Le deuxième et le troisième constructeur ajoutent un second paramètre, désigné ici sous le nom de "backlog". lorsqu'un socket serveur reçoit une demande de connexion, il est possible qu'il ne puisse la résoudre instantanément. C'est pourquoi le serveur possède une file d'attente dans laquelle s'alignent les demandes de connexions. Dans le cas d'un serveur destiné à héberger un site internet, des centaines de demandes peuvent affluer instantanément. Le paramètre backlog est destiné à limiter la taille de la file d'attente. Prenons l'exemple suivant: try { ServerSocket server = new ServerSocket(1515,1); } catch (IOException ioe) {} Java page : 54 Méthode accept() La classe ServerSocket contient neuf méthodes. La méthode la plus utile est accept() public Socket accept(); Java Cette méthode permet d'obtenir des instances de la classe Socket correspondant aux divers clients connectés à votre serveur. page : 55 Code du client (1) Java Le programme client se connecte à un serveur en indiquant l'adresse de la machine et le port qu'il demande. Notre exemple met en communication deux processus sur la même machine, donc l'adresse de la machine est l'adresse locale et le port est le numéro choisi par le serveur (5000). page : 56 Le schéma de fonctionnement Serveur Client s = ServerSocket(port#) connexion c = s.accept() InputStream lecture s = Socket(@IP,port#) lecture InputStream écriture OutputStream pipe réseau OutputStream écriture c.close() s.close() s.close() Java page : 57 Exemple de Serveur Echo import java.io.*; import java.net.*; public class Serveur { private ServerSocket soquette; private Socket client; int nbClients = 0; public static void main (String [] args) { new Serveur(); } Java page : 58 Exemple de Serveur (2) public Serveur() { try { soquette = new ServerSocket(5000); while (nbClients < 2) { nbClients ++; client =soquette.accept(); PrintStream p = new PrintStream(client.getOutputStream()); StringBuffer s = new StringBuffer("Bonjour client"); p.println(s.toString()); client.close(); } } catch(Exception e) { System.out.println(e.getMessage()); } }; } Java page : 59 Code du client (3) import java.io.*; import java.net.*; public class Client { public static void main (String [ ] arg) { new Client(); } InetAddress adresse; Socket soquette; public Client () { try { adresse = InetAddress.getLocalHost (); soquette = new Socket(adresse,5000); BufferedReader in = new BufferedReader( new InputStreamReader( soquette.getInputStream())); System.out.println(in.readLine()); soquette.close(); } catch (Exception e) { System.out.println(e.getMessage()); }; } Java page : 60 Servir plusieurs clients Chaque fois que le serveur établi une nouvelle connexion, c'est à dire qu'il a accepté une requête, un nouveau Thread est créé qui sera chargé de la gestion de la connexion entre le serveur et le client La boucle principale : while(true) { Socket cl = s.accept(); Thread t = new ThreadEchoHandler( cl ); t.start(); } Java page : 61 La classe ThreadEchoHandler class ThreadEchoHandler extends Thread { private Socket cl; public ThreadEchoHandler(Socket cl){ this.cl = cl; } public void run() { try { BufferedReader in = new BufferedReader( new InputStreamReader(cl.getInputStream())); PrintWriter out = new PrintWriter ( cl.getOutPutStream(), true); String line; while((line=in.readLine() != null) { traitement de la ligne } cl.close(); } catch(Exception ex) { System.out.println(e.getMessage(); } } } Java page : 62