Vous êtes sur la page 1sur 36

COURS JAVA

AVANCÉ –
PARTIE 6
Mme Nouira Sana
COURS JAVA AVANCÉ
PROGRAMMATION CONCURRENTE
LES THREADS
Mme Nouira Sana
DÉFINITION
 La machine virtuelle java (JVM) permet d'exécuter plusieurs
traitements en parallèle (en pratique, ils s'exécutent par
“tranche” et en alternance sur le processeur).
 Ces traitements sont gérés par des threads of control (fil de
contrôle), ou plus simplement threads.

 Les threads Java sont des processus légers : ils partagent un


ensemble de codes, données et ressources au sein d'un
processus lourd qui les héberge(ce processus est la JVM).
o A la différence des processus, Le thread, lui, partage les
données avec les autres threads.
 Les threads existe dans un processus (au moins un thread
par processus).Les threads nécessitent moins de ressources :
ils partagent les ressources du processus père : mémoire,
fichiers ouverts, etc.
COMMENT DÉFINIR UN THREAD EN JAVA
 Il existe deux méthodes dans Java pour créer une
classe qui représente un thread :
 en héritant de la classe Thread.
 en implémentant l'interface Fonctionnelle Runnable
 Méthode run()

 Son implémentation haut niveau en Java commence en


Java 5. Des avancements ont été proposé en Java 9
dans le package: java.util.concurrent
INTÉRÊTS D'UNE APPLICATION MULTI-THREADS :
 Même sur une machine monoprocesseur, est que
l’ordinateur donne l’impression d’effectuer plusieurs tache
en meme temps
 Le fait de pouvoir ouvrir et de passé d’une à l’autre sans attendre
 gérer l'exécution des traitements longs sans bloquer les
autres
 exemples : - calculs, affichage et gestion des interactions dans une
interface graphique
 lancer des programmes démons qui tournent en tâche de
fond
 lancer plusieurs exécutions du même code simultanément
sur différentes données
 exemples : - répondre à plusieurs requêtes en même temps dans
un serveur
 traiter deux flux sonores en parallèle
 simuler le parallèlisme nécessaire à certaines classes
d'applications sans exploiter la structure multi-processeurs
des ordinateurs modernes
Exemple : Afficher d’une façon parallèle des messages (chacun un
certains nbr de fois). Vous fixez le délai entre deux affichages successifs
du même message.

Avec Thread Avec Runnable

class Message extends Thread { class Ecrit implements Runnable{


private String texte ; private String texte ;
private int nb ; private int nb ;
private long attente ; private long attente ;

public Message (String texte, int nb, public Ecrit (String texte, int nb, long
long attente) { attente) {
this.texte = texte ; this.nb = nb ; this.texte = texte ; this.nb = nb ;
this.attente = attente;} this.attente = attente;}

public void run(){ public void run(){


try{ try{
for (int i=0;i<nb;i++) { for (int i=0;i<nb;i++) {
System.out.print(texte); System.out.print(texte);
sleep(attente);}} Thread.sleep(attente);}}
catch (InterruptedException e){} catch (InterruptedException e){} 6
// imposée par sleep // imposée par sleep
}} }}
Dans une classe principale (aussi un Thread main)
public static void main (String public static void main (String
args[]){ args[]){
Message m1 = new Message("Bonjour",10,5) ; Ecrit e1 = new Ecrit(“Hello",10,5) ;
Message m2 = new Message("Bonsoir",12,10) ; Ecrit e2 = new Ecrit(“Go!!!",12,10) ;
Message m3 = new Message("\n",5,15) ;
Ecrit e3 = new Ecrit("\n",5,15) ;

m1.start(); Thread t1 = new Thread(e1);


m2.start(); t1.start();
m3.start();} Thread t2 = new Thread(e2);
t2.start();
Thread t3 = new Thread(e3);
t3.start();

 voir exécution des projets « exempleEcritThread »


et «exempleEcritRunnable » sous NetBeans IDE
 L’appel direct de « run » oblige de terminer le premier Thread puis
le deuxième...
L’appel de « start » se fait une seule fois sinon une exception sera
générée : IllegalThreadStateException
Avec la deuxième méthode on peut utiliser start() directement avec
un objet Ecrit. Seulement, il fallait redéfinir au niveau de la classe
7
Ecrit par :
public void start(){Thread t=new Thread(this) ; t.start() ;}
LA CLASSE JANV.LANG.THREAD
 La classe Thread encapsule les opérations systèmes
permettant de créer, exécuter et synchroniser les
threads.
 Schéma
QUELQUES : METHODES
 void start() ; // Permet de demander au Thread
concerné de démarrer (asynchrone : cette fonction redonne
immédiatement la main pour la suite des instructions).
 void stop() ; // Permet de demander au Thread
concerné de s'arrêter.
 void join() ; // On attend que le Thread concerné se
soit terminé.
 void run() ; // Méthode à redéfinir pour décrire les
actions d'un Thread (c'est le contenu de cette méthode qui
représente les actions du thread et qui sera donc exécuté
après que le Thread aura été démarré par la méthode
start().).
 getState() renvoie l'état du thread, isAlive() est vrai si le
thread est actif ou endormi
 …
EXEMPLE ACTION DE CUISINE
METHODES JOIN() DANS THREAD

• La méthode Join() permet le blocage du thread main jusqu’à la fin du


thread appelant

Class ActionDeCuisine extends Thread {


private String afaire ;
public ActionDeCuisine(String afaire) { this.afaire = afaire ; }
public void run() { // le contenu du Thread
for (int i=0 ; i<4 ; i++) System.out.println(afaire) ;
}
}
class Recette { //propagation de l’exception
public static void main(String[] args) throws InterruptedException{
ActionDeCuisine a1 = new ActionDeCuisine("Battre les oeufs");
ActionDeCuisine a2 = new ActionDeCuisine("Faire fondre le Chocolat");
a1.start() ;
a2.start() ;
a1.join() ;
a2.join() ;
System.out.println("Mélanger et Cuire") ;}}
CREATION - VIE ET MORT D'UN THREAD
 Création : constructeur de la classe THREAD
 Activation : la méthode start() appelle la méthode run() (start() n'est
pas bloquante dans le thread appelant).
 Destruction : elle intervient quand la méthode run() se termine.
 Il est possible d'interrompre le thread en utilisant la méthode interrupt()
qui crée une InterruptedException si le thread est en attente.

 Capture de l’exception
public class MonThread extends Thread{
public void run(){
try{
... // traitement, avec des périodes de sommeil et/ou d'attente
}
catch(InterruptedException e){
... // libération propre des ressources
}
// autre traitement
}
}
EXEMPLE
public class Perroquet extends Thread{
private String nom;
public Perroquet(String nom){
this.nom = nom;
}
public void run(){
try{
for(i=0;i<3;i++){
System.out.println(nom + " est content");
Thread.sleep(500);
}
}
catch(InterruptedException e){System.out.println(e.getMessage());}
}

public static void main(String[] a){


Thread t1 = new Perroquet("jacko");
Thread t2 = new Perroquet("jacki");
t1.start();
t2.start();
}
L'INTERFACE JAVA.LANG.RUNNABLE

 Il existe une deuxième mise en œuvre possible des


threads :
 On crée directement une instance de la classe Thread
en passant un objet implémentant l'interface
Runnable à son constructeur.
 L'objet Thread ainsi créé peut être utilisé
exactement comme dans la mise en œuvre précédente.
 La classe qui contient le code a exécuter en parallèle doit
simplement implémenter l'interface Runnable (Cette
interface ne contient que la méthode run() qui sera
appelée lors du démarrage du thread).
EXEMPLE ACTION DE CUISINE
Class ActionDeCuisine implements Runnable {
private String afaire ;
public ActionDeCuisine(String afaire) { this.afaire = afaire ; }

public void run() { // le contenu du Thread


for (int i=0 ; i<4 ; i++) System.out.println(afaire) ;
}
}
class Recette {
public static void main(String[] args) throws InterruptedException{
Thread a1 = new Thread(new ActionDeCuisine("Battre les œufs")) ;
Thread a2 = new Thread(new ActionDeCuisine("Faire fondre le Chocolat")) ;

a1.start() ;
a2.start() ;
a1.join() ;
a2.join() ;
System.out.println("Mélanger et Cuire") ;
}
}
EXEMPLE PERROQUET

public class Perroquet implements Runnable{


private String nom;
public Perroquet(String nom){
this.nom = nom;
}
public void run(){
try{
for(i=0;i<3;i++){
System.out.println(nom + " est content");
Thread.sleep(500);
}
}
catch(InterruptedException e){System.out.println(e.getMessage());}
}
}
Thread t1 = new Thread(new Perroquet("jacko"));
Thread t2 = new Thread(new Perroquet("jacki"));
 Remarque : Il est parfois utile dans la méthode
run() d'un objet Runnable de pouvoir récupérer
une référence sur le thread dans lequel cette
méthode est exécutée. Pour cela il est possible
d'utiliser la méthode :
static Thread currentThread() ;

public void run() {



Thread current = Thread.currentThread() ;

}
PRIORITÉ ET PARTAGE DU TEMPS
 Thread.sleep(long millis) est une méthode de classe
qui met le thread courant en sommeil un certain
nombre de millisecondes. Appelée dans la méthode
run() du thread, elle le met en sommeil.
 Si cette méthode est appelée dans du code synchronisé
(synchronized) le thread ne perd pas le moniteur (voir plus loin).
 La méthode de classe Thread.yield() suspend
l'exécution du thread qui est en train de s'exécuter
pour donner la main à un autre.
 le thread en train de s'exécuter n'est pas mis en sommeil,
il est toujours dans la liste des threads actifs
 - l'appel de cette méthode peut redonner la main au thread
courant!

 La méthode setDaemon(boolean on) appelée avant


start() permet de faire du thread un démon, processus
de basse priorité qui tourne en tâche de fond.
 exemple : le garbage collector
EXEMPLE
public class TestSleep extends Thread{
public TestSleep(String name){super(name);}
public void run(){
try{
System.out.println(this.getName()+" a ete lance");
Thread.sleep(100);
System.out.println(this.getName()+" est termine");
}
catch(InterruptedException e){
System.out.println(this.getName()+" a ete interrompu");
}
}
public static void main(String arg[]){
try{
TestSleep mt = new TestSleep("Toto");
mt.start();
Thread.sleep(100);
mt.interrupt();
}
catch(InterruptedException ie){}
}
}
 De temps en temps, Toto terminera normalement, d'autres fois il
sera interrompu.
EXEMPLE COURSE
 Simuler une course de 1000 m entre 2 personnes
appelées Jean et Paul.
 Comme il est nécessaire de de les faire courir en même
temps, chacun sera pris en charge par un thread.
Le programme crée une classe générale Coureur qui
hérite de Thread.
 La classe de test Course n'est chargée que de créer les
instances de Coureur, et les lancer dans la course avec
start().
class Coureur extends Thread { // constructeur de la classe, utilise le
constructeur de Thread, pour le nommer
public Coureur (String str) {
super(str);
}
// on redéfénit run() pour décrire le comportement d'un coureur quelconque
public void run() {
// la valeur i est incrémentée lors du passage à la ième centaine de mètres
for (int i =1; i<=10; i++) {
System.out.println(i*100 + " m par " + getName());
try {
/* pause aléatoire de 1 seconde maximum qui simule le temps mis par chaque coureur pour
franchir 100 m */
sleep((int)(Math.random() * 1000));
}
catch (InterruptedException e) {}
}
System.out.println( getName()+ " est arrive ! ");
}
}

class Course {
public static void main (String[] args) {
// Il s'agit d'une classe de coureurs
System.out.println("Passage aux : ");
Coureur j = new Coureur ("Jean");
Coureur p = new Coureur ("Paul");
j.start();
p.start();
}
}
PRIORITÉ ET ETAT
 Trois champs statiques dans une classe Thread qu’on peut accéder:
MIN_PRIORITY (correspond à 1) à MAX_PRIORITY (correspond à
10), NORM_PRIORITY (priorité par défaut qui correspond à 5)

 Une valeur de priorité est affectée à chaque thread et détermine sa


priorité d'accès au temps CPU.

 La priorité d’un thread est un nombre qui lui est assigné et qui
permet d’identifier la part de ressource de traitement auquel il aura
droit dans un système découpant le temps ou quel thread sera en
exécution dans un système

 Les méthodes :
 La priorité est modifiée par setPriority(int i)
 et accédée par int getPriority().
 setDaemon(boolean) appelée avant start() permet de faire du thread un démon,
processus de basse priorité qui tourne en tâche de fond.
 Méthode getState() retourne l’état (State) du Thread.
 Exemples: state = RUNNABLE, state = TERMINATED, state =BLOCKED, State =
WAITING, state = New...

 voir projet « exempleState » sous NetBeans IDE


class CChrono extends Thread /* CChrono.java */
{
private long Temps;
private int Distance ;
private boolean Continuer;
public CChrono (int Vitesse, String Nom)
{
super (Nom);
setPriority (1);
Vitesse = Math.abs(Vitesse) ;
if (Math.abs(Vitesse) > 6) Vitesse = 6;
Temps = 0;
Continuer = true;
}

public void run ()


{
long T0 = System.currentTimeMillis();
while (Continuer && Distance < 20)
{
Temps = System.currentTimeMillis()- T0;
Distance = Temps*Vitesse ;
System.out.println (getName() + " : " + Distance);}

System.out.println (getName() + " est mort ****");


}
public void Arret ()
{Continuer = false;}}
public class CHorlogerie /* CHorlogerie.java */ {
Exécution :
MIN: 1
public static void main (String Arg []) throws MAX: 10
InterruptedException Tortue status : false
{ Poisson status : false
System.out.println ("MIN: " + Thread.MIN_PRIORITY); Chat status : false
System.out.println ("MAX: " + Thread.MAX_PRIORITY); Tigre status : false
CChrono T[] = new CChrono [5]; Sangoku status : false
T[0] = new CChrono (1, "Tortue"); Tortue status : true
Poisson status : true
T[1] = new CChrono (2, "Poisson");
Chat status : true
T[2] = new CChrono (3, "Chat"); Tigre status : true
T[3] = new CChrono (5, "Tigre"); Sangoku : 0
T[4] = new CChrono (6, "Sangoku"); Chat : 0
for (int x=0; x<5; x++) Tortue : 0
Tigre : 0
System.out.println (T[x].getName() + " status : "+T[x].isAlive()); Poisson : 0
for (int x=0; x<5; x++) Sangoku status : true
T[x].start(); Sangoku : 6
Tigre : 5
for (int x=0; x<5; x++) Sangoku : 12
System.out.println (T[x].getName() + " status : " + Chat : 6
T[x].isAlive()); Sangoku : 18
Tigre : 15
T[4].join(); Sangoku est mort
System.out.println ("----------------------"); *********
----------------------
for (int x=0; x<5; x++)
Poisson est mort *********
T[x].Arret(); Chat est mort *********
}} Tigre est mort *********
Tortue est mort *********
INCONVÉNIENTS D'UNE APPLICATION MULTI-
THREADS :

 il faut gérer les problèmes de synchonisation


entre threads
 une telle application est difficile à écrire et à
débogger
SYNCHRONISATION ENTRE THREADS
 Problème: gérer l’accès aux ressources communes
 La synchronisation est un élément essentiel dès lors que
vous utilisez plusieurs threads.
 Sans synchronisation, il est impossible de
développer une application robuste, quel que soit
l'entrelacement de l'exécution des threads.

27
PARTAGE DE DONNÉES
 Tous les threads partagent le même espace d'adressage et
peuvent donc entrer en conflit pour l'accès à une ressource.
 Supposons que deux threads distincts (T1 et T2) réalisent au
même moment l'instruction i = i+1.
 Cette instruction consiste en fait à au moins trois instructions de
base :
 1) Lecture du contenu de i dans un registre
 2) on ajoute 1 à ce registre
 3) on stocke le résultat dans i.

 Il pourrait donc se produire ceci (on suppose que i contient


initialement la valeur 2 )

 T1 lit i dans son registre (2)


 T1 additionne 1 à son registre (3)
 T2 lit i dans son registre (2)
 T1 écrit son registre dans i =>3
 T2 additionne 1 à son registre (3)
 T2 écrit son registre dans i =>3
 Au final i contient 3 au lieu de 4…
 Il est donc nécessaire de disposer du moyen de dire "Personne ne
peut faire cette instruction en même temps que moi, les
autres doivent attendre que j'ai fini".

 Dans Java, un moniteur peut être associé à chaque objet. Dés


qu'un thread détient ce moniteur, les autres voulant accéder à ce
moniteur sont mis en attente, et ne peuvent ni inspecter, ni
modifier cet objet.
 Les segments de code qui permettent l'accès aux mêmes données
depuis des threads séparés et qu'il faut protéger sont appelés
"sections critiques". Ces sections doivent être marquées par le
mot-clé synchronized pour être protégées par le moniteur de
l'objet correspondant.

 Il est possible de marquer des méthodes ou de simples blocs de


code comme étant synchronized. Dés qu'un thread utilise une
zone synchronized d'un certain objet, toutes les zones critiques
de l'objet sont verrouillées : tout autre thread voulant accéder à
une de ces zones sera mis en attente.

 Section critiques grâce aux blocs et méthodes « synchronized »


 moniteurs grâce aux blocs et méthodes «synchronized» et aux méthodes wait
/ notify / notifyall
 Sémaphores, mutex, barrières en Java 1.5
 Lock /unlock (pour imbrication de sections critiques).
EXEMPLE

private static class Calculator {


private double n;

public double calculer (int i) {


DoubleStream.generate(new
Random()::nextDouble).limit(10);
synchronized(this){
this.n=2.0*i;
Return Math.sqrt(this.n);
};
}
SYNCHRONISATION
 Lorsque l’on gère une application multi-threads
 il faut gérer les problèmes de synchonisation entre threads
 Les threads s'exécutant en parallèle sur des données
qui peuvent être communes, il faut gérer des conflits
d'accès et des problèmes de synchronisation entre
threads.
 La synchronisation peut consister à entremêler les
exécutions des threads de manière à ce qu'ils
n'accèdent à certaines données ou à certains morceaux
de code que chacun à leur tour alternativement.
 A un instant donné, une seule méthode synchronisée peut
accéder à un objet donné.
 En effet, l’environnement gère « un verrou » (ou une clé) unique
mettant l’accès à un objet qui possède au moins une méthode
synchronisée.
 Le verrou est attribuée à la méthode synchronisée appelée
pour l’objet et n’est restituée à qu’à sa sortie.
 Lors de la prise de ce verrou, aucune autre méthode
synchronisée ne peut le prendre
 On peut définir une instruction ou bloc d’instructions comme
synchronisée qui prend le verrou sur un objet : synchronized
(objet) { instructions}
 voir projet « exempleSynchro » sous NetBeans IDE

Problème de situation d’inter-blocage :


le thread t1 prend le verrou de l’objet o1 et attend le verrou de
l’objet o2 et vice versa pour un deuxième thread t2.
32
SYNCHRONISATION SUR TERMINAISON
 Une méthode plus simple est la synchronisation sur
terminaison : on veut qu'un morceau de code ne
s'exécute qu'après qu'un thread donné ait terminé.
 La méthode join() permet une telle synchronisation.
 exemple : on veut que jacko attende que jacki ait fini de parler
pour prendre la parole
SYNCHRONISATION SUR TERMINAISON
public class Perroquet extends Thread{
private String nom;
private Perroquet interlocuteur;
private boolean premier;
public Perroquet(String nom, boolean b){
this.nom = nom;
this.premier = b;
}
public void setInterlocuteur(Perroquet p){this.interlocuteur = p;}
public void run(){
try{ if(!premier){ public class Test{
public static void main(String arg[]){
System.out.println("je vous écoute");
Perroquet p1 = new Perroquet
interlocuteur.join(); ("jacko",false);
System.out.println("je vous réponds"); } Perroquet p2 = new Perroquet
("jacki",true);
for(int i=0;i<3;i++){
p1.setInterlocuteur(p2);
System.out.println(nom + " est content"); p2.setInterlocuteur(p1);
Thread.sleep(500); } p1.start();
p2.start();
}
} }
catch(InterruptedException
e){System.out.println(e.getMessage());}
} }
SECTION CRITIQUE
 Un morceau de code est une section critique s'il est nécessaire de
garantir qu'au plus un thread exécutera ce code à la fois
public class Pile{
private Object[] tab;private int sommet;
public Pile(int taille){
this.tab = new Object[taille];
this.sommet = -1;
}
public void empile(Object o) throws IndexOutOfBoundsException {
if(this.sommet>=this.tab.length-1) throw new IndexOutOfBoundsException("Pile pleine");
else{
this.tab[this.sommet+1] = o;
this.sommet ++;
}
}
public Object depile() throws IndexOutOfBoundsException {
if(this.sommet == -1) throw new IndexOutOfBoundsException("Pile vide");
else{
this.sommet --;
return this.tab[this.sommet+1];
}
}
}
public class Depileur extends Thread{
private Pile p;
public Depileur(Pile p){
super();
this.p = p;
}
public void run(){
try{
while(true){
System.out.println(p.depile());
Thread.sleep(10); }
}
catch(IndexOutOfBoundsException e){System.out.println(e.getMessage());}
catch(InterruptedException e){System.out.println(e.getMessage());}
}
public static void main(String ar[]){
Pile p = new Pile(10);
for(int i = 0;i<10;i++) p.empile(new Integer(i));
Depileur d1 = new Depileur(p);
Depileur d2 = new Depileur(p);
d1.start();
d2.start();
}
}

Vous aimerez peut-être aussi