Académique Documents
Professionnel Documents
Culture Documents
Badr Benmammar
Publié pour la première fois en 2018 en Grande-Bretagne et aux États-Unis par ISTE Ltd et John Wiley & Sons, Inc.
www.iste.co.uk www.wiley.com
© ISTE Ltd 2018
Contenu
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix
Annexe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
Bibliographie. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
Acronymes
GC Garbage Collector
Protocole Internet IP
Kilo VM KVM
La plupart des systèmes d'exploitation offrent une distinction entre :
– Processus lourds : soi-disant complètement séparés d'un
une autre.
– Processus légers (threads) : qui partagent un espace mémoire (ainsi
que d'autres ressources) en commun.
réEFINITION 1.– Un thread est une chaîne de code capable de s'exécuter parallèlement
d'autres processus. Les threads ne s'exécutent pas en même temps mais utilisent plutôt
temps partagé, c'est pourquoi il est important qu'un fil donne toujours aux autres un
chance d'exécution.
- Synchronisation des compétitions : lorsque plusieurs threads utilisent le
même ressource. Il doit alors y avoir un système d'exclusion mutuelle afin de
éviter que les processus n'interfèrent les uns avec les autres.
- Synchronisation de la coopération : lorsqu'un thread attend qu'un autre
terminer l'exécution avant de commencer sa propre exécution.
1.3. Création de fil
Regardons le code :
– Méthode 1 : sous-classement de Thread
la classe A étend le fil {
A ( ) {...} // Le constructeur
...
public void run ( ) {
... // Ce que fait le Thread
}
}
A p1 = nouveau A( ); // Création du fil p1
p1.start(); // Démarre le thread et exécute p1.run()
Exemples : coloration de la syntaxe dans les éditeurs, ramasse-miettes
(Détruire JavaVM), etc.
– public final boolean isDaemon() dans la classe Thread pour déterminer le
type de fil.
– public final void setDaemon (booléen) de la classe Thread indique
si le thread sera un démon ou non (thread utilisateur par défaut).
– Il doit être appelé avant que le thread ne démarre à l'aide de la commande start().
– Utilisez setDaemon dans le constructeur.
La classe d'horloge étend Thread { public clock () { setDaemon (true); } public void run () { while (true) { try {Th
6 Programmation simultanée, temps réel et distribuée en Java blabla(); blabla(); perroquet.run (); for (int n=0; n<3; n+
}// fin de classe
class Parrot0 { chaîne privée cri = null ; int privé fois = 0 ; public Parrot0 (String s, int i) { cri = s; fois = je ; } publ
} // terminer l'exécution
} // classe de fin
Introduction aux threads en Java 7 coco coco coco coco blabla blabla blabla
Rien de spécial, le programme principal parle, passe à la course du perroquet et
l'exécution se termine par les trois blabla du programme primaire.
la classe Parrot2 étend Thread { chaîne privée cri = null ; int privé fois = 0 ;
8 Programmation concurrente, temps réel et distribuée en Java public Parrot2 (String s, int i) { cri = s; fois = je ; } publi
coco
blabla
coco
blabla
coco
blabla
coco
blabla
coco
blabla
coco
blabla
coco
blabla
coco
blabla
coco
blabla
coco
blabla
Parrot1 objectParrot = new Parrot1 ("coco",10); Thread ThreadParrot = nouveau Thread (objectParrot); ThreadP
}
la classe Parrot1 implémente Runnable { chaîne privée cri = null; int privé fois = 0 ; public Parrot1 (String s, int
blabla
coco
blabla
coco
blabla
coco
blabla
coco
blabla
coco
blabla
coco
blabla
coco
blabla
coco
blabla
coco
blabla
coco
– Il revient immédiatement à l'exécution du Thread appelant. La course
méthode du Thread s'exécute en même temps que les autres Threads
(multitâche).
– start() autorise le Thread à démarrer, mais pas nécessairement immédiatement.
public void run() { // La valeur i est incrémentée au passage de la nième centaine de mètres for (int i =1; i<=10; i
class Race { public static void main (String[] args) { System.out.println("Passing: "); Coureur Jean = nouveau Cou
12 Programmation concurrente, temps réel et distribuée en Java Runner Paul = new Runner ("Paul"); Jean.start(); Paul
Qui passe:
100 m : Jean
100 m : Paul
200 m : Jean
200 m : Paul
300 m : Paul
300 m : Jean
400 m : Jean
500 m : Jean
600 m : Jean
400 m : Paul
700 m : Jean
500 m : Paul
600 m : Paul
800 m : Jean
900 m : Jean
1000 m : Jean
700 m : Paul
Jean termine !
800 m : Paul
900 m : Paul
1000 m : Paul
Paul termine !
1.6. Différents états d'un thread
Un thread peut être dans l'un des quatre états suivants :
– Nouveau : inclut tous les moments entre sa création (par un
constructeur) et l'appel de sa méthode start().
– Exécutable : immédiatement après l'appel de la méthode de démarrage.
– Bloqué : représentant les instants où le thread est en attente, par
exemple, lorsqu'il est suspendu par la méthode sleep (long) qui interrompt le
l'exécution du thread pendant un temps donné.
– Terminé : lorsque sa méthode run() se termine.
L'état d'un thread peut être spécifié par l'appel de sa méthode getState().
Le cycle de vie d'un thread illustre qu'il peut être suspendu par :
– attendre la fin d'un sommeil ;
– attendre la fin d'un bloc entrée-sortie;
– en attente d'une synchronisation d'attente.
Pour la création de threads en Java, nous avons huit constructeurs
(surtaxe constructeur) [ORA 17a] :
- Fil de discussion();
– Thread (cible exécutable);
- Thread (cible exécutable, nom de chaîne);
– Thread (nom de chaîne);
– Thread (groupe ThreadGroup, cible exécutable);
– Thread (groupe ThreadGroup, cible exécutable, nom de chaîne);
– Thread (groupe ThreadGroup, cible exécutable, nom de chaîne, long
taille de la pile);
– Thread (groupe ThreadGroup, nom de chaîne).
Le code suivant crée un groupe de threads contenant trois threads :
Dans le segment suivant, nous lancerons deux threads sans utiliser le sommeil,
le programme principal et un autre thread.
class ChatAndLaunchTheParrot7 { public static void main(String args[]) { Parrot7 perroquet = new Parrot7("coco",10);
}
la classe Parrot7 étend Thread { chaîne privée cri = null; int privé fois = 0 ; public Parrot7(String s, int i) { cri = s; fois =
} public void repeter() { System.out.println(cri); } public void run() { for (int n=0; n<fois; n++) repeter(); }
Exécution:
blabla
blabla
blabla
blabla
blabla
blabla
blabla
blabla
blabla
blabla
coco
coco
coco
coco
coco
coco
coco
coco
coco
coco
Il s'agit clairement d'une exécution multitâche mais le temps d'exécution est trop court pour
visualiser l'allocation du CPU entre les deux threads.
} // fin principale
} // termine la classe InfernalRace1
18 Programmation concurrente, temps réel et distribuée en Java public Runner(String name) { super(name); this.nam
}// fin d'exécution
}// termine la classe Coureur
Exécution:
Le coureur B effectue 4 500 000 coups de pédale.
Le coureur A effectue 5000000 coups de pédale.
Le coureur B effectue 5000000 coups de pédale.
public class InfernalRace2 { public static void main(String[] args) { Runner A = new Runner("A"); Coureur B = no
A.setPriority(Thread.MAX_PRIORITY);
B.setPriority(Thread.MIN_PRIORITY);
System.out.println("Thread Runner " + A.name + " a la priorité = "
+ A.getPriority());
System.out.println("Thread Runner " + B.name + " a la priorité = "
+ B.getPriority()); Un début(); B.start(); }
class Runner extend Thread { String name public Runner(String name) { super(name); this.name = nom; } publ
20 Programmation simultanée, temps réel et distribuée en Java while (PedalStroke < 5000000) { PedalStroke++; if ((Ped
}
}
}
Exécution:
Le thread A a accédé au processeur en premier car il avait la priorité maximale.
Le thread B s'exécute à la fin du thread A car il a la priorité minimale.
La priorité nous a permis de définir un ordre d'exécution entre les deux threads.
– Priorité entre les threads : méthode setPriority(int).
– Les priorités des threads sont comprises entre 1 et 10.
– Trois constantes représentent les valeurs limites et médianes.
– Plus la valeur est élevée, plus la priorité du thread pour accéder au
processeur.
– MIN_PRIORITY : priorité minimale (1).
– NORM_PRIORITY : priorité normale (5).
– MAX_PRIORITY : priorité maximale (10).
– La priorité normale est attribuée par défaut à un nouveau thread.
Dans la section suivante, nous souhaitons programmer des tâches soit après un
retard (c'est le cas pour le lancement d'une simulation après un certain temps, par
exemple) ou après un certain délai mais périodiquement (dans le cas d'un logiciel
mises à niveau).
importer java.util.Scanner ;
importer java.util.TimerTask ;
importer java.util.Timer ;
class InitiateTheParrot11{ public static void main(String args[]) { Parrot11 perroquet = new Parrot11("coco", 3); Minut
22 Programmation simultanée, temps réel et distribuée en Java do { System.out.println("blabla"); System.out.println("b
importer java.util.TimerTask ;
la classe Parrot11 étend TimerTask { chaîne privée cri = null; int privé fois = 0 ; public Parrot11(String s, int i) { cri = s;
Exécution:
blabla
blabla
Voulez-vous continuer à discuter ? (o/n)
y
blabla
blabla
Voulez-vous continuer à discuter ? (o/n)
coco
coco
coco
y
blabla
blabla
Voulez-vous continuer à discuter ? (o/n)
n
importer java.util.Scanner ;
importer java.util.TimerTask ;
importer java.util.Timer ;
class InitializeTheParrot11{ public static void main(String args[]) { Parrot11 parrot = new Parrot11("coco", 3); Mi
24 Programmation simultanée, temps réel et distribuée en Java timer.schedule(parrot, 3000, 2000); Chaîne réponse="o
importer java.util.TimerTask ;
la classe Parrot11 étend TimerTask { chaîne privée cri = null; int privé fois = 0 ; public Parrot11(String s, int i) { cr
schedule(task, long delay, long period) programme la tâche après un certain délai
pour les exécutions périodiques : les temps sont donnés en millisecondes.
Introduction aux threads en Java 25
Exécution:
blabla
blabla
Souhaitez-vous que le perroquet continue ? (o/n)
coco
coco
coco
coco
coco
coco
ncoco coco
coco
1 Pour une version couleur des codes apparaissant dans ce chapitre, voir www.iste.co.uk/benmammar/
java.zip
Exécution:
AXBYC
ABZ
XC
AYZ
XBC
AYBC
AZ
XBC
AYBC
AZ
XBC
ABYZ
XC
AYBC
AZ
XBC
Ecrivain de ABC a fini
YZ
XYZ
XYZ
XYZ
Ecrivain de XYZ a fini
Il y a concordance entre les deux auteurs et c'est la raison pour laquelle nous
obtenu l'exécution ci-dessus. En utilisant la méthode join() de la classe Thread,
nous pouvons synchroniser les deux threads. EcrivainB sera lancé après la
terminaison d'EcrivainA (classe Prog55.java suivante).
Ce type de synchronisation fait appel à la terminaison.
public class Prog55 { public static void main (String argv[]) { Ecrivain EcrivainA, EcrivainB; EcrivainA = nouvel Ecriva
Exécution:
abc
abc
abc
abc
abc
abc
abc
abc
abc
abc
Ecrivain de ABC a fini
XYZ
XYZ
XYZ
XYZ
XYZ
XYZ
XYZ
XYZ
XYZ
XYZ
Ecrivain de XYZ a fini
2.2. Ressource en exclusion mutuelle : modificateur synchronisé
Prenez le code suivant : la méthode print affiche un mot lettre par lettre
avec un saut de ligne à la fin de la dernière lettre. Les écrivains passent par une imprimante
pour écrire.
Les deux auteurs s'adressent maintenant à une imprimante commune. Ils "écrasent" le
variable texte de l'imprimeur (imprimeur). Le tout est encore illisible (voir
l'exécution ci-dessous).
Exécution:
AXYYZ
Z
AXYYZ
Z
AXYYZ
CA
XYYZ
Z
XABBC
C
AXYYZ
Z
AXYYZ
Z
XABBC
C
XABBC
C
XABBC
Ecrivain de XYZ a fini
C
Ecrivain de ABC a fini
Exécution:
abc
XYZ
abc
XYZ
abc
XYZ
abc
XYZ
abc
XYZ
abc
XYZ
abc
XYZ
abc
XYZ
abc
XYZ
abc
XEcrivain de ABC a fini
YZ
Ecrivain de XYZ a fini
Dans ce qui suit, nous aborderons le partage de variables entre les threads. La
le code suivant illustre un scénario d'utilisation :
La classe Perroquet20 est située dans la classe PerroquetsMatheux20, et
la méthode join est utilisée pour ne pas afficher la valeur du compteur avant
la terminaison des deux threads.
Exécution:
Coco 1
bonjour 2
bonjour 3
Coco 4
bonjour 5
coco 6
bonjour 7
bonjour 8
bonjour 9
coco 10
bonjour 11
Coco 12
bonjour 13
Coco 14
Coco 15
bonjour 16
Coco 17
bonjour 18
Coco 19
coco 20
compteur = 21
2.4. Le problème des exclusions mutuelles
Exécution:
Coco 1
bonjour 1
Coco 2
Coco 3
bonjour 3
Coco 4
bonjour 4
coco 5
bonjour 5
coco 6
bonjour 7
Coco 7
bonjour 8
Coco 8
bonjour 9
Coco 9
coco 10
bonjour 10
bonjour 11
bonjour 12
compteur = 12
La variable compteur est partagée, mais la valeur est différente pour chaque thread. Ce
arrivera que les deux threads doivent travailler sur la même valeur avant de pouvoir
pour l'incrémenter.
Dans ce qui suit, la section où pointe la flèche est celle qui provoque un
publier:
Il faut donc trouver le moyen de définir cette partie comme une section critique. La
Voici la solution à ce problème :
38 Programmation simultanée, temps réel et distribuée en Java
– Nous avons créé une nouvelle classe Counter afin de créer un nouveau « compteur »
objet car la syntaxe de "synchronisé" l'exige.
– Le mot-clé "synchronisé" définit un bloc d'instructions qui peut
ne s'exécutent qu'en exclusivité, même si d'autres threads souhaitent l'exécuter.
– Lorsque le thread perroquetA exécute ce bloc synchronisé et que le
le thread perroquetB souhaite commencer l'exécution de ce même bloc, perroquetB
doit attendre. Une fois perroquetA terminé, perroquetB peut prendre le relais.
– Un seul thread peut exécuter le bloc synchronisé à la fois.
– Le bloc est dit en exclusion mutuelle voire critique
section.
– Si les autres threads souhaitent exécuter cette section, ils doivent attendre le
fil de section critique pour finir.
– S'il y a plus d'un thread en attente d'un bloc synchronisé
lorsqu'il sera disponible, le contrôleur n'autorisera qu'un seul à s'exécuter.
– L'appel sleep() n'entraîne pas la sortie d'un thread de la section critique.
Exécution:
bonjour 1
Coco 2
bonjour 3
Coco 4
bonjour 5
coco 6
Coco 7
bonjour 8
Coco 9
bonjour 10
bonjour 11
Coco 12
Coco 13
bonjour 14
Coco 15
bonjour 16
bonjour 17
Coco 18
Coco 19
bonjour 20
compteur = 20
Illustration 2.2. Section critique partagée entre trois threads. Pour une couleur
version de la figure, voir www.iste.co.uk/benmammar/java.zip
Le mécanisme de "moniteur" d'un objet s'applique à toutes les instances d'Objet :
il s'agit donc d'un mécanisme implémenté au cœur de Java.
Dans ce qui suit, nous allons modifier le code de la section 2.5 afin de
créer une méthode d'instance synchronisée. Le nouveau code est le suivant :
Plus1 est une méthode qui utilise l'exclusion mutuelle et incrémente "val" d'un
unité pour chaque thread qui possède le verrou de la méthode.
Les élèves doivent répéter chaque mot trois fois, peut-être plus.
Illustration 2.3. Exemple consommateur/producteur. Pour une version couleur
de la figure, voir www.iste.co.uk/benmammar/java.zip
Le code naïf de cet exemple est le suivant :
Exécution:
bof
coco bleu
coco bleu
coco bleu
coco bleu
coco bleu
coco bleu
nouveau mot pour perroquet ? (sans non)
jacoblo
jacoblo
jacoblo
jacoblo
jacoblo
.....
Une variable statique "mot" (appelée variable de classe) est partagée entre
les deux fils de perroquet qui doivent l'apprendre puis le répéter. Par la suite, le
thread principal entre un autre mot (mot).
Au début, ils doivent attendre avec une boucle le premier mot et si le professeur
fournit trop vite de nouveaux mots, les deux perroquets risquent d'en "sauter" !
Dans le code suivant, nous avons créé un objet appelé attheboard pour partager
entre producteur et consommateur. Cet objet doit avoir deux méthodes : la
le premier est utilisé par le consommateur (learn) et le second est utilisé par le
producteur (enseigner).
Exécution:
maître jaco
maître jaco
DSR
coco RSD
coco RSD
coco RSD
Jaco RSD
Jaco RSD
Jaco RSD
nouveau mot pour perroquet ? (sans non)
non
2.8.2. Attendez et notifiez
Exécution:
coco bonjour
coco bonjour
coco bonjour
DSR
coco RSD
coco RSD
coco RSD
nouveau mot pour perroquet ? (sans non)
bis
jaco encore
jaco encore
jaco encore
nouveau mot pour perroquet ? (sans non)
bizarre
coco bizarre
coco bizarre
coco bizarre
nouveau mot pour perroquet ? (sans non)
vraiment
jaco vraiment
jaco vraiment
jaco vraiment
nouveau mot pour perroquet ? (sans non)
non
notify() ne réveille qu'un seul thread à la fois ; il n'y a pas de spécification sur
le fil sélectionné ! C'est le contrôleur de thread qui choisit (le mécanisme est
pas sur une base FIFO).
class BusSimple { public static void main (String args[]) { Station BusStation = new Station (); Bus b = nouveau Bus (
1500), new Usager ("E",BusStation, 1000)} ; b.start () ; for (int i = 0 ; i < u.length ; i++) u[ i ].start () ; }
L'objet partagé est BusStation (de type Station) ; il contient deux méthodes
comme prévu. L'un est utilisé par le producteur (chargerUsagers) et l'autre est
utilisé par le consommateur (attendreBus).
Synchronisation des fils 51
La classe BusSimple est utilisée pour lancer cinq utilisateurs ainsi que le bus.
Exécution:
C arriver à la gare
E arriver à la gare
A arriver à la gare
D arriver à la gare
Le bus arrive à la gare
D est monté dans le bus
A est monté dans le bus
E est monte dans le bus
C'est monté dans le bus
Bus départ de la gare
B arriver à la gare
L'exemple utilisé ici est celui d'un professeur enseignant à un étudiant quatre nouvelles
mots. L'élève doit répéter chaque mot trois fois.
Le code de cet exemple est le suivant :
Exécution:
nouveau mot a enseigner au perroquet?
Coco Bonjour
Coco Bonjour
Coco Bonjour
Discuter
Coco Chat
Coco Chat
Coco Chat
Il peut aussi y avoir des modèles avec plus d'un producteur et plus d'un
consommateur. Un tel exemple est le suivant :
– Les threads « producteurs » qui produisent des données et les placent dans un message
file d'attente.
– Les threads «consommateurs» qui récupèrent les données de la file d'attente.
– Les producteurs et les consommateurs ne doivent pas accéder à la file d'attente en même temps
temps (section critique).
- Les producteurs:
- lorsque la file d'attente est pleine : bloquer ;
- lorsqu'il reste des places disponibles : débloquer.
– Les consommateurs :
- lorsque la file d'attente est vide : bloquer ;
- lorsqu'il y a des données dans la file d'attente : débloquer.
Exemples:
– gérer les entrées/sorties d'un parking de N places ;
– gestion d'une salle d'attente dans un cabinet médical, un salon de coiffure,
compagnie d'assurance, etc. avec N chaises disponibles.
Chaque thread qui souhaite accéder à la section critique doit utiliser la méthode
P et, par la suite, il décrémentera la variable du compteur.
Après accès par les quatre premiers threads, le contenu de la section critique
et la valeur du compteur sera la suivante dans le cas où un cinquième
thread devrait souhaiter accéder à la section critique.
Une fois que tous les threads sont sortis de la section critique, l'état de cette dernière
et la valeur du compteur sera la suivante :
– On peut voir le sémaphore comme un ensemble de puces, avec deux opérations :
- prendre une puce ou, si nécessaire, attendre qu'il y en ait une de disponible ;
- acquiert(), généralement avec attente ;
- remettre une puce ;
- release(), généralement avec notification.
– Un sémaphore a une puce et il est très similaire à une serrure.
Exécution:
Le fil 1 n'est pas dans le parking
Le fil 1 entre dans le parking
Le fil 2 entre dans le parking
Le fil 3 entre dans le parking
Le fil 1 sort du parking
Le fil 0 entre dans le parking
Le fil 0 sort du parking
Le fil 4 entre dans le parking
Le fil 2 sort du parking
Le fil 4 sort du parking
Le fil 3 sort du parking
3.1.1. Définition
Les systèmes en temps réel se distinguent des autres systèmes par le fait
qu'ils tiennent compte des contraintes temporelles dans les cas où
le respect des délais est aussi important que la précision du résultat ; en d'autre
mots, le système ne doit pas seulement fournir des résultats exacts, il doit les fournir
dans les délais impartis.
En calcul temps réel, le bon comportement d'un système dépend non seulement
sur les résultats du traitement logique, mais aussi sur le moment où les résultats sont
produit [STA 88].
Exemples:
3.1.2. Exemples de systèmes d'exploitation en temps réel
3.1.4. Architecture
– Architecture monotâche
- Un système temps réel avec une seule tâche est facile à définir. Tout ce qu'il faut, c'est
répéter indéfiniment la liste de tâches suivante :
- Attendez le stimulus.
- Agir en fonction du stimulus.
Exemple : radar automatique.
– Architecture multitâche :
- Chaque tâche peut être effectuée par un thread. Une ressource peut être
protégé par une section critique. La longueur des sections critiques affecte la
temps de réponse du système.
Exemple : système de détection d'intrusion (IDS) qui effectue diverses tâches dans
en temps réel, y compris :
– Analyse du trafic Internet ;
– identifier les adresses IP impliquées dans une attaque ;
– reconfiguration du pare-feu ;
– notification visuelle de l'alerte : affichage de l'alerte dans une ou plusieurs
consoles de gestion.
Les priorités aident à organiser les tâches. Plus la valeur d'une priorité est élevée, plus
plus la priorité d'accès d'une tâche au processeur est élevée. Les priorités peuvent être
dynamique ou statique.
Dans un système temps réel, une tâche n'est généralement jamais bloquée par un
tâche prioritaire.
– Considérons deux threads T1 et T2.
– T1 a une haute priorité (ex : 10), T2 a une basse priorité (ex : 1).
– Au lancement des deux threads, T1 doit s'exécuter avant T2.
– A un certain moment, T1 est bloqué (ex : par wait()), T2 s'exécute et entre dans le
section critique.
– T1 débloque et tente d'acquérir le verrou.
– T2 n'est jamais ordonné et ne libère jamais la serrure
– T2 s'exécute avant T1.
– La non gestion de l'inversion de priorité peut avoir des effets désastreux.
– En effet, comme l'absence de gestion de l'inversion de priorité implique que
une tâche hautement prioritaire ne peut pas s'exécuter, il est possible de réagir en cas d'urgence
situations à ne pas réaliser.
3.2. Java en temps réel
À l'origine, Java a été conçu pour les appareils embarqués. Mais Java rapidement
commencé à être utilisé pour les applications Web (applets). Java a ensuite été utilisé pour la
candidatures suivantes :
– Classic J2SE (Java 2 Standard Edition) pour le poste client (JVM
classique, Point d'accès).
– Business J2EE (Java 2 Enterprise Edition) pour le développement de
applications métier (HotSpot).
– J2ME réduit (Java 2 Micro Edition) pour les systèmes mobiles tels que
tablettes d'assistants personnels et téléphones portables (JVM classique, Kilo VM (KVM),
Carte VM (JVM embarquée sur puce imprimée)).
Notez que le langage ici n'est pas conçu pour le temps réel, pour le
les raisons suivantes:
Il s'agit d'une proposition d'extension de la JVM avec des fonctionnalités temps réel
(JVM RTSJ) [JAV 17a].
Cahier des charges réalisé par un groupe d'experts de plusieurs cabinets
(Sun, IBM, QNX, Nortel, etc.).
Restrictions :
– Compatibilité avec l'existant : toute application Java non temps réel peut s'exécuter
sur une JVM RTSJ.
– Pas d'extension syntaxique.
– Aucun prérequis matériel ou électrique.
– Temps d'exécution prévisible.
– Granularité : nanosecondes. 64 bits (ms) + 32 bits (ns).
– RTSJ améliore la programmation Java temps réel dans les domaines suivants :
- Gestion de la mémoire.
– Horloge et gestion du temps.
– Ordonnance et objets « planifiables ».
– Fils en temps réel.
– Gestion des événements asynchrones et des temporisateurs.
– Synchronisation et partage des ressources.
– Paquet ajouté : javax.realtime [JAV 17b].
– Trois interfaces.
– 47 cours.
– 15 dérogations.
– Quatre erreurs.
– RTSJ : Fils
– Une interface :
– Programmable : objet qui peut être ordonné, s'étend
java.lang.Runnable.
– l'interface publique Schedulable étend java.lang.Runnable.
– Deux nouvelles classes :
– Fil en temps réel.
– la classe publique RealtimeThread étend les implémentations de java.lang.Thread
Programmable.
– Peut accéder au tas. De moindre priorité que le GC (DestroyJavaVM).
– NoHeapRealtimeThread.
– la classe publique NoHeapRealtimeThread étend RealtimeThread.
– Sa priorité est supérieure au GC. Fonctionne dans la mémoire étendue,
les objets de ce thread sont alloués à cette mémoire.
– ScopedMemory présente les caractéristiques suivantes :
– A la durée de vie du thread temps réel qui l'occupe.
– N'est pas géré par le ramasse-miettes.
– Les objets peuvent être alloués dans une ScopedMemory (plutôt que dans
HeapMemory).
– Les objets sont libres à la fin de la portée.
– Un NoHeapRealtimeThread ne peut pas accéder au Heap.
– Par conséquent, un NoHeapRealtimeThread ne peut pas être bloqué par le Garbage
Collectionneur.
– Les priorités sont organisées comme suit :
– Thread < RealtimeThread < Garbage Collector < NoHeapRealtime
Fil de discussion.
3.2.2. Implémentations
1
L'exécution de son code est présentée dans le schéma suivant :
Illustration 3.1. Implémentation de référence
jRate est un projet universitaire (Irvine University en Californie) dirigé par Angelo
Corsaro [UCI 17].
– Java.
– C++ (via CNI).
– CNI : Interface native Cygnus
– Même objectif que JNI (Java Native Interface) : permettre l'accès au
JVM via un langage autre que Java.
– CNI : uniquement C++.
– Correspondance directe entre Java et C++.
1 Pour une version couleur des codes apparaissant dans ce chapitre, voir www.iste.co.uk/benmammar/
java.zip
– Plus simple.
- Plus rapide.
– jRate est une extension de GCJ.
Illustration 4.1. Application monolithique versus application distribuée. Pour une version couleur
de la figure, voir www.iste.co.uk/benmammar/java.zip
4.2.1.1. Définition
Un socket réseau est un modèle qui permet aux processus de communiquer et
synchroniser les uns avec les autres, soit sur la même machine, soit sur un réseau.
Les sockets ont été développés par l'Université de Berkeley (Californie) en 1986.
L'interface originale conçue par Berkeley était pour C mais depuis lors
Les sockets ont été implémentés dans différentes langues.
Pour Java, la classe Socket du package java.net apparaissant dans J2SE 1.4
(6 février 2002) est utilisé pour implémenter les sockets.
Le mode de communication IP utilisé, UDP ou TCP, correspond au
type de prise.
Dans la section suivante, nous nous concentrerons sur les sockets TCP.
Quelques méthodes :
importer java.net.* ;
public class ResolveName{ public static void main(String[ ] args){
InetAddress adresse ;
essayer{
adresse=InetAddress.getByName("localhost");
System.out.println(adresse.getHostAddress());
adresse=InetAddress.getByName("www.facebook.com");
System.out.println(adresse.getHostAddress());
System.out.println(adresse);
}catch(UnknownHostException e) { }
}
}
Programmation distribuée en Java 75
Exécution:
127.0.0.1
31.13.75.36
www.facebook.com/31.13.75.36
– connect (SocketAddress adresseSocket, int timeout) : connecte le
socket au serveur avec une valeur d'expiration de l'ordre des millisecondes.
– Lève une SocketTimeoutException si le délai a déjà expiré.
– SocketAddress est une classe abstraite.
– InetSocketAddress hérite de SocketAddress et est une classe représentant
une InetAddress et un port.
– InetSocketAddress(InetAddress ina, int port).
– InetSocketAddress(chaîne hostName, int port).
– getAddress().
– getHostname().
– getPort().
Le code suivant est utilisé pour créer un socket avec un délai d'attente en utilisant
relier:
secondes.
} catch (UnknownHostException e) {
} catch (SocketTimeoutException e) {
} capture (IOException e) {
}
Une demande de connexion bloque jusqu'à ce que la connexion soit établie ou que le
le délai expire ; si le délai a expiré, une SocketTimeoutException est
soulevé.
4.2.1.3. Exemple de communication
Dans ce qui suit, nous présentons un exemple très simple de communication
avec les sockets TCP :
– Le client envoie un message au serveur.
– Le serveur accuse réception et demande le mot suivant.
– Pour se déconnecter, le client envoie le mot « non ».
importer java.net.* ;
importer java.io.* ;
class ServerEcho { public static void main(String args[]) { ServerSocket server = null; essayez { serveur = nouveau Serv
78 Programmation simultanée, temps réel et distribuée en Java while ((recu = sockIn.readLine()) != null) { System.out.
essayez {server.close();}
capture (IOException e2) {}
} // fin de la première capture
}// fin main } // fin classe
Le code client est le suivant :
importer java.io.* ;
importer java.net.* ;
importer java.util.Scanner ;
public class ClientEcho { public static void main(String[] args) throws IOException { Socket sock = null; PrintWriter soc
L'exécution est la suivante :
– Côté serveur :
lié
reçu : bonjour
reçu : au revoir
- Côté client:
A noter que, pour les caractères, le langage Java utilise le type "char" basé
sur Unicode encodé en 16 bits. La transmission sur les réseaux est basée
sur un octet de 8 bits.
Les méthodes de télécommunication pour Java sont donc basées sur le transfert
d'octets.
– Convertir une String en bytes[ ] :
- chaîne de caractères = "ABCD" ;
- byte[ ] message=chaine.getBytes();
– Convertir un byte[ ] en String :
- chaine = new String(message);
buffer = "ABCD".getBytes();
essayez { sockOut.write(buffer);
L'écriture et le vidage peuvent déclencher une exception IOException particulière, une tentative de
écrire sur un flotteur fermé.
essayez { OutputStream sockOut = socket.getOutputStream(); octet[ ] tampon = nouvel octet[1024] ; String envoi
} capture (IOException e) {}
// lit le flux et le stocke dans le tampon. Renvoie le nombre de
lire les octets.
System.out.write(buffer, 0, lu);
// affiche le tampon sur la sortie standard.
} capture (IOException e) {}
en UTF-8 modifié.
} capture (IOException e) {
}
Pour la lecture de données Java dans un format connu depuis une socket, il faut
utiliser DataInputStream qui nous permet de lire n'importe quel type de primitive Java sur tous
systèmes.
DataInputStream sockDataIn = null ;
essayez { sockDataIn = new DataInputStream(socket.getInputStream()); octet par = sockDataIn.readByte(); // renvoie l'o
// lit un octet et renvoie faux si l'octet est == 0. int i = sockDataIn.readInt(); // lit 4 octets et retourne le correspondant
Le code suivant permet à un utilisateur de lire ligne par ligne le texte d'un socket :
essayer{
BufferedReader sockReader = nouveau BufferedReader (nouveau InputStreamReader
(socket.getInputStream()))
Ligne de cordes ;
while ((line = sockReader.readLine()) != null) System.out.println(line);
} capture (IOException e) {}
importer java.io.Serializable ;
public class Student implémente Serializable{ String name; Corde majeure ; int moy;
1 Pour une version couleur des codes apparaissant dans ce chapitre, voir www.iste.co.uk/ benmammar/
java.zip
Exécution:
Du côté serveur:
lié
reçu un
Côté client:
serveur -> client : Etudiant : A GL : 13
4.2.1.8. Communications entre une applet Java et un serveur utilisant
prises
L'exécution suivante montre une communication entre une applet et un
serveur utilisant des sockets :
Illustration 4.4. Communication entre une applet et un serveur avec les Sockets. Pour un
version couleur de la figure, voir www.iste.co.uk/benmammar/java.zip
importer java.io.* ;
importer java.net.* ;
importer java.util.* ;
public class NetServer { public static void main(String[] args) { try{ ServerSocket serverSocket = new ServerSocket(876
import java.applet.Applet ;
import java.awt.*;
import java.awt.event.* ;
importer java.io.* ;
importer java.net.* ;
public class NetApplet étend Applet implémente ActionListener { TextField numbField; Affichage d'étiquettes ; Prise d
}// fin de classe
4.2.2. Communication de haut niveau : middleware
Ce sont des couches offrant des services plus complexes. Ces calques sont créés
à l'aide de couches TCP/UDP.
Exemple : appel depuis une méthode dans une entité distante.
Figure 4.5. Position du middleware dans un réseau. Pour une version couleur de
la figure, voir www.iste.co.uk/benmammar/java.zip
- Mono-langue ; Java, multiplateforme : de JVM à JVM.
- CORBA (Common Object Request Broker Architecture).
- Multi-langue, multi-plateforme.
- DCOM (Distributed Component Object Model) de Microsoft.
- Multi-langue, communication entre logiciels distribués
Composants.
Dans la section suivante, nous nous concentrerons sur Java RMI.
Pour que les clients puissent accéder aux services distants, ils doivent être enregistrés dans un
enregistrement.
Le registre RMI s'appelle rmiregistry et il possède une table de hachage
où les clés sont des noms et les valeurs des objets distants.
– Un stub est un proxy (représentant local d'un objet distant) qui est chargé
dans le client lors de l'obtention de la référence.
– Le client convoque donc, via sa référence, les moyens qui résident
dans le stub qui convoque les méthodes réelles sur l'objet distant en voyageant
sur un réseau.
– C'est le mécanisme qui assure la transparence des appels.
Figure 4.8. Exploitation d'une application RMI. Pour une version couleur de
la figure, voir www.iste.co.uk/benmammar/java.zip
Illustration 4.9. Étapes d'un appel de méthode distant. Pour une version couleur du
figure, voir www.iste.co.uk/benmammar/java.zip
Figure 4.10. Architecture IRM. Pour une version couleur de la figure, voir
www.iste.co.uk/benmammar/java.zip
– Première couche : talon/squelette.
- Stub : représentant local de l'objet distribué. - Initie une connexion avec la JVM distante en transmettant le
invocation distante à la couche d'objets de référence (RRL). - Assemble les paramètres en vue de leur transfert au
votre interlocuteur.
- Remarques : Le squelette n'est plus nécessaire depuis Java 2 (1998).
- Une classe similaire de squelette générique est partagée entre tous
objets.
- De plus, jusqu'à la version 5.0 de J2SE (2004), un compilateur stub
appelé RMIC (Java RMI Compiler) était nécessaire pour générer le stub/
squelette avant de l'enregistrer dans le registre RMI.
- Désormais, il est possible de les générer dynamiquement (plus besoin d'utiliser le
CRIM).
– Deuxième couche : RRL (couche de référence d'objet).
- Permet d'obtenir la référence de l'objet distant en consultant le
rmiregistry.
- rmiregistry s'exécute sur chaque machine hébergeant des objets distants.
- Un seul rmiregistry par JVM.
- rmiregistry accepte les demandes de service sur le port 1099 (par défaut).
– Troisième couche : transport.
- Utilise un protocole de communication supérieur à TCP/IP.
- Lors d'un appel de méthode, le stub transmet l'appel à la couche RRL, la
ce dernier transmet la requête à la couche transport du RMI qui est au-dessus du
TCP/IP et transfère la requête en remontant jusqu'au squelette qui appelle
sur l'objet éloigné.
- Par défaut, RMI utilise le protocole JRMP (Java Remote Method
Protocol) sur le port 1099. Il s'agit d'un protocole utilisé pour appeler des méthodes distantes. - JRMP est le protocole d
applications écrites en Java - Version originale. - Intégré dans la langue. - Simple à utiliser.
– lancements de liaison vide statique (nom de chaîne, obj distant)
DéjàBoundException, MalformedURLException, RemoteException.
– static void rebind (String name, Remote obj) lance RemoteException,
MalformedURLException.
– Remarque : bind() lève une exception si un objet est déjà enregistré sous
le même nom dans la base de registre (même nom pour deux objets différents).
Remote est une interface pour concevoir des objets distants.
– Le client trouvera l'objet grâce à la méthode lookup().
- la recherche à distance statique (nom de chaîne) lève NotBoundException,
MalformedURLException, RemoteException
Les éléments suivants déploieront RMI :
- Du côté serveur:
- La définition d'une interface contenant les méthodes pouvant être
appelé à distance (l'interface est partagée avec le client).
- Ecriture d'une classe qui implémente cette interface.
- Ecriture d'une classe (serveur) qui va instancier l'objet et l'enregistrer
en lui donnant un nom dans le registre RMI.
- Côté client:
- Obtenir une référence sur l'objet distant à partir de son nom.
- Méthode d'appel à partir de cette référence.
Pour déployer RMI, suivez les étapes ci-dessous :
importer java.rmi.Remote ;
import java.rmi.RemoteException ; interface publique Hello étend Remote { public String Bonjour() lance RemoteEx
– Remote est une interface sans méthode.
– Un objet qui implémente l'interface Hello est un objet qui prend en charge
un accès distant à ses méthodes.
– Toutes les méthodes doivent lancer une RemoteException.
Figure 4.11. Déploiement RMI. Pour une version couleur de la figure, voir
www.iste.co.uk/benmammar/java.zip
Implémentation de la classe qui implémente l'interface côté serveur :
import java.rmi.RemoteException ;
importer java.rmi.server.UnicastRemoteObject ;
la classe publique HelloImpl étend UnicastRemoteObject implémente Hello
{ public HelloImpl() lance RemoteException { }
public String Bonjour() lance RemoteException { return "Bonjour tous le monde !"; }
– Il doit implémenter l'interface Hello.
– Il doit étendre UnicastRemoteObject.
– Unicast : l'objet distant existe en un seul exemplaire sur un seul
machine.
– Il peut implémenter d'autres méthodes que celle de l'interface, mais celles-ci
ne sera pas accessible à distance.
– Les builders lancent une RemoteException.
– Par conséquent, l'utilisateur doit toujours en écrire un.
Implémentation du serveur :
import java.net.MalformedURLException ;
import java.rmi.Naming ;
import java.rmi.RemoteException ;
importer java.rmi.registry.LocateRegistry ;
public class HelloServer { public static void main(String[] args) { try {
Implémentation du client :
import java.net.MalformedURLException ;
import java.rmi.Naming ;
importer java.rmi.NotBoundException ;
import java.rmi.RemoteException ;
public class HelloClient { public static void main(String[] args) { String url = null; Bonjour bonjour = null; essayez {
– Lancer le serveur.
– Lancer le client.
– Exécution côté client :
Bonjour tout le monde !
Pour implémenter une application RMI, vous devez d'abord :
– Écrivez la ou les interfaces distantes.
- Bonjour.java.
– Ecrire sa (leurs) implémentation(s).
- BonjourImpl.java.
– Écrivez le serveur.
- HelloServer.java.
– Écrivez le client.
- BonjourClient.java.
Remarque : il est possible de regrouper les deux classes (serveur et interface
implémentation) dans la même classe.
L'exemple suivant décrit ce type de cas. C'est une télécommande
multiplication.
L'interface:
importer java.rmi.Remote ;
import java.rmi.RemoteException ;
interface publique bonjour étend à distance {
public int multi (int a, int b) lève RemoteException ;
}
Implémentation du serveur :
import java.rmi.*;
import java.rmi.server.* ;
import java.rmi.registry.* ;
la classe publique HelloServer étend les implémentations d'UnicastRemoteObject
Bonjour {
public HelloServer() lève RemoteException {}
public int multi(int a, int b) lance RemoteException {
retourner un * b ; }
public static void main(String[] args) { essayez { LocateRegistry.createRegistry(1099); } catch (RemoteException e1
catch(Exception e) {
System.err.println("Erreur : " + e.getMessage());
}
}
}
Cela fonctionne avec un rebind car il s'agit d'un resave; badr se réfère uniquement à object1.
Implémentation du client :
import java.rmi.*;
classe publique BonjourClient {
public static void main(String[] args) {
essayer {
104 Programmation simultanée, temps réel et distribuée en Java Hello reference = (Hello)Naming.lookup("badr"); int r
Exécution:
RMIClientSocketFactory csf)
Exemple d'application :
Dans l'exemple précédent, l'appel est effectué par valeur.
Dans notre cas:
import java.rmi.*; interface publique La position s'étend à distance { public void insererTable (Point p, String no
RemoteException ; public Point position (String nom) lance RemoteException ; public int nombreCles() lève Rem
}
L'interface précédente implémente les éléments suivants :
– Création d'un nouveau poste et attribution d'un nom.
– Accès en lecture aux positions.
– Accès au nombre de positions.
importer java.io.Serializable ;
public class Point implements Serializable { public float x; flotteur public y ; point public(float xx,float yy) { x = x
L'implémentation du serveur :
import java.rmi.*;
import java.rmi.server.* ;
importer java.net.* ;
importer java.util.Hashtable ;
import java.rmi.registry.* ;
public class Serveur étend UnicastRemoteObject implémente Position
{ table de hachage privée h ;
public Serveur() lance RemoteException { h = new Hashtable() ; } public void insererTable(Point p,String nom) lan
{ h.put(nom,p); }
public Point position(String nom) lance RemoteException { return((Point) h.get(nom)); } public int nombreCles() la
public static void main(String [] args) { try { Serveur ib = new Serveur(); essayer {
importer java.rmi.* ; public class Client { public static void main(String [] args) { try { Position b =(Position) Namin
Exécution:
- Du côté serveur:
Prêt
- Côté client:
0
2
(11.0, 5.0)
(14.0, 9.0)
Ignorer Serializable dans la définition de point comme suit :
public class Point { public float x ; flotteur public y ; point public(float xx,float yy) { x = xx ; y = yy ; }
- Du côté serveur:
- Côté client:
0 // le type primitif int ne pose pas de problème. java.rmi.MarshalException : erreur lors du regroupement des argu
Le problème survient pour la transmission d'un objet de type Point qui doit être
Sérialisable.
Transmission d'objets Distants : Serveurs multi-threads RMI (chatrooms) :
– Chaque client est un fil et a trois actions possibles : rejoindre, parler ou quitter.
– Le serveur doit maintenant maintenir une liste d'objets distribués (Remote).
– Utilisez la synchronisation pour garantir la cohérence entre les données.
– Les actions du client doivent être affichées aux autres clients.
– Thread implémenté à l'aide de Jframe (Swing)
Première cliente :
Deuxième client :
112 Programmation simultanée, temps réel et distribuée en Java
Troisième client :
Le deuxième client sera le suivant :
import java.rmi.*;
interface publique ChatInterface extend Remote { public void rejoindre (Notify n) throws RemoteException; public vo
}
import java.rmi.*;
public interface Notify extend Remote { // Notify fait référence à un client public String getName() lance RemoteExcep
Côté serveur :
collection privée<Notify> threadList = new ArrayList<Notify>(); public Serveur() lève RemoteException {}
…
…
iterator() est une méthode Collection qui renvoie un Iterator sur la table (Iterator
est une interface pour désigner un itérateur sur la table).
hasNext() et next() sont des méthodes de l'itérateur.
4.2.2.5. Gestion de la sécurité dans RMI
La sécurité est importante lorsque le code est téléchargé (il peut être risqué d'exécuter
code d'un serveur distant). Pouvoir transmettre du code exécutable à distance
est, en théorie, une faille de sécurité. Lors de la transmission locale du code, il n'y a pas
problème de sécurité.
- La solution:
- Mettre en place un « gestionnaire de sécurité ».
Exemple de contenu dans le fichier java.policy :
} ;
Il permet les connexions socket sur les ports 1024 à 65535 pour tous les clients RMI
et les connexions au serveur Web par défaut (port 80) sur toutes les machines. Ça aussi
définit les droits d'accès par rapport aux fichiers cités.
Exemple:
Pour utiliser java.policy :
$ javac Serveur.java
$ java -Djava.security.policy=java.policy Serveur
Objectif:
– Authentification du serveur.
– En option, authentification du client.
– Confidentialité des données (ou session cryptée).
– Intégrité des données échangées.
– L'API JSSE (Java Secure Socket Extension) permet de
manipuler des sockets sécurisés en Java répondant aux spécifications SSL.
– Les classes et interfaces JSSE sont regroupées dans des packages javax.net
et javax.net.ssl.
Figure 4.13. RMI avec TLS. Pour une version couleur de la figure, voir
www.iste.co.uk/benmammar/java.zip
4.2.2.6. Chargement de classe dynamique
– Les classes Java sont hébergées sur un serveur Web.
– Cela évite de conserver toutes les définitions de classe localement.
– Les mêmes fichiers de classe sont partagés par tous les clients.
– Seules les classes nécessaires sont chargées.
– Dans ce cas, on peut se tourner vers le chargement dynamique :
- Placez la classe java (stub, par exemple) dans un serveur Web qui est
accessibles par le client. Par exemple : http://monserveur.fr/test.
- Implémentez un SecurityManager : - Ajoutez la ligne suivante dans le client et le serveur :
import java.rmi.Naming ;
classe publique BonjourClient {
public BonjourClient() {
essayer {
Bonjour obj = (Bonjour)Naming.lookup("Bonjour");
System.out.println(obj.Bonjour());
} capture (Exception e) {
System.out.println("Exception HelloClient : " + e);
}
}
}
HelloClient.java est une classe partagée entre plusieurs clients RMI.
importer java.rmi.server.RMIClassLoader ;
importer java.util.Properties ;
classe publique DynamicClient {
public DynamicClient() lance une exception {
// Installer un gestionnaire de sécurité (sans ce gestionnaire, le chargement n'est pas autorisé)
System.setSecurityManager(nouveau SecurityManager());
Propriétés p = System.getProperties();
/Indiquer le chemin de téléchargement
URL de chaîne = p.getProperty("java.rmi.server.codebase");
// Télécharger la classe souhaitée
Classe clientClass = RMIClassLoader.loadClass(url, "HelloClient");
// Crée une instance de classe
clientClass.newInstance();
}
public static void main (String args[]) {
essayez {DynamicClient dc = new DynamicClient();
} catch (Exception e) {System.out.println(e);}
}
}
Effectuer un appel du serveur vers les clients.
Dans ce cas, chaque élément (client ou serveur) peut jouer les deux rôles
indifféremment (client et serveur simultanément).
Illustration 4.14. Rappel dans RMI. Pour une version couleur de la figure, voir
www.iste.co.uk/benmammar/java.zip
Pour effectuer le rappel, nous avons besoin de six classes comme suit :
Figure 4.15. Classes nécessaires pour un rappel. Pour une version couleur de
la figure, voir www.iste.co.uk/benmammar/java.zip
InterfaceCalback.java : interface contenant la méthode de rappel. Cette
L'interface est implémentée par Callback.java.
Un exemple de cette interface est le suivant :
importer java.rmi.Remote ;
import java.rmi.RemoteException ;
interface publique InterfaceCallback étend Remote { public void doCallback () lance RemoteException ;
import java.rmi.RemoteException ;
import java.rmi.server.UnicastRemoteObject ;
la classe publique Callback étend les implémentations d'UnicastRemoteObject
Rappel d'interface {
public Callback() lève RemoteException {}
public void doCallback() throws RemoteException { System.out.println ("Bonjour tous le monde") ;
}
}
Servant.java : le thread qui lance la méthode de rappel.
import java.rmi.*;
public class Servant étend Thread {
obj de rappel d'interface privé ;
serviteur public (obj InterfaceCallback) {
this.obj = obj ;
}
public void run() { // exécution en tant que thread séparé
essayez {Thread.sleep (3000) ; } catch ( InterruptedException e ) { }
essayez {obj.doCallback() ; // exécute le callback, appelle la méthode sur le
côté client
}
catch (RemoteException e ) { System.err.println ("Echec du retour d'appel : " + e ) ;
}
}
}
importer java.rmi.Remote ;
import java.rmi.RemoteException ;
interface publique InterfaceServeur extend Remote { public void callMeBack (InterfaceCallback obj) throws RemoteEx
;
}
import java.rmi.Naming ;
Client de classe publique {
public static void main( String [ ] args ) lance une exception {
Rappel obj = nouveau Rappel ( ) ; // création de l'objet callback
InterfaceServeur serv= (InterfaceServeur) Naming.lookup ("Serveur");
System.out.println ("Démarrage de l'appel");
serv.callMeBack (obj) ; // demande d'appel
}
}
Server.java : implémente la méthode qui rappelle le client en passant le
objet reçu de ce dernier fil.
import java.rmi.Naming ;
import java.rmi.RemoteException ;
importer java.rmi.registry.LocateRegistry ;
importer java.rmi.server.UnicastRemoteObject ;
public class Server étend UnicastRemoteObject implémente
Serveur d'interface {
public Server ( ) lève RemoteException { }
public void callMeBack (InterfaceCallback obj) lance RemoteException {
Serviteur serviteur = nouveau Serviteur (obj) ; // Création du fil
serviteur.start ( ) ; // Initialisation du thread
}
public static void main( String [ ] args ) lance une exception {
essayez {LocateRegistry.createRegistry(1099);} catch (RemoteException e1) { System.err.println("rmiregistry est déjà la
port");System.exit(1); }
Serveur serv = nouveau Serveur ( ) ;
Naming.rebind ("Serveur" , serv) ;
System.out.println ("Serveur prêt" ) ;
}
}
Exécution:
- Bail : Mémoire « louée » à un objet pour une durée limitée. - Par défaut 600000 millisecondes (10 min), modifiabl
propriété java.rmi.dgc.leaseValue (à partir de la version 1.1 du JDK). - Pour le modifier, faites-le au lancement du prog
annexe
la classe Parrot4 étend Thread { chaîne privée cri = null ; int privé fois = 0 ; public Parrot4 (String s, int i) { cri = s; fois =
}
class ChatAndLaunchParrot4{ public static void main(String args[]) { Parrot4 parrot = new Parrot4("coco",5);
128 Programmation concurrente, temps réel et distribuée en Java parrot.start(); System.out.println("Thread bavard : "
Quel est le nom du thread principal, quel est le nom donné par
par défaut à l'autre fil? Quel est l'intérêt de la méthode isAlive() ? Indice:
la méthode de classe currentThread() retourne un pointeur sur l'objet Thread,
qui appelle cette méthode.
la classe Parrot5 étend Thread { chaîne privée cri = null; int privé fois = 0 ; public Parrot5(String s, int i) { super("perro
}
public void run(){ afficheThreads();
Annexe 129 for (int n=0; n<fois; n++) { try { Thread.sleep(1000);
La méthode de classe énumère (table Thread[]) stocke dans la table donnée le
références des threads actifs dans le groupe et les sous-groupes du thread appelant.
Il renvoie le nombre de threads actifs obtenus.
Écrivez la classe compteur et testez-la en lançant 4 qui comptent jusqu'à 10. Voir
lequel termine premier (les quatre jetons s'appellent Tata, Titi, Toto, Tutu).
Annexe 131
Exercice 4 : ressource en exclusion mutuelle
} public void print(String t) { text=t; for (int j=0;j<text.length()-1; j++) { System.out.print(text.charAt(j)); essayez { Threa
System.out.println(text.charAt(text.length()-1)); }
public class Prog56 { public static void main (String argv[]) { Writer2 writerA, writerB ; Imprimante1 impression= nou
public class Writer2 extend Thread { private String text; impression privée Printer1 ; public Writer2(String t, Printer1
132 Programmation simultanée, temps réel et distribuée en Java text=t; } public void run() { for (int i=0; i<10; i++) { pri
Annexe 133
4 * 7 = 28
5 * 7 = 35
6 * 7 = 42
7 * 7 = 49
8 * 7 = 56
9 * 7 = 63
10 * 7 = 70
PS : Toute réponse autre que « y » annule la programmation.
Considérons les deux classes suivantes (la classe Parrot20 est dite interne
car il est situé à l'intérieur de la classe MathsParrots20), et il y a un
fichier java unique appelé MathsParrots20.java.
classe publique MathsParrots20{ compteur int privé ; public static void main(String args[]) { new MathsParrots20(); } p
134 Programmation concurrente, temps réel et distribuée en Java private int fois = 0; public Parrot20(String s, int i) { c
– Réutiliser la simulation d'une course pour simuler une course entre cinq coureurs
A, B, C, D et E et affiche le rang de chaque coureur.
Exemple:
Exercice 7 : variables partagées : variable globale (statique)
Avant de commencer : sous Java, chaque lettre peut être codée via le type char
qui a aussi une valeur entière. Testez et examinez le code suivant :
System.out.println('A');
System.out.println('A'+0);
System.out.println('A'+1);
System.out.println((char)('A' + 0));
System.out.println((char)('A' + 1));
System.out.println("Afficher l'alphabet :"); for (int i = 0; i < 26; i++) { System.out.print((char)('A' + i)+" "); }
– Reprendre l'exemple d'une course pour simuler une course entre cinq coureurs A,
B, C, D et E et affiche le rang de chaque coureur. Vous devez utiliser une boucle pour
lancer les cinq threads.
Exemple d'exécution :
– A est arrivé à la première place
– B est arrivé à la deuxième place
– D est arrivé à la troisième place
–…
– Simuler les quatre threads A, B, C et D, qui calculent respectivement
la factorielle de 4, 5, 6 et 7. Si l'ordre de terminaison est A, B, C et D, vous
doit également afficher :
Exercice 8 : synchronisation des threads
public class Counter { private int balance = 0; public void operationNulle(int sum) { balance += sum; System.out.p
}
L'opération de classe publique étend le fil { Compteur privé Compteur ; public Operation(String nom, Counter C
public void run() { while (true) { int i = (int) (Math.random() * 1001); Nom de chaîne = getName(); System.out.pr
solde); System.exit(1); } } }
public static void main(String[] args) { Counter Counter = new Counter(); for (int i = 0; i < 3; i++) { Operation ope
Compteur); opération.start(); } }
importer java.awt.Graphics ;
import java.applet.Applet ;
la classe publique Prog11 étend l'applet {
public void paint (Graphics g) { g.drawLine (10, 30, 200, 30); g.drawRect (20, 40, 100, 50); g.drawOvale (140, 40, 50, 50);
importer java.awt.Graphics ;
importer java.awt.Color ;
import java.applet.Applet ;
public class Prog12 étend Applet { public void init() { setSize(220,120); setForeground(Color.red); setBackground(Color
}
}
Exécutez l'applet.
Expliquez les différentes méthodes dans init et paint.
Considérez la classe CounterThread suivante :
la classe publique CounterThread étend l'applet implémente Runnable { Thread t; nombre entier ; public void init() { C
} public void run() { while (Count < 20) { Count++; repeindre(); essayez {Thread.sleep(1000); } catch (InterruptedExcep
Exécutez l'applet et examinez le résultat.
Modifier le code précédent pour Counter dix fois plus rapide et sans
arrêt.
import java.util.concurrent.Semaphore ; la classe Process2 étend Thread { identifiant int privé ; sémaphore privé sem
Annexe 141 try { sem.acquire(); } catch (InterruptedException e
Sémaphore sem = nouveau Sémaphore(4); Process2[] p = nouveau Process2[4] ; for (int i = 0; i < 4; i++) { p[i] = new Pro
}
Des questions:
1) Lancer l'exécution et expliquer la déclaration Semaphore sem =
nouveau Sémaphore(4); c'est-à-dire le point du nombre 4, que signifie 4
ici?
2) Relancer l'exécution en changeant le chiffre 4 en 3, 2, 1 et 0,
respectivement. Expliquez pour chacun.
3) Avec la déclaration Semaphore, sem = new Semaphore(1); mettre
sem.release(); en commentaire. Exécuter et expliquer.
- Si le coiffeur est occupé lorsque le client arrive, le client s'assied et
s'endort sur une des chaises N de la salle d'attente, il doit attendre le
siège de barbier à disposition.
– Lorsque le coiffeur a terminé une coupe de cheveux, il quitte le client et
va réveiller un des clients dans la salle d'attente.
– Si la salle d'attente est vide, le barbier se rendort sur son
siège de barbier et attend l'arrivée d'un autre client.
Le but de cet exercice est d'associer un fil au barbier également
comme pour chaque client et programmez une séance de barbier endormi en Java. La
l'utilisation de sémaphores est nécessaire pour garantir l'exclusion mutuelle entre les
fils.
Exemple d'initialisation :
Annexe 143
Exemple : while (!FreeChairs.tryAcquire()) sleep(5000); pourrait être utilisé dans
l'exécution du client afin de se mettre en attente s'il n'y a pas de
chaises.
Dans cet exercice, nous souhaitons compter les entrées et les sorties des véhicules dans un
parking pour afficher le nombre de places disponibles dans le parking chaque
moment où une voiture sort. Le fonctionnement du parking est simple : il y a un seul
entrée au parking qui a une capacité de N.
Cette variable doit être déclarée dans une classe Car (le thread qui représente
le comportement d'une voiture) comme suit : static int cont=N ;
Une seule classe est nécessaire dans cet exercice (Classe Voiture), et cette dernière est
caractérisé par un nombre entier i et un sémaphore s.
Ce problème est un grand classique [DIJ 70]. Cinq philosophes sont réunis pour
effectuer deux activités principales : penser et manger. Chaque philosophe pense pour un
un laps de temps aléatoire, mange (si possible) pendant un laps de temps aléatoire,
puis revient à la réflexion. Quand un philosophe demande à manger, une assiette de
les spaghettis attendent. Les cinq assiettes sont disposées autour d'une table ronde comme
montré dans la figure ci-dessous:
144 Programmation simultanée, temps réel et distribuée en Java
Pour qu'un philosophe mange ses spaghettis, il faut deux fourchettes : la sienne (la
celui de droite) et celui de son voisin de gauche. Naturellement, si l'un des
voisins mange déjà, le philosophe ne peut pas manger et doit attendre un
ou les deux fourches pour les libérer.
On ne voit naturellement pas deux philosophes manger côte à côte en même temps
temps.
– Programmer une solution en java qui utilise deux classes : Dinner (contient le
"main", etc.) et Philo (thread correspondant à l'activité d'un
philosophe).
Exemple d'exécution :
Philo0 réfléchit
Philo2 réfléchit
Philo1 réfléchit
Philo3 réfléchit
Philo4 réfléchit
Philo4 veut manger
Philo4 mange
Philo0 veut manger
Philo3 veut manger
Philo1 veut manger
Philo1 mange
Philo2 veut manger
Annexe 145
Philo4 a fini de manger
Philo3 mange
Philo4 réfléchit
Philo1 a fini de manger
Philo1 réfléchit
Philo0 mange
Philo4 veut manger
Philo1 veut manger
Philo3 a fini de manger
Philo3 réfléchit
Philo2 mange
Philo0 a fini de manger
Philo0 réfléchit
Philo4 mange
...
– Lors de vos tests, assurez-vous qu'il n'y a pas deux philosophes côte à côte.
manger en même temps, et qu'il n'y a pas de famine, et bien sûr, que
il n'y a pas d'interblocage.
Exercice 14 : producteur/consommateur
Considérez les trois classes Java suivantes : B.java, Producer.java et
Consommateur.java.
dépôt de vide synchronisé public (Object obj) {
while (cond1) try {wait ();}catch (InterruptedException e) {}
tampon [der] = obj ;
der = (der + 1) % taille ;
nbObj = nbObj + 1 ;
notifier ();
}
Objet public synchronisé collecté () {
while (cond2) try {wait ();}catch (InterruptedException e) {}
Objet obj = buffer [prem] ;
tampon [prem] = null ;
prem = (prem + 1) % taille ;
nbObj = nbObj - 1 ;
notifier ();
retour (obj);
}
} // fin B
Annexe 147
class Consumer implémente Runnable {
privé B b;
Consommateur public (B b) {this.b = b;}
public void run () {
Valeur entière ;
tandis que (vrai) {
val = (Entier) b.preleve();
System.out.println ( Thread.currentThread ().getName () +"collecté" + val );
essayez {Thread.sleep ((int)( Math.random ()*300));}catch ( interrompu
Exception e) {}
}
}// fin d'exécution
}// Consommateur final
Voici l'opération possible avant votre intervention :
Utilisez la synchronisation des threads pour trier ce problème.
Fin
Annexe 149
Exercice 17 : mystère avec les sockets TCP
//// A.java
importer java.io.* ;
public class A{ public static void mystère (InputStream in, OutputStream out) jette
IOException { byte buf[ ] = new byte[1024] ; int n ; while((n=in.read(buf))!=-1) out.write(buf,0,n); joindre(); out.clos
/////////// Client.java
importer java.io.* ;
importer java.net.* ;
public class Client{ public static void main(String []args) throws IOException { Socket sock = new Socket(InetAddre
}
//// Serveur.java
importer java.io.* ;
importer java.net.* ;
public class Serveur{ public static void main(String []args) throws IOException { Socket sock = new ServerSocket(9
Nous avons une liste de guichets bancaires. Un compteur est caractérisé par son compte
titulaire, son numéro et son solde.
Annexe 151
Exercice 19 : applet et sockets TCP
Créez le modèle client/serveur suivant :
Exercice 20 : opérations arithmétiques avec des sockets TCP
Votre objectif est de créer une application client/serveur utilisant des sockets TCP.
Le client envoie deux opérandes au serveur ainsi que l'opération à
effectuer (tous les trois sous la forme d'une seule chaîne de caractères), et le serveur
répond avec le résultat de l'opération. Par exemple, si le client devait
envoyer au serveur :
– ADD 6 1, le serveur répondrait par 7.
– MUL 10 88, ce dernier répondrait par 880.
– SOUS 5 9, le serveur répondrait par -4.
– DIV 9 8, le serveur répondrait par 1.125. (division réelle).
– PARIS 6 7, le serveur répondrait par hhhhh erreur.
0 4 8 12 16 20 24 28 32 36 40
– Votre client doit maintenant envoyer un tableau d'entiers, en disant {1, 6, 8, 9, 13,
dix}.
– Le serveur doit répondre par la sous-table ne contenant que des
entiers ({6, 8, 10}).
Annexe 153
Le serveur se propagera via la même phrase mais sur deux lignes (chacune
mot sur une ligne).
PARIS
MADRID
– En utilisant Java RMI, écrivez une méthode distante pour calculer l'inverse d'un
mot. Pour cela, nous devons écrire l'interface partagée entre le client et
le serveur, l'implémentation client et l'implémentation serveur.
– Ajouter une deuxième méthode distante pour retourner la sous-chaîne contenant le
quatre caractères au début de ce mot.
– Ajouter une troisième méthode à distance pour savoir si le mot est un
palindrôme.
La suite de Fibonacci est une suite d'entiers dans laquelle chaque nombre est
la somme des deux nombres qui la précèdent. Il commence généralement par 1 et son premier
les nombres sont : 1, 1, 2, 3, 5, 8, 13, 21, etc.
À l'aide de Java RMI, écrivez une méthode distante qui a un paramètre de nombre n
et qui renvoie les n premiers entiers de la suite. Envoi 5, par exemple,
au serveur, ce dernier répondra par la chaîne de caractères suivante : 1,
1, 2, 3, 5.
En utilisant Java RMI, écrivez une méthode distante qui reçoit une chaîne de
caractères avec un nombre impair de caractères (vous devrez effectuer un test
sur la longueur du mot) et l'affiche sous la forme d'un nœud papillon. Pour
Exemple:
ho
il lo
154 Programmation simultanée, temps réel et distribuée en Java
bonjour
il lo
ho
Nous souhaitons mettre en place un service de registre qui enregistre les noms et numéros de téléphone
numéros et permet à l'utilisateur d'y accéder à l'aide d'un nom. Le registre doit
être accessible à distance via RMI. Nous considérons que le nom et le numéro sont
chaînes de caractères.
– Définir une interface RMI répondant à ces spécifications.
– Développer un objet serveur implantant cette interface.
– Développer un client qui interroge l'objet serveur précité.
Bibliographie
Indice
A, C, D JNI, 68 ans
JRMP, 96
acquérir, 57, 58, 61, 64
JSSE, 117
API, 91, 117
KVM, 65
asynchrone, 66, 120
Linux, 67 ans
CNI, 68 ans
bas niveau, 72
CORBA, 91, 96
DCOM, 91
M, N, O
détruire, 15
DGC, 125 mobile, 65
Nortel, 66 ans
G, H, je notifier, 47–49, 55–58, 65, 113, 114
nombre, 17, 66, 71–75, 81–83, 89,
GC, 65, 67, 69 96, 106, 107, 125
CCG, 69 OSI, 72
GCJ, 69 ans
GNU, 69 ans
Q, R, S
table de hachage, 106, 108
IIOP, 96 QNX, 66 ans
PI, 72–75, 95, 96 temps réel, 61–63, 65–67, 69
libération, 57, 58
J, K, L À distance, 90, 95–98, 101, 102, 104,
105, 107, 110, 113, 115, 120, 122,
J2EE, 65 ans
123, 125
J2ME, 65 ans
CRIM, 95
J2SE, 65, 72, 95, 117
RPC, 90, 91
JDK, 91, 125
RRL, 95
RTSJ, 65, 66 T, V, O
courir, 3–11, 13, 16, 18, 19, 22–24, 66,
TCP, 72, 73, 77, 89, 95, 96
122
Minuterie, 21–24
sécurisé, 117
variable, 1, 30, 33, 34, 36, 43, 45, 55
sémaphore, 54, 55, 58, 59
attendre, 10, 13, 38, 45, 47–49, 55, 56,
douille, 72–77, 79–83, 87–89, 116,
58, 63, 64, 73, 81
117
WinCE, 62 ans
SSL, 117