les threads
TRANSCRIPT
1
LES THREADS
Macodou Diouf
(Formateur en Technologie Objet)
JAVA AVANCE
2
PLAN INTRODUCTION EXEMPLE INTERFACE RUNNABLE INTERRUPTION D’UN THREAD
1. Démarche usuelle d’interruption par un autre thread2. Threads démos et arrêt brutal
COORDINATION DE THREADS1. Méthodes synchronisées2. Exemple3. Notion de verrou4. L’instruction synchronized5. Interblocage6. Attente et notification
ETATS D’UN THREAD PRIORITES DES THREADS
3
INTRODUCTION
Machines actuels qu’elles soient monoprocesseur ou multiprocesseur, permettent d’exécuter plus ou moins simultanément plusieurs tâches ou processus.
Sur les machines monoprocesseur, la simultanéité n’est qu’une illusion.
Grace aux threads la multiprogrammation est possible en Java. Un programme Java est formé de plusieurs threads qui
communiquent entre eux et partagent des données. Nous allons voir les deux façons de créer des threads:
Dériver de la classe Thread Implémenter l’interface Runnable
Puis nous verrons comment interrompre un thread depuis un autre thread. Nous apprendrons ensuite à coordonner les actions de plusieurs threads. Ensuite nous passerons en revue les différents états d’un thread. Enfin nous verrons comment agir sur la priorité d’un thread.
4
EXEMPLE
Un programme qui va lancer trois threads simple, chacun d’eux se contentant d’afficher un certain nombre de fois un texte donné, à savoir : 10 fois « Bonjour » pour le premier thread 12 fois « Bonsoir » pour le deuxième thread 5 fois un changement de ligne pour le troisième thread
Un thread objet d’une classe disposant d’une méthode run qui sera exécuté lorsque le thread sera démarré
class Ecrit extends Thread{public Ecrit(String texte, int nb)
{this.texte = texte ;this.nb = nb ;}
public void run (){for ( int i = 0 ; i < nb ; i++ )System.out.println(texte) ;}
}
5
EXEMPLE
La création des objets threads pourra se faire de la manière suivante :
Ensuite on appelle la méthode start de la classe Thread pour le lancement de l’exécution du thread.
Cette instruction réalise toutes les opération nécessaire pour qu’un nouveau thread puisse être prise en compte à la fois par le JVM et le SE, puis lance l’exécution de la méthode run.
Pour permettre à nos threads de s’exécutent en apparente simultanéité, nous ferons appel à la méthode sleep(t) où t correspond au nombre de millisecondes que le thread sera arrêté (mis en sommeil).
La méthode sleep est susceptible de générer une exception de type InterruptedException.
Ecrit e1 = new Ecrit(« Bonjour », 10);Ecrit e2 = new Ecrit(« Bonsoir », 12);Ecrit e3 = new Ecrit(« \n », 5);
e1.start(); // lance l’exécution du thread e1
6
EXEMPLE
Nous allons prévoir un temps de sommeil pour chaque thread.
class Ecrit extends Thread{private String texte; private int nb; private long attente;
public Ecrit(String texte, int nb, long attente){this.texte = texte ;this.nb = nb ;this.attente = attente ;}
public void run (){
try {for ( int i = 0 ; i < nb ; i++ ){
System.out.println(texte) ;sleep(attente) ;
}}catch (InterruptedException e){ }
}}
7
EXEMPLE
Remarques
Un programme Java comporte au moins un thread dit « thread principal » correspondant à la méthode main.
L’appel de la méthode sleep permet de donner la main à l’un des autres threads.
L’appel direct de la méthode run de nos objets thread, n’empêche pas notre programme de fonctionner mais on n’aurai plus affaire à trois threads différents.
La méthode start ne peut être appelé qu’une seule fois pour un objet thread donné. Sinon on obtient une exception IllegalThreadStateException.
La méthode sleep est une méthode static de la classe Thread qui met en sommeil le thread en cours d’exécution.
Si nous prévoyons pas d’appel de sleep dans notre méthode run, le programme fonctionnera encore mais son comportement dépendra de l’environnement.
8
INTERFACE RUNNABLE
Nous venons de voir une façon très simple de créer des threads à partir de la classe Thread.
Cette méthode présente une lacune puisque les objets ne peuvent pas dérivés d’autre chose que de Thread ( Java ne gère pas l’héritage multiple).
La deuxième manière de créer des threads est d’implémenter l’interface Runnable, laquelle comporte une seule méthode nommée run.
class Ecrit implements Runnable{
public Ecrit (String texte, int nb, long attente){
// mêmes instructions que précedemment}public void run() {
// même contenu que precedemment// en n’oubliant pas que la methode sleep est static
}}
9
INTERFACE RUNNABLE
Nous créerons des objets de type Ecrit, de la manière suivante :
L’objet e1 n’est plus un thread et ne peut plus être lancer par la méthode start.
Nous devons donc créer un objet de type Thread et le lancer avec la méthode start.
Exercice d’application :
Transformer l’exemple précèdent, en utilisant la deuxième méthode.
Ecrit e1 = new Ecrit(« Bonjour », 10, 5) ;
Thread t1 = new Thread(e1) ;t1.start() ;
10
INTERFACE RUNNABLE
Remarques Dans la pratique, la classe permettant de créer les objets destinés à
être transmis au constructeur de la classe Thread dérivera d’une autre classe (ce qui n’est pas encore le cas).
Nous allons doter de notre classe Ecrit une méthode nommée start jouant alors un double rôle : Création de l’objet de type Thread Lancement du Thread
class Ecrit implements Runnable{public Ecrit(String texte, int nb, long attente){
// constructeur inchangé}public void start (){
Thread t = new Thread (this) ;t.start() ;
}public void run (){
//methode inchangée}……
}
11
INTERRUPTION D’UN THREAD
Démarche usuelle d’interruption par un autre threadJusqu’ici les thread s’achevaient tout naturellement avec la fin de l’exécution de leur méthode run.
Dans le cas où la méthode run n’a pas de fin de programme « threads infinis », nous aurons besoin d’interrompre prématurément un thread.
La méthode interrupt de la classe Thread demande à l’environnement de positionner un indicateur signalant une demande d’arrêt du thread concerné.
La méthode static interrupted permet de connaitre l’état de l’indicateur.
Thread 1 Thread 2 nommé t
run (){t.interrupt () ;
….if( interrupted){….return ;}
}
12
INTERRUPTION D’UN THREAD
Threads démons et arrêt brutalJusqu’ici, nous avons considéré qu’un programme se termine lorsque le dernier thread est arrêté. En réalité, il existe deux catégories de threads:• Les threads utilisateurs• Les threads démons
Si dans un programme les seuls threads en cours d’exécution sont des démons, le programme est arrêté brutalement.
La méthode System.exit met fin à un programme et provoque, elle-aussi l’arrêt brutal de tous les threads en cours.
La méthode destroy, appliqué à un thread, en provoque également l’arrêt brutal.
Pour faire d’un thread un démon, on appelle la méthode setDaemon (true) avant d’appeler la méthode start.
13
COORDINATION DE THREADS
Contrairement aux processus, les threads appartiennent au même programme. Ils peuvent également partager les mêmes objets.
Il faudra donc éviter que deux threads puissent accéder presque en même temps au même objet.
Un thread devra donc attendre qu’un autre ait achevé un certain travail sur un objet avant de pouvoir lui-même poursuivre son exécution.
Le premier problème est réglé par l’emploi de méthode mutuellement exclusif : synchronized.
Le second problème sera réglé par le mécanisme d’attente et de notification mis en œuvre à l’aide des méthodes wait et notify.
14
COORDINATION DE THREADS
Méthodes synchroniséesVoici deux treads qui répètent indéfiniment les taches suivantes:
Thread 1 : incrémentation d’un nombre et calcul de son carré
Thread 2 : Affichage du nombre et de son carré
Si Thread 1 se trouve interrompu entre l’incrémentation et le calcul de carré, le second risque d’afficher le nouveau nombre et l’ancien carré.
Pour pallier cette difficulté, Java permet de déclarer des méthodes avec le mot-clé synchronized. A un instant donné, une seule méthode ainsi déclarée peut être appelée pour un objet donné.
15
COORDINATION DE THREADS
Exemple
class Nombre{private int n = 0, carre ;public synchronize void calcul (){n++;carre = n * n ;}public synchronize void affiche(){System.out.println(n + « a pour carre » + carre) ;}}
16
COORDINATION DE THREADS
Notion de verrouA un instant donné, une seule méthode synchronisée peut donc accéder à un objet donné.
Pour chaque objet dotant au moins d’une méthode synchronisée, l’environnement gère un verrou unique permettant l’accès à l’objet.
Le verrou est attribué à la méthode synchronisée appelé pour l’objet et il est restitué à la sortie de le méthode. Tant que le verrou n’est pas restitué, aucune autre méthode synchronisée ne peut le recevoir (les méthodes non synchronisées peuvent quant à elles, accéder à tout moment à l’objet).
17
COORDINATION DE THREADS
L’instruction synchronizedUne méthode synchronisée acquiert donc le verrou sur l’objet qui l’a appelée (implicitement) pour toute la durée de son exécution.
L’utilisation d’une méthode synchronisée comporte deux contraintes:• L’objet concerné (celui sur lequel elle acquiert le verrou) est
nécessairement celui qui l’a appelée.• L’objet est verrouillé pour toute la durée de l’exécution de la méthode.
L’instruction synchronized permet d’acquérir un verrou sur un objet quelconque pour une durée limitée à l’exécution d’un simple bloc.
synchronized (objet){// instruction}
18
COORDINATION DE THREADS
InterblocageL’utilisation des verrou sur des objets peut conduire à une situation de blocage « étreinte mortelle » qui peut se définir ainsi :• Le thread t1 possède un verrou de l’objet o1 et il attend le verrou de
l’objet o2• Le thread t2 possède un verrou de l’objet o2 et il attend le verrou de
l’objet o1
Ce problème est résolu grâce à l’ordonnancement des ressources qui consiste à numéroter les verrous dans un certain ordre et imposer aux threads de demander les verrous suivant cet ordre.
19
COORDINATION DE THREADS
Attente et notificationPour coordonner l’exécution de threads, Java offre une mécanisme basé sur l’objet et sur le méthodes synchronisées.
• Une méthode synchronisée peut appelé la méthode wait de l’objet dont elle possède le verrou, ce qui a pour effet:- de rendre le verrou à l’environnement qui pourra, le cas échéant,
l’attribuer à une autre méthode synchronisée- de mettre « en attente » le thread correspondant; plusieurs threads
peuvent être en attente sur un même objet; tant qu’un thread est en attente l’environnement ne lui donne pas la main.
• Une méthode synchronisée peut appeler la méthode notifyAll d’un objet pour prévenir tous les thrads en attente sur cet objet et leur donner la possibilité de s’exécuter.
20
ETATS D’UN THREAD
Nous allons voir les différents « états » dans lesquels peut se trouver un thread et sur les actions qui le font passer d’un état à un autre.
Au départ, on crée un objet Thread. L’appel de start rend le thread disponible pour l’exécution « prêt ». L’environnement peut faire passer un thread de l’état prêt à l’état « en cours d’exécution ».
Un thread en cours d’exécution peut subir différentes actions : Interrompu par l’environnement qui le ramène à l’état prêt Mis « en sommeil » par appel de la méthode sleep Mis dans une liste d’attente associé à un objet (appel de wait) Lancer une opération d’E/S et il se trouve alors bloqué tant que l’opération
d’E/S n’est pas terminée.
21
ETATS D’UN THREAD
start
désactivation activation
E/S terminé
Fin méthode run
sleep wait lancement E/S
Temps de sommeil notifyAll
écoulé
objet créé
prêt
en cours d’exécution
morten
sommeil
en attente de
notification
Bloqué en E/S
22
PRIORITES DES THREADS
Par défaut tous les threads ont la même priorité. Il es possible de modifier la priorité des threads à l’aide de la méthode setPriority à laquelle on fournit en argument une valeur entière comprise entre 1 et 10.
La priorité d’un thread est exploitée par l’environnement de la manière suivante : Il choisit parmi ceux qui sont l’état prêt celui de plus haut priorité; s’il y’a
plusieurs candidats le choix dépendra de l’environnement Si un thread plus prioritaire que le thread encours d’exécution dévient
prêt, on lui donne la main (l’autre thread passe à l’état prêt).
D’une manière générale, il est déconseillé d’agir sur les priorités des threads dans des programmes qui se veulent portables.