Bonjour à tous, dans cette séquence nous allons parler de la classe Thread
la classe Thread c'est l'objet de base permettant le parallélisme en Java
et vous trouverez partout dans les langages une notion équivalente par exemple le type Task en ADA ou dans des bibliothèques de.... programmation par exemple POSIX ou autres également un type qui est associé à ce mécanisme système et en fait cet objet de base dispose d'un certain nombre de primitives les plus importantes c'est start(), sleep(), yield(), wait(), notify() et notifyAll() que nous verrons plus tard alors au départ on a vu dans une séquence précédent le cycle de vie d'un flux d'exécution et donc on va regarder un petit peu ce qui se passe avec Thread Thread c'est une classe mais elle a un rôle un petit peu particulier c'est que quand je crée une occurrence de cette classe je vais créer en fait une nouvelle Thread donc un nouveau flux d'exécution évidemment il va falloir que je lui associe quelque chose qui va s'exécuter donc lorsque la Thread est créée et bien la méthode start() va la mettre dans l'état prêt donc il y a deux choses il y a le fait que l'objet soit créé et le fait que l'objet puisse envisager d'avoir le processeur donc une fois que vous avez fait un start() il est prêt ça veut pas dire qu'il s'exécute tout de suite ça peut attendre un petit moment en fonction des exécutions qui sont en cours alors à ce moment-là vous allez avoir le système classique où l'objet va être élu, avoir le processeur et sur fin de quantum vous avez cette boucle qu'on a déjà vue lorsque j'ai parlé des flux d'exécutions c'est assez classique et puis l'objet peut également se trouver suspendu et en particulier les méthodes comme sleep(), wait() peuvent provoquer la suspension de l'objet et à ce moment-là et bien il y a d'autres méthodes qui vont le remettre dans un état prêt donc typiquement notify(), notifyAll(), on les verra un peu plus tard il y a également yield() yield() en fait il passe pas par l'état suspendu mais il dit j'abandonne le processeur donc en fait je me remets dans la file d'attente bon si je suis tout seul évidemment je reprends derrière mais c'est une manière de provoquer ce qu'on appelle une commutation donc si j'étais élu et bien je provoque l'exécution potentielle d'une autre Thread et moi je me remets prêt je suis pas suspendu parce que suspendu je ne suis pas éligible pour avoir le processeur tandis que prêt je suis éligible pour le réavoir éventuellement immédiatement si c'est possible et puis au bout d'un certain temps la fin de l'exécution amène la Thread dans un état où elle est terminée alors bien évidemment que se passe-t-il pendant que on fait tout cela et bien on est en train d'exécuter du code et ce code se situe dans une méthode qui doit être implémentée qui s'appelle run(), alors vous pouvez enfin je dis elle doit être implanté mais vous pouvez ne pas l'implémenter mais cette méthode sera appelée par start() quoi qu'il arrive et l'implémentation par défaut de la classe Thread c'est je ne fais rien donc si je fais euh...je crée une Thread sans avoir implémenté la méthode run(), on va voir comment on peut implémenter la méthode run() et bien évidemment ça va pas marcher, ça va pas le faire prenons un exemple simple donc on va faire des petites tâches qui font pas grand-chose donc vous voyez que la manière d'associer un traitement à la méthode run() c'est bien évidemment la première manière parce qu'il y a plusieurs façons de manipuler les Thread on verra qu'il y a des façons qui sont plus normales que d'autres donc on voit cette première manière c'est d'étendre la classe Thread donc je vais créer ici une classe uneTache qui va étendre Thread donc je rajoute euh...un attribut dédié à cette classe et puis ici je vais avoir le constructeur qui va me permettre d'initialiser la Thread je n'utilise pas le constructeur de la super classe et puis la méthode run() qui se contente de dire bonjour au revoir en utilisant le nom qui a été spécificité ici et puis j'ai la classe principale de mon exemple qui contient la seule méthode main je vais créer trois occurrences de uneTache et puis je vais les lancers et regardons ce que cela donne alors ce qui se passe c'est que là je vous donne plusieurs exécutions ces exécutions elles diffèrent vous voyez que ici c'est t1 qui démarre qui s'arrête puis t3 dit "Bonjour", se suspens et dit "Au revoir" là derrière et le "Bonjour" et le "Au revoir" de t3 sont séparés par un "Bonjour" et "Au revoir" de t2 et puis la deuxième exécution c'est t1, t3, t2 comme ça et puis la 3ème exécution c'est t2, t1, t3 qui passe donc je vous ai parlé d'indéterminisme il se passe plein de choses bien évidemment ça se passe au niveau du langage assemblage donc euh...mais là ce qu'on voit c'est que rien qu'entre mes deux instructions je peux avoir une commutation et vous voyez que deux exécutions peuvent ne pas donner la même chose c'est parfaitement normal on est dans un contexte concurrent t1, t2 et t3 s'exécutent en concurrence les uns avec les autres on va construire notre premier chronogramme donc on a vu dans une séquence précédente ce qu'était un chronogramme là je vais le construire je vais pas le dessiner à la main parce que vous avez vu que j'étais assez... pathétique en termes de dessin à la main et donc en fait regardons ici le chronogramme qui a été préparé donc là je lance mon programme qui lance donc la classe principale et la classe principale je vais avoir une partie là le trait c'est ce qui s'exécute et les pointillés c'est ce qui ne s'exécute pas donc que fait mon programme principale il commence d'abord à faire des new donc il crée dans un premier temps la Thread t1 puis la Thread t2 puis la Thread t3 pour l'instant les Thread sont créées, l'exécution n'a pas eu lieu vous voyez que la création se passe en 2 temps création de l'objet et pour que l'exécution commence pour une Thread il faut lancer start() donc là j'ai un t1.start() donc vous le voyez apparaître ici c'est le démarrage puis un t2.start() puis un t3.start() alors je les ai pas représentés avec un aller-retour parce que là ici c'est vraiment un lancement d'exécution et je vous rappelle que je me mets dans l'hypothèse où j'ai un seul cœur donc en fait pendant que le main s'exécute donc ça c'est un flux qui existe toujours et bien les autres ne peuvent pas s'exécuter donc une fois que cela c'est terminé et bien la classe principale a terminé son exécution pour autant le programme n'est pas terminé puisque j'ai les Threads qui s'exécutent et le programme ne sera terminé que lorsque tous les flux d'exécution toutes les Threads se seront terminées donc bah que se passe-t-il là ici c'est t2 qui démarre donc je vois "Bonjour de t2", "Au revoir de t2" j'imagine qu'il n'y a pas de commutation ici maintenant c'est t1 qui prend la relève "Bonjour de t1", "Au revoir de t1" et ensuite c'est t3 qui s'exécute : "Bonjour de t3", "Au revoir de t3" et vous remarquez que à la fin de chaque séquence et bien ces Threads n'ont plus d'instructions à exécuter donc ici t2 se termine t2 t1 se termine t3 se termine, c'est simple lorsque les trois Threads sont terminées et bien là mon programme est réellement terminé et vous voyez que ce programme il a 4 flux d'exécution parce que il y a le main puis les trois flux d'exécution qui ont été créés par le main toujours considérer la classe principale, l'exécution de la classe principale comme un flux à part entière alors que se passe-t-il quand le programme est terminé et bien en fait le processus parce que tout ça ça s'exécute dans un processus souvenez-vous de ce qu'on a dit dans une séquence passée et bien est nettoyé par le système d'exploitation donc là je vais vous proposer une petite variation pour laquelle on va forcer des commutations de tâche donc c'est ce que j'ai fait en introduisant ici l'instruction "Thread.yield()" d'accord yield() ce qu'il va faire c'est qu'il va provoquer une commutation donc avant j'avais des exécutions qui pouvait avoir une commutation ou qui pouvait ne pas l'avoir là je l'aurai forcément et donc je vais avoir un peu plus de commutations donc ça va donner ceci du coup vous voyez que j'ai des "Bonjour" qui se séparent des "Au revoir" mais ils s'entrelacent également de façon variable comme c'était le cas auparavant et donc j'ai toujours cette propriété que deux exécutions ne vont pas forcément être identique maintenant regardons le chronogramme pour cette variation donc jusqu'à présent pas de souci je lance mon exemple et là et bien création de la Thread t1 plus de la Thread t2 puis de la Thread t3 puis j'exécute les trois start() et ensuite le programme principal se termine donc maintenant regardons l'exécution des programmes associés donc là je vais avoir un "Bonjour" de t1 et puis il va y avoir une commutation qui va se passer elle est quelque part, j'aurais pu mettre un petit éclair, elle est quelque part forcée par l'exécution du yield() qui est ici donc et puis c'est t1 là qui a démarré t3 qui prend la relève t2 qui prend la relève maintenant ben c'est l’ordonnanceur hein, c'est l'exécution moi j'ai lancé le programme et puis après j'ai reconstitué t3 reprend la main vous voyez que c'est pas parce que t1 s'est exécuté que c'est lui qui va reprendre la main on sait pas t3 reprend la main et là le programme se terminer puisqu'on arrive à la fin de la méthode run() là après c'est t1 qui reprend la main et enfin c'est t2 qui prend la main à chaque fois les Threads se terminent et ici comme auparavant le programme est terminé et le processus disparaît alors que dire en guise de conclusion ben vous voyez que c'est quand même extrêmement simple cette première manière de procéder je vais créer des fils d'exécution en dérivant la classe Thread et en surchargeant la méthode run() avec les traitements que moi je veux avoir et à chaque fois que je vais vouloir créer un nouveau traitement de ce type et bien je vais créer un objet de cette classe dérivée et puis je vais le lancer avec le start(), il ne suffit pas de le créer il faut aussi le lancer et puis voilà alors bien évidemment on n'a pas encore vu les communications ça c'est quelque chose qu'on verra plus tard mais on a déjà une forme de communication parce que si j'ai 2 classes différentes Thread1 et Thread2 par exemple client et serveur et bien le client pourra appeler une méthode de la classe serveur et à ce moment-là vous allez pouvoir déjà avoir un début de semblant de communication mais on va rentrer en détail plus tard sur les problèmes de communication qui sont évidemment beaucoup plus compliqués que cela voilà je vous remercie de votre attention, à bientôt