Vous êtes sur la page 1sur 16

Ecole Nationale des Sciences Appliquées

EL JADIDA

Volume 1

Programmation Réseau et Distribuées


JAVAEE

Dr. Mohamed LACHGAR


lachgar.m@gmail.com

2019 - 2020
2
Contents

1 Les Threads 5
1.1 Programmation concurrente . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.1 Processus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.2 Processus & Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.3 Avantage des processus légers par rapport au processus système . 6
1.2 Thread En Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.1 Création d’un Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.2 Les différents états d’un Thread . . . . . . . . . . . . . . . . . . . . 7
1.2.3 Connaître l’état d’un thread . . . . . . . . . . . . . . . . . . . . . . 8
1.2.4 La priorité d’un thread . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.5 La classe Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.6 La Synchronisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.7 Section critique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.8 Autre type de synchronisation: sémaphore . . . . . . . . . . . . . . 11
1.3 TP 1 : Thread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3
4 CONTENTS
Chapter 1

Les Threads

1.1 Programmation concurrente


La programmation est distribuée lorsque les processus ne partagent pas la mémoire.
RMI, Corba, EJB …

Sinon la programmation est dite parallèle.


Processuss, Thread ….

1.1.1 Processus
Chaque application (excel, word, etc.) exécutée sur un ordinateur lui est associée un
processus représentant l’activité de cette application.

À ce processus est associé un ensemble de ressources propres à lui comme l’espace mé-
moire, le temps CPU etc.

Ces ressources seront dédiées à l’exécution des instructions du programme associé à


l’application.

1.1.2 Processus & Thread


Dans la programmation concurrente, il existe principalement deux unites d’execution :
les processus et les threads.

• L’execution des processus et des threads est gerée par l’OS ;

• Un processus possede son propre environnement d’execution (ressources systemes);

• En general on a un processus par application (mais on peut faire cooperer des


processus (IPC : Inter Process Communication);

• La plupart des JVM tourne sur un seul processus;

• Un thread est souvent appele un processus leger (lightweight process);

• Un thread n’est pas un objet! C’est un sous-processus qui exécute une série
d’instructions d’un programme donné.

5
6 CHAPTER 1. LES THREADS

1.1.3 Avantage des processus légers par rapport au processus


système
• rapidité de lancement et d’exécution;

• partage des ressources système du processus englobant;

• simplicité d’utilisation.

1.2 Thread En Java


1.2.1 Création d’un Thread
La machine virtuelle java (JVM) permet d’exécuter plusieurs traitements en parallèle ,
pour créer un thread nous avons besoin de décrire le code qu’il doit exécuter et le démarrer
par la suite:

• Un thread doit obligatoirement Implémenter l’interface Runnable


public interface Runnable {
void run();
}

• La méthode run doit contenir le code à exécuter par le thread.

Créer un contrôleur de thread qui permet de démarrer un thread. Ceci est réalisable avec
l’une des deux approches suivantes:

Approche 1 : Classe qui dérive de java.lang.Thread

class ThreadTest extends Thread {


public void run() {
// le code à exécuté
}
public ThreadTest(…) {
// constructeur . avec/sans args.
}
}

ThreadTest MonThread = new ThreadTest(…);


MonThread.start();

Approche 2 : Classe qui implemente l’interface Runnable

class ThreadTest implements Runnable


{
public void run() {
// le code à executé.
1.2. THREAD EN JAVA 7

}
public ThreadTest(…) {
// construcateur. avec/sans args.
}
}

ThreadTest test = new ThreadTest(…);


Thread t1 = new Thread(test);
t1.start();

1.2.2 Les différents états d’un Thread


Au cours de sa vie, un thread passe par plusieurs états (de sa création à sa mort).
La figure donne le graphe des états d’un Thread. Quand une transition se révèle être
impossible une exception IllegalThreadStateException est déclenchée.

Figure 1.1: Les états d’un Thread

• Etat Nouveau
Le thread vient d’être créé et initialisé par exécution de la méthode privée init
appelée par le constructeur. Il ne dispose pas encore de ressources systèmes. C’est
la méthode native start qui les lui fournira. Le thread existe seulement comme
objet passif. Le thread sera ultérieurement activé (thread.start()), auquel cas il se
retrouve dans l’état Exécutable qui regroupe 2 sous-états : activable et actif.

• Etat Exécutable : Actif/Activable


Un thread dans l’état actif est en cours d’exécution par un processeur. Un thread
à l’état activable attend qu’un processeur se libère pour devenir actif. Le choix
du thread à rendre actif est fait par l’ordonnanceur. La machine virtuelle Java de
SUN ne met pas en œuvre de mécanismes de temps partagé permettant d’éviter, à
priorité égale, qu’un thread monopolise un processeur.

Le programmeur ne dispose que de deux moyens pour influer sur la politique


d’ordonnancement : modifier la priorité des threads ou utiliser la méthode yield
(le thread cède la main au profit d’un thread de même priorité).
8 CHAPTER 1. LES THREADS

• Etat Bloqué
Un thread bloqué ne peut pas être exécuté par le processeur. Il est en état d’attente.
Différents cas d’attente existent : attente qu’un certain laps de temps s’écoule, qu’
un signal en provenance d’un autre thread arrive, qu’une condition sur des vari-
ables partagées soit satisfaite, qu’une donnée devant être lue soit disponible, …

Exécutable –> Bloqué

sleep(x) // Suspension pendant x ms suspend( )


wait( ) // synchronisation  
Blocage en attente de fin d’entrée/sortie 
synchronized // exclusion mutuelle  
join //attente qu’un autre thread soit terminé

Bloqué –> Exécutable

Redevient exécutable au bout de x ms 


resume( ) notify( ), notifyAll( ) 
Déblocage à la fin de l’entrée/sortie 
Libération de l’exclusion mutuelle. 
Le thread attendu est terminé

• Etat Mort
Un thread meurt naturellement lorsque la fin de sa méthode run est atteinte. At-
tention, l’objet n’est plus actif, mais il existe toujours en tant qu’objet passif,
appelable via ses méthodes publiques. Il disparaîtra vraiment lorsque le ramasse-
miettes s’apercevra que l’objet n’est plus référencé.

La méthode join appliquée à un objet Thread permet d’attendre que ce thread passe


à l’état mort

1.2.3 Connaître l’état d’un thread


Il peut être utile de connaître l’état d’un thread (mort ou vivant). La méthode isAlive(
) renvoie true si le thread a été démarré (via start( )), mais n’est pas terminé.

Il faut en dernier lieu constater que le langage ne fournit pas le moyen de faire la dif-
férence entre l’état Nouveau et l’état Mort, pas plus qu’entre l’état Exécutable et l’
état Bloqué.

1.2.4 La priorité d’un thread


Les threads sont concurrents. Cela signifie que les threads à l’état exécutable sont en
compétition les uns avec les autres pour l’obtention du ou des processeurs. C’est le rôle
de l’ordonnanceur (scheduler) sous-jacent d’effectuer ce partage.
1.2. THREAD EN JAVA 9

Lors de sa création, un thread se voit affecter une priorité, celle du thread parent (celui
qui l’a créé). Le thread du programme principal possède la priorité 5 (NORM_PRIORITY).

La priorité doit appartenir à l’intervalle défini par 2 attributs constants de la classe Thread :


MIN_PRIORITY (qui vaut 1) et MAX_PRIORITY (qui vaut 10). Cette priorité est
modifiable par la méthode setPriority de la classe Thread. La priorité d’un thread peut
être obtenue par la méthode getPriority.

Règle
Un thread ne doit pas être à l’état actif alors qu’un thread de plus forte priorité est
activable.

1.2.5 La classe Thread


Nous allons présenter quelques méthodes de la classe Thread, l’ensemble de la classe est
décrit sur ce lien:

http://java.sun.com/j2se/1.4/docs/api/java/lang/Thread.html

Les constructeurs
crée un nouveau Thread dont le nom est
public Thread();
généré automatiquement (aléatoirement).
target est le nom de l’objet dont la méth-
Public Thread (Runnable target);
ode run est utilisée pour lancer le Thread.
public Thread(Runnable target, String name); on précise l’objet et le nom du Thread.
public Thread(String name); on précise le nom du Thread.

Les méthodes
Méthode Description
void destroy(); Permet de détruire un Thread.
String getName(); retourne le nom du Thread.
void interrupt(); interrompt le Thread.
static boolean interrupted(); teste si le Thread courrent a été interrompu.
attendre la mort du Thread, ou après un millis de
void join(...);
millisecondes, ou millisecondes plus nanosecondes.
void resume(); redémarrer le Thread.
La méthode contenant le code à exécuter par le
void run();
Thread.
void setPriority(int newPriority); changer la priorité du Thread.
mettre en veille le Thread pendant millis millisecondes
static void sleep(...);
ou millisecondes plus nanosecondes.
void start(); démarrer un Thread.

1.2.6 La Synchronisation
Soit une classe Compte Bancaire
10 CHAPTER 1. LES THREADS

public static void main(String[] args){


CompteBancaire cp=new CompteBancaire(100);
Retrait marie = new Retrait(cp);
Retrait epouse = new Retrait(cp);
marie.start();
epouse.start();
}

Problématique
• Il faut bloquer le retrait quand un détenteurs du compte commence un retrait
(exécution exclusif)
• Solution : placer le bout de code critique dans un bloque garder par le mot
synchronized
• Le mot clé synchronized informe la machine que ce bloque ne peut être instancier
ou exécuté que par un seul thread à la fois
• Conséquence direct: le Thread qui commence un bloque synchronized à l’exclusivité
de l’exécution

1.2.7 Section critique

Figure 1.2: Section critique

• Comment protéger une section critique ?


• En java, par un bloc « synchronized » : (bloc, méthode)
• Un seul thread accède au code synchronized à un moment donné.
• Protection efficace mais basique….
– wait() : met le thread en attente (sans perte de cycles CPU…) et relâche le
verrou
– notify() et notifyAll() : libèrent un ou tous les threads de l’état wait
1.2. THREAD EN JAVA 11

Solution pour le compte bancaire

public class CompteBancaire {


private float total;
public CompteBancaire(float init){
total=init;
}
synchronized public boolean retrait(float t){
if (t <= total) {
total -=t;
return true;
}
return false;
}
}

1.2.8 Autre type de synchronisation: sémaphore


• Exemple ping pong affiche des suite varié de mot ping et pong : (ping|pong)*

• On veut changer le code de tel façon que le langage généré soit: (ping pong)*

• L’idée est d’utilisé la notion de drapeau ou sémaphore :

Solution: Chaque thread doit garder son code par une variable qui lui block l’exécution
et permet a son concurrent de s’exécuter

Producteur/Consommateur

• Un drapeau indique qu’une valeur a été déposée

• Un drapeau indique qu’une valeur a été consommée

Figure 1.3: Producteur/Consommateur


12 CHAPTER 1. LES THREADS

1.3 TP 1 : Thread
Exercice 1 : Utiliser les threads
1. Compiler et exécuter le programme suivant :
Essayer de prévoir ce qui devrait s’afficher et comparer à l’exécution.
public class TwoThread extends Thread {
public void run() {
for ( int i = 0; i < 10; i++ ) {
System.out.println("New thread");
}
}

public static void main(String[] args) {


TwoThread tt = new TwoThread();
tt.start();

for ( int i = 0; i < 10; i++ ) {


System.out.println("Main thread");
}
}
}

A noter : le démarrage d’un thread se fait via la méthode start()


La méthode main() se trouve elle-même dans un thread (le thread principal)

2. Modifier le programme précédent pour que le Thread créé utilise la propriété name
de sa classe parente Thread pour faire l’affichage de son nom dans la boucle (faire
appel les methodes setName() et getName() de la classe Thread).

A noter : pour accéder au thread de main il faut utiliser la méthode Thread.currentThread().

3. Modifier le code précédent afin que les 2 threads se passent la main (utiliser la
méthode Thread.yield()) après chaque affichage : les 2 affichages doivent être al-
ternés.

4. Ajouter dans la classe la méthode suivante :


static void Wait(long milli) {
System.out.println("pause de "+milli+" ms") ;
try {
Thread.sleep(milli);
} catch (InterruptedException x) {
// ignorer
}
}

Remplacer dans le code précédent les appels à yield() par un appel à Wait(200).
Quelle est la différence ?

5. Créer à partir de l’exercice précédent une classe permettant de :


1.3. TP 1 : THREAD 13

• Créer un nombre n de threads, n passé en paramètre de main (utiliser String[]


args de main ). (max de n étant 10).
• Chaque thread créé possède un nom de la forme « thread x » avec x de 1 à n.
• Faire en sorte que le programme main () se bloque jusqu’à ce que les n threads
aient terminé leur exécution (utiliser la méthode join() de Thread).
A noter : par exemple, si main () veut attendre la fin du thread « tt », il appellera
: tt.join();

Exercice 2 : Compteurs (concurrence)


Modifier le programme de l’exercice précédent (partie 5) pour que chaque thread fasse
une pause (appel à Wait() ci-dessus) de durée aléatoire entre 0 et 1000 milliseconde avant
de rentrer dans la boucle. Chaque thread « compteur » aura le comportement suivant :
• Il compte de 1 à n et affiche chaque nombre (Toto affichera par exemple, ”Toto : 3”)
et il affiche un message du type ”*** Toto a fini de compter jusqu’à 10” quand il a
fini. Il marque une pause aléatoire entre chaque nombre (de 0 à 5000 millisecondes
par exemple).
• Le n est fixé à 10.
Ecrivez la classe compteur et testez-la en lançant plusieurs compteurs qui comptent
jusqu’à 10.

Exercice 3 : Runnable
Dans les exercices précédents, nous avons créé des threads en héritant de la classe Thread.
Une autre possibilité est d’implémenter l’interface Runnable. Cette interface contient une
méthode run() qui sera appelé lorsque le thread démarre (cad après l’appel de sa méthode
start()).

Consulter la documentation en ligne de l’interface Runnable pour plus d’informations.


1. Soit la classe suivante :
public class Alphabet {
public void affiche() {
for (char a = 'A'; a <= 'Z'; a++) {
System.out.print(a);
try {
Thread.sleep(10); // ms
} catch (InterruptedException e) {}
}
System.out.print("\n");
}

public static void main(String args[]) {


Alphabet A = new Alphabet();
A.affiche();
}
}
14 CHAPTER 1. LES THREADS

Modifier cette classe en utilisant l’interface Runnable pour que l’affichage se fasse
dans un thread séparé. On pourra donc écrire le programme de test suivant :
AlphabetThread A1 = new AlphabetThread();
Thread T1 = new Thread(A1);
AlphabetThread A2 = new AlphabetThread();
Thread T2 = new Thread(A2);
T1.start();
T2.start();

2. Modifier le code de l’exercice 1 pour que la classe hérite non plus de la classe
Thread mais plutôt de l’interface runnable.

Exercice 4 : Problème d’accès concurrent


Voici 2 classes Compte (correspond à un compte bancaire) et Operation (thread qui
effectue des opérations sur un compte bancaire).
//La classe Compte
public class Compte {
private int solde = 0;

public void ajouter(int somme) {


solde += somme;
System.out.print(" ajoute " + somme);
}

public void retirer(int somme) {


solde -= somme;
System.out.print(" retire " + somme);
}

public void operationNulle(int somme) {


solde += somme;
System.out.print(" ajoute " + somme);
solde -= somme;
System.out.print(" retire " + somme);
}

public int getSolde() {


return solde;
}
}

//La classe opération


public class Operation extends Thread {
private Compte compte;

public Operation(String nom, Compte compte) {


super(nom);
this.compte = compte;
1.3. TP 1 : THREAD 15

public void run() {


while (true) {
int i = (int) (Math.random() * 10000);
String nom = getName();
System.out.print(nom);
// compte.ajouter(i);
// compte.retirer(i);
compte.operationNulle(i);
int solde = compte.getSolde();
System.out.print(nom);
if (solde != 0) {
System.out.println(nom + ":**solde=" + solde);
System.exit(1);
}
}
}

public static void main(String[] args) {


Compte compte = new Compte();
for (int i = 0; i < 20; i++) {
Operation operation = new Operation("" + (char)('A' + i), compte);
operation.start();
}
}
}

1. Examinez le code et faites exécuter la classe Opération. Constatez le problème :


opération effectue des opérations qui devraient laisser le solde du compte inchangé,
et pourtant, après un moment, le solde ne reste pas à 0. Expliquez.

2. Modifiez le code pour empêcher ce problème. (utilisation de synchronized).

3. Dans le code de Operation, remplacez l’opération nulle par 2 opérations ajouter


et retirer qui devraient elles aussi laisser le solde du compte à 0 (elles sont en
commentaire dans le code). Lancez l’exécution et constatez le problème. Modifiez
le code pour que ça marche.

Exercice 5 : ping pong


1. Ecrire une classe Sem qui implémente un sémaphore : elle contient un champ
booléen curval et deux méthodes get(boolean val) et set(boolean val).

2. La méthode get(boolean val) attend que curval soit égal à val pour continuer

3. La méthode set(boolean val) positionne curval à val

4. Get et set définissent une section critique


16 CHAPTER 1. LES THREADS

Exercice 6 : Un problème d’accès concurrent


Soit le programme suivant :
public class ExempleConcurrent extends Thread {
private static int compte = 0;

public void run() {


int tmp = compte;
try {
Thread.sleep(1); // ms
} catch (InterruptedException e) {
System.out.println("ouch!\n");
return;
}
tmp = tmp + 1;
compte = tmp;
}

public static void main(String args[]) throws InterruptedException {


Thread T1 = new ExempleConcurrent();
Thread T2 = new ExempleConcurrent();
T1.start();
T2.start();
T1.join();
T2.join();
System.out.println("compteur=" + compte);
}
}

Les deux threads T1 et T2 accèdent à une même variable partagée compte, travaillent


sur une copie locale tmp qui est incrémentée avant d’être réécrite dans compte. Nous
avons intercalé un appel à sleep pour simuler un traitement plus long et augmenter la
probabilité que le système bascule d’une tâche à l’autre durant l’exécution de run.

Vous aimerez peut-être aussi